From 6a375821581b2f728a8f58af6f7f8974a93db60b Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Fri, 2 Nov 2018 20:08:11 +0100 Subject: [PATCH] msc_role: Manage roles on ACI MultiSite (#47757) This includes the new msc_role module and integration tests. --- lib/ansible/modules/network/aci/msc_role.py | 210 +++++++++++++ test/integration/targets/msc_role/aliases | 2 + .../targets/msc_role/tasks/main.yml | 289 ++++++++++++++++++ 3 files changed, 501 insertions(+) create mode 100644 lib/ansible/modules/network/aci/msc_role.py create mode 100644 test/integration/targets/msc_role/aliases create mode 100644 test/integration/targets/msc_role/tasks/main.yml diff --git a/lib/ansible/modules/network/aci/msc_role.py b/lib/ansible/modules/network/aci/msc_role.py new file mode 100644 index 00000000000..0878e3d380b --- /dev/null +++ b/lib/ansible/modules/network/aci/msc_role.py @@ -0,0 +1,210 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = r''' +--- +module: msc_role +short_description: Manage roles +description: +- Manage roles on Cisco ACI Multi-Site. +author: +- Dag Wieers (@dagwieers) +version_added: '2.8' +options: + role_id: + description: + - The ID of the role. + required: yes + role: + description: + - The name of the role. + required: yes + aliases: [ name, role_name ] + display_name: + description: + - The name of the role to be displayed in the web UI. + description: + description: + - The description of the role. + permissions: + description: + - A list of permissions tied to this role. + type: list + choices: + - manage-roles + - manage-schemas + - manage-sites + - manage-tenants + - manage-users + - view-roles + - view-schemas + - view-sites + - view-tenants + - view-tenant-schemas + - view-users + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: msc +''' + +EXAMPLES = r''' +- name: Add a new role + msc_role: + host: msc_host + username: admin + password: SomeSecretPassword + name: north_europe + role_id: 101 + description: North European Datacenter + state: present + delegate_to: localhost + +- name: Remove a role + msc_role: + host: msc_host + username: admin + password: SomeSecretPassword + name: north_europe + state: absent + delegate_to: localhost + +- name: Query a role + msc_role: + host: msc_host + username: admin + password: SomeSecretPassword + name: north_europe + state: query + delegate_to: localhost + register: query_result + +- name: Query all roles + msc_role: + host: msc_host + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.aci.msc import MSCModule, msc_argument_spec, issubset + + +def main(): + argument_spec = msc_argument_spec() + argument_spec.update( + role=dict(type='str', required=False, aliases=['name', 'role_name']), + role_id=dict(type='str', required=False), + display_name=dict(type='str'), + description=dict(type='str'), + permissions=dict(type='list', choices=[ + 'manage-roles', + 'manage-schemas', + 'manage-sites', + 'manage-tenants', + 'manage-users', + 'view-roles', + 'view-schemas', + 'view-sites', + 'view-tenants', + 'view-tenant-schemas', + 'view-users', + ]), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['role']], + ['state', 'present', ['role']], + ], + ) + + role = module.params['role'] + role_id = module.params['role_id'] + description = module.params['description'] + permissions = module.params['permissions'] + state = module.params['state'] + + msc = MSCModule(module) + + path = 'roles' + + # Query for existing object(s) + if role_id is None and role is None: + msc.existing = msc.query_objs(path) + elif role_id is None: + msc.existing = msc.get_obj(path, name=role) + if msc.existing: + role_id = msc.existing['id'] + elif role is None: + msc.existing = msc.get_obj(path, id=role_id) + else: + msc.existing = msc.get_obj(path, id=role_id) + existing_by_name = msc.get_obj(path, name=role) + if existing_by_name and role_id != existing_by_name['id']: + msc.fail_json(msg="Provided role '{1}' with id '{2}' does not match existing id '{3}'.".format(role, role_id, existing_by_name['id'])) + + # If we found an existing object, continue with it + if role_id: + path = 'roles/{id}'.format(id=role_id) + + if state == 'query': + pass + + elif state == 'absent': + msc.previous = msc.existing + if msc.existing: + if module.check_mode: + msc.existing = {} + else: + msc.existing = msc.request(path, method='DELETE') + + elif state == 'present': + msc.previous = msc.existing + + msc.sanitize(dict( + id=role_id, + name=role, + displayName=role, + description=description, + permissions=permissions, + ), collate=True) + + if msc.existing: + if not issubset(msc.sent, msc.existing): + if module.check_mode: + msc.existing = msc.proposed + else: + msc.existing = msc.request(path, method='PUT', data=msc.sent) + else: + if module.check_mode: + msc.existing = msc.proposed + else: + msc.existing = msc.request(path, method='POST', data=msc.sent) + + msc.exit_json() + + +if __name__ == "__main__": + main() diff --git a/test/integration/targets/msc_role/aliases b/test/integration/targets/msc_role/aliases new file mode 100644 index 00000000000..cd28d3c3621 --- /dev/null +++ b/test/integration/targets/msc_role/aliases @@ -0,0 +1,2 @@ +# No ACI MultiSite infrastructure, so not enabled +unsupported diff --git a/test/integration/targets/msc_role/tasks/main.yml b/test/integration/targets/msc_role/tasks/main.yml new file mode 100644 index 00000000000..1ebb4e234c1 --- /dev/null +++ b/test/integration/targets/msc_role/tasks/main.yml @@ -0,0 +1,289 @@ +# Test code for the MSC modules +# Copyright: (c) 2018, Dag Wieers (@dagwieers) + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI MultiSite host, username and password + fail: + msg: 'Please define the following variables: msc_hostname, msc_username and msc_password.' + when: msc_hostname is not defined or msc_username is not defined or msc_password is not defined + + +# CLEAN ENVIRONMENT +- name: Remove role ansible_test2 + msc_role: &role_absent + host: '{{ msc_hostname }}' + username: '{{ msc_username }}' + password: '{{ msc_password }}' + validate_certs: '{{ msc_validate_certs | default(false) }}' + use_ssl: '{{ msc_use_ssl | default(true) }}' + use_proxy: '{{ msc_use_proxy | default(true) }}' + output_level: '{{ msc_output_level | default("info") }}' + role: ansible_test2 + state: absent + +- name: Remove role ansible_test + msc_role: + <<: *role_absent + role: ansible_test + register: cm_remove_role + + +# ADD ROLE +- name: Add role (check_mode) + msc_role: &role_present + host: '{{ msc_hostname }}' + username: '{{ msc_username }}' + password: '{{ msc_password }}' + validate_certs: '{{ msc_validate_certs | default(false) }}' + use_ssl: '{{ msc_use_ssl | default(true) }}' + use_proxy: '{{ msc_use_proxy | default(true) }}' + output_level: '{{ msc_output_level | default("info") }}' + role: ansible_test + description: Ansible test role + permissions: view-sites + state: present + check_mode: yes + register: cm_add_role + +- name: Verify cm_add_role + assert: + that: + - cm_add_role is changed + - cm_add_role.previous == {} + - cm_add_role.current.id is not defined + - cm_add_role.current.displayName == 'ansible_test' + - cm_add_role.current.description == 'Ansible test role' + +- name: Add role (normal mode) + msc_role: *role_present + register: nm_add_role + +- name: nm_Verify add_role + assert: + that: + - nm_add_role is changed + - nm_add_role.previous == {} + - nm_add_role.current.id is defined + - nm_add_role.current.displayName == 'ansible_test' + - nm_add_role.current.description == 'Ansible test role' + +- name: Add role again (check_mode) + msc_role: *role_present + check_mode: yes + register: cm_add_role_again + +- name: Verify cm_add_role_again + assert: + that: + - cm_add_role_again is not changed + - cm_add_role_again.previous.displayName == 'ansible_test' + - cm_add_role_again.previous.description == 'Ansible test role' + - cm_add_role_again.current.id == nm_add_role.current.id + - cm_add_role_again.current.displayName == 'ansible_test' + - cm_add_role_again.current.description == 'Ansible test role' + +- name: Add role again (normal mode) + msc_role: *role_present + register: nm_add_role_again + +- name: Verify nm_add_role_again + assert: + that: + - nm_add_role_again is not changed + - nm_add_role_again.previous.displayName == 'ansible_test' + - nm_add_role_again.previous.description == 'Ansible test role' + - nm_add_role_again.current.id == nm_add_role.current.id + - nm_add_role_again.current.displayName == 'ansible_test' + - nm_add_role_again.current.description == 'Ansible test role' + + +# CHANGE ROLE +- name: Change role (check_mode) + msc_role: + <<: *role_present + role_id: '{{ nm_add_role.current.id }}' + role: ansible_test2 + description: Ansible test role 2 + check_mode: yes + register: cm_change_role + +- name: Verify cm_change_role + assert: + that: + - cm_change_role is changed + - cm_change_role.current.id == nm_add_role.current.id + - cm_change_role.current.displayName == 'ansible_test2' + - cm_change_role.current.description == 'Ansible test role 2' + +- name: Change role (normal mode) + msc_role: + <<: *role_present + role_id: '{{ nm_add_role.current.id }}' + role: ansible_test2 + description: Ansible test role 2 + output_level: debug + register: nm_change_role + +- name: Verify nm_change_role + assert: + that: + - nm_change_role is changed + - nm_change_role.current.id == nm_add_role.current.id + - nm_change_role.current.displayName == 'ansible_test2' + - nm_change_role.current.description == 'Ansible test role 2' + +- name: Change role again (check_mode) + msc_role: + <<: *role_present + role_id: '{{ nm_add_role.current.id }}' + role: ansible_test2 + description: Ansible test role 2 + check_mode: yes + register: cm_change_role_again + +- name: Verify cm_change_role_again + assert: + that: + - cm_change_role_again is not changed + - cm_change_role_again.current.id == nm_add_role.current.id + - cm_change_role_again.current.displayName == 'ansible_test2' + - cm_change_role_again.current.description == 'Ansible test role 2' + +- name: Change role again (normal mode) + msc_role: + <<: *role_present + role_id: '{{ nm_add_role.current.id }}' + role: ansible_test2 + description: Ansible test role 2 + register: nm_change_role_again + +- name: Verify nm_change_role_again + assert: + that: + - nm_change_role_again is not changed + - nm_change_role_again.current.id == nm_add_role.current.id + - nm_change_role_again.current.displayName == 'ansible_test2' + - nm_change_role_again.current.description == 'Ansible test role 2' + + +# QUERY ALL ROLES +- name: Query all roles (check_mode) + msc_role: &role_query + host: '{{ msc_hostname }}' + username: '{{ msc_username }}' + password: '{{ msc_password }}' + validate_certs: '{{ msc_validate_certs | default(false) }}' + use_ssl: '{{ msc_use_ssl | default(true) }}' + use_proxy: '{{ msc_use_proxy | default(true) }}' + output_level: '{{ msc_output_level | default("info") }}' + state: query + check_mode: yes + register: cm_query_all_roles + +- name: Query all roles (normal mode) + msc_role: *role_query + register: nm_query_all_roles + +- name: Verify query_all_roles + assert: + that: + - cm_query_all_roles is not changed + - nm_query_all_roles is not changed + # NOTE: Order of roles is not stable between calls + #- cm_query_all_roles == nm_query_all_roles + + +# QUERY A ROLE +- name: Query our role + msc_role: + <<: *role_query + role: ansible_test2 + check_mode: yes + register: cm_query_role + +- name: Query our role + msc_role: + <<: *role_query + role: ansible_test2 + register: nm_query_role + +- name: Verify query_role + assert: + that: + - cm_query_role is not changed + - cm_query_role.current.id == nm_add_role.current.id + - cm_query_role.current.displayName == 'ansible_test2' + - cm_query_role.current.description == 'Ansible test role 2' + - nm_query_role is not changed + - nm_query_role.current.id == nm_add_role.current.id + - nm_query_role.current.displayName == 'ansible_test2' + - nm_query_role.current.description == 'Ansible test role 2' + - cm_query_role == nm_query_role + + +# REMOVE ROLE +- name: Remove role (check_mode) + msc_role: *role_absent + check_mode: yes + register: cm_remove_role + +- name: Verify cm_remove_role + assert: + that: + - cm_remove_role is changed + - cm_remove_role.current == {} + +- name: Remove role (normal mode) + msc_role: *role_absent + register: nm_remove_role + +- name: Verify nm_remove_role + assert: + that: + - nm_remove_role is changed + - nm_remove_role.current == {} + +- name: Remove role again (check_mode) + msc_role: *role_absent + check_mode: yes + register: cm_remove_role_again + +- name: Verify cm_remove_role_again + assert: + that: + - cm_remove_role_again is not changed + - cm_remove_role_again.current == {} + +- name: Remove role again (normal mode) + msc_role: *role_absent + register: nm_remove_role_again + +- name: Verify nm_remove_role_again + assert: + that: + - nm_remove_role_again is not changed + - nm_remove_role_again.current == {} + + +# QUERY NON-EXISTING ROLE +- name: Query non-existing role (check_mode) + msc_role: + <<: *role_query + role: ansible_test + check_mode: yes + register: cm_query_non_role + +- name: Query non-existing role (normal mode) + msc_role: + <<: *role_query + role: ansible_test + register: nm_query_non_role + +# TODO: Implement more tests +- name: Verify query_non_role + assert: + that: + - cm_query_non_role is not changed + - nm_query_non_role is not changed + - cm_query_non_role == nm_query_non_role