Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote users #530

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ansible-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ jobs:
- name: Install the collection tarball
run: ansible-galaxy collection install .cache/collection-tarballs/*.tar.gz

- name: Install the ND collection (NDO dependency)
run: ansible-galaxy collection install cisco.nd

- name: Run sanity tests
run: ansible-test sanity --docker -v --color --truncate 0 --coverage
working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/mso
Expand Down
69 changes: 58 additions & 11 deletions plugins/module_utils/mso.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ansible.module_utils._text import to_native, to_text
from ansible.module_utils.connection import Connection
from ansible_collections.cisco.mso.plugins.module_utils.constants import NDO_API_VERSION_PATH_FORMAT
from ansible_collections.cisco.nd.plugins.module_utils.nd import NDModule

try:
from requests_toolbelt.multipart.encoder import MultipartEncoder
Expand Down Expand Up @@ -50,7 +51,7 @@ def issubset(subset, superset):
return True

# Both objects have a different type
if type(subset) != type(superset):
if type(subset) is not type(superset):
return False

for key, value in subset.items():
Expand Down Expand Up @@ -239,6 +240,13 @@ def mso_site_anp_epg_bulk_staticport_spec():
)


def ndo_remote_user_spec():
return dict(
name=dict(type="str", required=True),
login_domain=dict(type="str", required=True),
)


# Copied from ansible's module uri.py (url): https://github.com/ansible/ansible/blob/cdf62edc65f564fff6b7e575e084026fa7faa409/lib/ansible/modules/uri.py
def write_file(module, url, dest, content, resp, tmpsrc=None):
# create a tempfile with some test content
Expand Down Expand Up @@ -916,26 +924,65 @@ def lookup_users(self, users, ignore_not_found_error=False):
users.append("admin")

ids = []
if self.platform == "nd":
nd = NDModule(self.module)
remote_users = nd.request("/nexus/infra/api/aaa/v4/remoteusers", method="GET")
local_users = nd.request("/nexus/infra/api/aaa/v4/localusers", method="GET")

for user in users:
user_dict = dict()
if self.platform == "nd":
u = self.get_obj("users", loginID=user, api_version="v2")
user_dict = self.get_user_from_list_of_users(user, local_users)
if user_dict is None:
user_dict = self.get_user_from_list_of_users(user, remote_users)
else:
u = self.get_obj("users", username=user)
if not u and not ignore_not_found_error:
user_dict = self.get_obj("users", username=user)
if not user_dict and not ignore_not_found_error:
self.fail_json(msg="User '{0}' is not a valid user name.".format(user))
elif (not u or "id" not in u) and ignore_not_found_error:
elif (not user_dict or "id" not in user_dict) and ignore_not_found_error:
self.module.warn("User '{0}' is not a valid user name.".format(user))
return ids
if "id" not in u:
if "userID" not in u:
self.fail_json(msg="User lookup failed for user '{0}': {1}".format(user, u))
id = dict(userId=u.get("userID"))
if "id" not in user_dict:
if "userID" not in user_dict:
self.fail_json(msg="User lookup failed for user '{0}': {1}".format(user, user_dict))
id = dict(userId=user_dict.get("userID"))
else:
id = dict(userId=u.get("id"))
id = dict(userId=user_dict.get("id"))
if id in ids:
self.fail_json(msg="User '{0}' is duplicate.".format(user))
ids.append(id)
return ids

def get_user_from_list_of_users(self, user_name, list_of_users, login_domain=""):
"""Get user from list of users"""
for user in list_of_users.get("items"):
if user.get("spec").get("loginID") == user_name and (login_domain == "" or user.get("spec").get("loginDomain") == login_domain):
return user.get("spec")
return None

def lookup_remote_users(self, remote_users, ignore_not_found_error=False):
ids = []
if self.platform == "nd":
nd = NDModule(self.module)
remote_users_data = nd.request("/nexus/infra/api/aaa/v4/remoteusers", method="GET")
for remote_user in remote_users:
user_dict = dict()
if self.platform == "nd":
user_dict = self.get_user_from_list_of_users(remote_user.get("name"), remote_users_data, remote_user.get("login_domain"))
if not user_dict and not ignore_not_found_error:
self.fail_json(msg="User '{0}' is not a valid user name.".format(remote_user.get("name")))
elif (not user_dict or "id" not in user_dict) and ignore_not_found_error:
self.module.warn("User '{0}' is not a valid user name.".format(remote_user.get("name")))
return ids
if "id" not in user_dict:
if "userID" not in user_dict:
self.fail_json(msg="User lookup failed for user '{0}': {1}".format(remote_user.get("name"), user_dict))
id = dict(userId=user_dict.get("userID"))
else:
id = dict(userId=user_dict.get("id"))
if id in ids:
self.fail_json(msg="User '{0}' is duplicate.".format(remote_user.get("name")))
ids.append(id)
return ids

def create_label(self, label, label_type):
Expand Down Expand Up @@ -1112,7 +1159,7 @@ def make_dhcp_label(self, data):
"""Create a DHCP policy from input"""
if data is None:
return None
if type(data) == list:
if isinstance(data, list):
dhcps = []
for dhcp in data:
if "dhcp_option_policy" in dhcp:
Expand Down
31 changes: 26 additions & 5 deletions plugins/modules/mso_tenant.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Copyright: (c) 2018, Dag Wieers (@dagwieers) <[email protected]>
# Copyright: (c) 2020, Cindy Zhao (@cizhao) <[email protected]>
# Copyright: (c) 2023, Anvitha Jain (@anvjain) <[email protected]>
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
Expand Down Expand Up @@ -40,6 +41,22 @@
- Admin user is always added to the associated user list irrespective of this parameter being used.
type: list
elements: str
remote_users:
description:
- A list of associated remote users for this tenant.
type: list
elements: dict
suboptions:
name:
description:
- The name of the associated remote user for this tenant.
required: true
type: str
login_domain:
description:
- Domain name of the associated remote user for this tenant.
required: true
type: str
sites:
description:
- A list of associated sites for this tenant.
Expand Down Expand Up @@ -119,7 +136,7 @@
"""

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec
from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec, ndo_remote_user_spec
from ansible_collections.cisco.mso.plugins.module_utils.constants import YES_OR_NO_TO_BOOL_STRING_MAP


Expand All @@ -130,6 +147,7 @@ def main():
display_name=dict(type="str"),
tenant=dict(type="str", aliases=["name"]),
users=dict(type="list", elements="str"),
remote_users=dict(type="list", elements="dict", options=ndo_remote_user_spec()),
sites=dict(type="list", elements="str"),
orchestrator_only=dict(type="str", default="yes", choices=["yes", "no"]),
state=dict(type="str", default="present", choices=["absent", "present", "query"]),
Expand All @@ -149,13 +167,10 @@ def main():
tenant = module.params.get("tenant")
orchestrator_only = module.params.get("orchestrator_only")
state = module.params.get("state")
remote_users = module.params.get("remote_users")

mso = MSOModule(module)

# Convert sites and users
sites = mso.lookup_sites(module.params.get("sites"))
users = mso.lookup_users(module.params.get("users"))

tenant_id = None
path = "tenants"

Expand All @@ -182,6 +197,12 @@ def main():
mso.existing = mso.request(path, method="DELETE")

elif state == "present":
# Convert sites and users
sites = mso.lookup_sites(module.params.get("sites"))
users = mso.lookup_users(module.params.get("users"))
if remote_users is not None:
users += mso.lookup_remote_users(remote_users)

mso.previous = mso.existing

payload = dict(
Expand Down
42 changes: 40 additions & 2 deletions tests/integration/targets/mso_tenant/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,41 @@
- nm_add_tenant2_again.current.userAssociations | length == 1
when: mso_username == 'admin'

# ADD TENANT WITH REMOTE USERS
- name: Add tenant 3 (check_mode)
mso_tenant:
<<: *tenant_present
tenant: ansible_test3
display_name: null
remote_users:
- name: ansible_github_ci
login_domain: test
state: present
check_mode: true
register: cm_add_rmt_usr_tenant3

- name: Verify cm_add_rmt_usr_tenant3
assert:
that:
- cm_add_rmt_usr_tenant3 is changed

- name: Add tenant 3 (normal_mode)
mso_tenant:
<<: *tenant_present
tenant: ansible_test3
display_name: null
remote_users:
- name: ansible_github_ci
login_domain: test
state: present
register: nm_add_rmt_usr_tenant3

- name: Verify cm_add_rmt_usr_tenant3
assert:
that:
- nm_add_rmt_usr_tenant3 is changed
- nm_add_rmt_usr_tenant3.current.name == 'ansible_test3'

- name: Add tenant 3 with duplicate admin user (normal mode)
mso_tenant:
<<: *tenant_present
Expand All @@ -226,7 +261,7 @@
- name: Verify nm_add_tenant3_with_duplicate_admin
assert:
that:
- nm_add_tenant3_with_duplicate_admin is not changed
- nm_add_tenant3_with_duplicate_admin is changed
- nm_add_tenant3_with_duplicate_admin.msg == "User 'admin' is duplicate."

- name: Add tenant 3 with invalid user (normal mode)
Expand All @@ -252,6 +287,9 @@
tenant: ansible_test3
users:
- '{{ mso_username }}'
remote_users:
- name: ansible_github_ci
login_domain: test
display_name: null
state: present
register: nm_add_tenant3
Expand All @@ -264,7 +302,7 @@
- name: Verify nm_add_tenant3 (when mso_username != admin)
assert:
that:
- nm_add_tenant3.current.userAssociations | length == 2
- nm_add_tenant3.current.userAssociations | length == 3
when: mso_username != 'admin'

- name: Verify nm_add_tenant3 (when mso_username == admin)
Expand Down
Loading