From dfd1b29777253748f7a5fa98e3ee16104b3a5f22 Mon Sep 17 00:00:00 2001 From: jasonjuenger Date: Tue, 27 Aug 2019 05:08:24 -0700 Subject: [PATCH] Add aci_vmm_credential.py (#60910) * add module to create aci VMM credential objects add initial remove and add integration tests for VMM credential objects * update 'credential' var name to 'name' * move vmware tests to domain type specific file * move vmware tests to domain type specific file add include task in main file to reference domain type specific tests * update task names add test to remove credential prior to first credential add add tests for querying individual credentials add tests for query all credentials add additional tests for removing credentials * update version added to 2.9 remove invalid module references from 'seealso' section * fix list reference in query all assertions * add reference to VM_PROVIDER_MAPPING keys for vm_provider arg --- .../modules/network/aci/aci_vmm_credential.py | 308 ++++++++++++++++++ .../targets/aci_vmm_credential/aliases | 2 + .../targets/aci_vmm_credential/tasks/main.yml | 12 + .../aci_vmm_credential/tasks/vmware.yml | 239 ++++++++++++++ 4 files changed, 561 insertions(+) create mode 100644 lib/ansible/modules/network/aci/aci_vmm_credential.py create mode 100644 test/integration/targets/aci_vmm_credential/aliases create mode 100644 test/integration/targets/aci_vmm_credential/tasks/main.yml create mode 100644 test/integration/targets/aci_vmm_credential/tasks/vmware.yml diff --git a/lib/ansible/modules/network/aci/aci_vmm_credential.py b/lib/ansible/modules/network/aci/aci_vmm_credential.py new file mode 100644 index 00000000000..74cd996a7c9 --- /dev/null +++ b/lib/ansible/modules/network/aci/aci_vmm_credential.py @@ -0,0 +1,308 @@ +#!/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: aci_vmm_credential +short_description: Manage virtual domain credential profiles (vmm:UsrAccP) +description: +- Manage virtual domain credential profiles on Cisco ACI fabrics. +version_added: '2.9' +options: + name: + description: + - Name of the credential profile. + type: str + aliases: [ credential_name, credential_profile ] + credential_password: + description: + - VMM controller password. + type: str + aliases: [] + credential_username: + description: + - VMM controller username. + type: str + aliases: [] + description: + description: + - Description for the tenant. + type: str + aliases: [ descr ] + domain: + description: + - Name of the virtual domain profile. + type: str + aliases: [ domain_name, domain_profile, name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present + vm_provider: + description: + - The VM platform for VMM Domains. + - Support for Kubernetes was added in ACI v3.0. + - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1. + type: str + choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ] +extends_documentation_fragment: aci +seealso: +- module: aci_domain +- name: APIC Management Information Model reference + description: More information about the internal APIC classes B(vmm:DomP) + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Jason Juenger (@jasonjuenger) +''' + +EXAMPLES = r''' +- name: Add credential to VMware VMM domain + aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + description: secure credential + name: vCenterCredential + credential_username: vCenterUsername + credential_password: vCenterPassword + vm_provider: vmware + state: present + +- name: Remove credential from VMware VMM domain + aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + name: myCredential + vm_provider: vmware + state: absent + +- name: Query a specific VMware VMM credential + aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + name: vCenterCredential + vm_provider: vmware + state: query + delegate_to: localhost + register: query_result + +- name: Query all VMware VMM credentials + aci_vmm_credential: + host: apic + username: admin + password: SomeSecretPassword + domain: vmware_dom + vm_provider: vmware + state: query + delegate_to: localhost + register: query_result +''' + +RETURN = r''' +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec + +VM_PROVIDER_MAPPING = dict( + cloudfoundry='CloudFoundry', + kubernetes='Kubernetes', + microsoft='Microsoft', + openshift='OpenShift', + openstack='OpenStack', + redhat='Redhat', + vmware='VMware', +) + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update( + name=dict(type='str', aliases=['credential_name', 'credential_profile']), + credential_password=dict(type='str'), + credential_username=dict(type='str'), + description=dict(type='str', aliases=['descr']), + domain=dict(type='str', aliases=['domain_name', 'domain_profile']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + vm_provider=dict(type='str', choices=VM_PROVIDER_MAPPING.keys()) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['domain']], + ['state', 'present', ['domain']], + ], + ) + + name = module.params['name'] + credential_password = module.params['credential_password'] + credential_username = module.params['credential_username'] + description = module.params['description'] + domain = module.params['domain'] + state = module.params['state'] + vm_provider = module.params['vm_provider'] + + credential_class = 'vmmUsrAccP' + usracc_mo = 'uni/vmmp-{0}/dom-{1}/usracc-{2}'.format(VM_PROVIDER_MAPPING[vm_provider], domain, name) + usracc_rn = 'vmmp-{0}/dom-{1}/usracc-{2}'.format(VM_PROVIDER_MAPPING[vm_provider], domain, name) + + # Ensure that querying all objects works when only domain is provided + if name is None: + usracc_mo = None + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class=credential_class, + aci_rn=usracc_rn, + module_object=usracc_mo, + target_filter={'name': domain, 'usracc': name}, + ), + ) + + aci.get_existing() + + if state == 'present': + aci.payload( + aci_class=credential_class, + class_config=dict( + descr=description, + name=name, + pwd=credential_password, + usr=credential_username + ), + ) + + aci.get_diff(aci_class=credential_class) + + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/test/integration/targets/aci_vmm_credential/aliases b/test/integration/targets/aci_vmm_credential/aliases new file mode 100644 index 00000000000..f16b2509294 --- /dev/null +++ b/test/integration/targets/aci_vmm_credential/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +unsupported diff --git a/test/integration/targets/aci_vmm_credential/tasks/main.yml b/test/integration/targets/aci_vmm_credential/tasks/main.yml new file mode 100644 index 00000000000..3095ed50279 --- /dev/null +++ b/test/integration/targets/aci_vmm_credential/tasks/main.yml @@ -0,0 +1,12 @@ +# Test code for the ACI 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 APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- include_tasks: vmware.yml + when: vmware is not defined or vmware \ No newline at end of file diff --git a/test/integration/targets/aci_vmm_credential/tasks/vmware.yml b/test/integration/targets/aci_vmm_credential/tasks/vmware.yml new file mode 100644 index 00000000000..be386718d93 --- /dev/null +++ b/test/integration/targets/aci_vmm_credential/tasks/vmware.yml @@ -0,0 +1,239 @@ +# Test code for the ACI modules +# Copyright: (c) 2017, Dag Wieers (@dagwieers) + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Remove VMM domain +- name: Remove VMM domain (normal mode) + aci_domain: &domain_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: absent + register: nm_remove_domain + +# ADD VMM domain for testing +- name: Add VMM domain (normal mode) + aci_domain: &domain_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + domain: vmm_dom + domain_type: vmm + vm_provider: vmware + state: present + register: nm_add_domain + +- name: Verify add_domain + assert: + that: + - nm_add_domain is changed + +# REMOVE credential +- name: Remove credential (check mode) + aci_vmm_credential: &credential_absent + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + description: my_new_cred + domain: vmm_dom + credential_username: myUsername + credential_password: mySecretPassword + vm_provider: vmware + state: absent + check_mode: yes + register: cm_remove_credential + +- name: Remove vmware VMM credential (normal mode) + aci_vmm_credential: *credential_absent + register: nm_remove_credential + +- name: Verify remove_credential + assert: + that: + - cm_remove_credential is not changed + - nm_remove_credential is not changed + +# ADD credential +- name: Add vmware VMM credential (check mode) + aci_vmm_credential: &credential_present + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + description: my_new_cred + domain: vmm_dom + credential_username: myUsername + credential_password: mySecretPassword + vm_provider: vmware + state: present + check_mode: yes + register: cm_add_credential + +# NOTE: Setting password is not idempotent +- name: Add vmware VMM credential (normal mode) + aci_vmm_credential: *credential_present + register: nm_add_credential + +# NOTE: Setting password is not idempotent +- name: Add vmware VMM credential again (check mode) + aci_vmm_credential: *credential_present + check_mode: yes + register: cm_add_credential_again + +- name: Verify add_credential + assert: + that: + - cm_add_credential is changed + - nm_add_credential is changed + - 'cm_add_credential.sent == nm_add_credential.sent == {"vmmUsrAccP": {"attributes": {"descr": "my_new_cred", "name": "vmm_cred", "pwd": "mySecretPassword", "usr": "myUsername"}}}' + - 'cm_add_credential.proposed == nm_add_credential.proposed == {"vmmUsrAccP": {"attributes": {"descr": "my_new_cred", "name": "vmm_cred", "pwd": "mySecretPassword", "usr": "myUsername"}}}' + - cm_add_credential.current == cm_add_credential.previous == nm_add_credential.previous == [] + - nm_add_credential.current.0.vmmUsrAccP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom/usracc-vmm_cred' + - nm_add_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + +# MODIFY credential +- name: Modify vmware VMM credential (check mode) + aci_vmm_credential: &credential_mod + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + description: my_updated_descr + domain: vmm_dom + credential_username: myNewUsername + credential_password: myNewSecretPassword + vm_provider: vmware + state: present + check_mode: yes + register: cm_mod_credential + +- name: Modify vmware VMM credential (normal mode) + aci_vmm_credential: *credential_mod + register: nm_mod_credential + +- name: Verify mod_credential + assert: + that: + - cm_mod_credential is changed + - nm_mod_credential is changed + - 'cm_mod_credential.sent == nm_mod_credential.sent == {"vmmUsrAccP": {"attributes": {"descr": "my_updated_descr", "pwd": "myNewSecretPassword", "usr": "myNewUsername"}}}' + - 'cm_mod_credential.proposed == nm_mod_credential.proposed == {"vmmUsrAccP": {"attributes": {"descr": "my_updated_descr", "name": "vmm_cred", "pwd": "myNewSecretPassword", "usr": "myNewUsername"}}}' + - nm_mod_credential.current.0.vmmUsrAccP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom/usracc-vmm_cred' + - nm_mod_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + +- name: Query existing vmware VMM credential (check mode) + aci_vmm_credential: &query_existing_cred + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_cred + domain: vmm_dom + vm_provider: vmware + state: query + check_mode: yes + register: cm_query_credential + +- name: Query existing vmware VMM credential (normal mode) + aci_vmm_credential: *query_existing_cred + register: nm_query_credential + +- name: Query non-existent vmware VMM credential + aci_vmm_credential: + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + name: vmm_fake_cred + domain: vmm_dom + vm_provider: vmware + state: query + register: nm_query_fake_credential + +- name: Query all vmware VMM credentials (check mode) + aci_vmm_credential: &query_all_creds + host: '{{ aci_hostname }}' + username: '{{ aci_username }}' + password: '{{ aci_password }}' + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + vm_provider: vmware + state: query + check_mode: yes + register: cm_query_all_credential + +- name: Query all vmware VMM credentials (normal mode) + aci_vmm_credential: *query_all_creds + register: nm_query_all_credential + +- name: Verify query_credential + assert: + that: + - cm_query_credential is not changed + - nm_query_credential is not changed + - nm_query_fake_credential is not changed + - cm_query_all_credential is not changed + - nm_query_all_credential is not changed + - cm_query_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + - nm_query_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + - nm_query_fake_credential.current == [] + - cm_query_all_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + - nm_query_all_credential.current.0.vmmUsrAccP.attributes.name == 'vmm_cred' + +- name: Remove credential (check_mode) + aci_vmm_credential: *credential_absent + check_mode: yes + register: cm_remove_credential_again + +- name: Remove credential (normal_mode) + aci_vmm_credential: *credential_absent + register: nm_remove_credential_again + +- name: Remove credential (normal_mode) + aci_vmm_credential: *credential_absent + register: nm_remove_credential_final + +- name: Verify remove_credential + assert: + that: + - cm_remove_credential_again is changed + - nm_remove_credential_again is changed + - nm_remove_credential_final is not changed + +# Remove VMM domain after testing +- name: Remove VMM domain (normal_mode) + aci_domain: *domain_absent + register: nm_remove_domain_again \ No newline at end of file