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