diff --git a/lib/ansible/modules/network/aci/aci_access_port_to_interface_policy_leaf_profile.py b/lib/ansible/modules/network/aci/aci_access_port_to_interface_policy_leaf_profile.py new file mode 100644 index 0000000000..5a2ce68008 --- /dev/null +++ b/lib/ansible/modules/network/aci/aci_access_port_to_interface_policy_leaf_profile.py @@ -0,0 +1,191 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero +# 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_access_port_to_interface_policy_leaf_profile +short_description: Manage Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics (infra:HPortS, infra:RsAccBaseGrp, infra:PortBlk) +description: +- Manage Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics. +- More information from the internal APIC class + I(infra:HPortS, infra:RsAccBaseGrp, infra:PortBlk) at U(https://developer.cisco.com/media/mim-ref). +author: +- Bruno Calogero (@brunocalogero) +version_added: '2.5' +options: + leaf_interface_profile: + description: + - The name of the Fabric access policy leaf interface profile. + required: yes + aliases: [ leaf_interface_profile_name ] + access_port_selector: + description: + - The name of the Fabric access policy leaf interface profile access port selector. + required: yes + aliases: [ name, access_port_selector_name ] + leaf_port_blk: + description: + - The name of the Fabric access policy leaf interface profile access port block. + required: yes + aliases: [ leaf_port_blk_name ] + fromPort: + description: + - The beggining (from range) of the port range block for the leaf access port block. + required: yes + aliases: [ from_port_range ] + toPort: + description: + - The end (to range) of the port range block for the leaf access port block. + required: yes + aliases: [ to_port_range ] + policy_group: + description: + - The name of the fabric access policy group to be associated with the leaf interface profile interface selector. + required: no + aliases: [ policy_group_name ] + 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 +''' + +EXAMPLES = r''' +- name: Associate an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group + aci_access_port_to_interface_policy_leaf_profile: + hostname: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + leaf_port_blk: leafportblkname + fromPort: 13 + toPort: 16 + policy_group: policygroupname + state: present + +- name: Associate an interface access port selector to an Interface Policy Leaf Profile (w/o policy group) (check if this works) + aci_access_port_to_interface_policy_leaf_profile: + hostname: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + leaf_port_blk: leafportblkname + fromPort: 13 + toPort: 16 + state: present + +- name: Remove an interface access port selector associated with an Interface Policy Leaf Profile + aci_access_port_to_interface_policy_leaf_profile: + hostname: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + state: absent + +- name: Query Specific access_port_selector under given leaf_interface_profile + aci_access_port_to_interface_policy_leaf_profile: + hostname: apic + username: admin + password: SomeSecretPassword + leaf_interface_profile: leafintprfname + access_port_selector: accessportselectorname + state: query +''' + +RETURN = r''' +# +''' + +from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec + argument_spec.update( + leaf_interface_profile=dict(type='str', aliases=['leaf_interface_profile_name']), + access_port_selector=dict(type='str', aliases=['name', 'access_port_selector_name']), + leaf_port_blk=dict(type='str', aliases=['leaf_port_blk_name']), + fromPort=dict(type='str', aliases=['from_port_range']), + toPort=dict(type='str', aliases=['to_port_range']), + policy_group=dict(type='str', aliases=['policy_group_name']), + state=dict(type='str', default='present', choices=['absent', 'present', 'query']), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ['state', 'absent', ['leaf_interface_profile', 'access_port_selector']], + ['state', 'present', ['leaf_interface_profile', 'access_port_selector']], + ], + ) + + leaf_interface_profile = module.params['leaf_interface_profile'] + access_port_selector = module.params['access_port_selector'] + leaf_port_blk = module.params['leaf_port_blk'] + fromPort = module.params['fromPort'] + toPort = module.params['toPort'] + policy_group = module.params['policy_group'] + state = module.params['state'] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class='infraAccPortP', + aci_rn='infra/accportprof-{0}'.format(leaf_interface_profile), + filter_target='eq(infraAccPortP.name, "{0}")'.format(leaf_interface_profile), + module_object=leaf_interface_profile + ), + subclass_1=dict( + aci_class='infraHPortS', + # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module + aci_rn='hports-{0}-typ-range'.format(access_port_selector), + filter_target='eq(infraHPortS.name, "{0}")'.format(access_port_selector), + module_object=access_port_selector, + ), + child_classes=['infraPortBlk', 'infraRsAccBaseGrp'] + ) + aci.get_existing() + + if state == 'present': + # Filter out module parameters with null values + aci.payload( + aci_class='infraHPortS', + class_config=dict( + name=access_port_selector, + ), + child_configs=[ + dict(infraPortBlk=dict(attributes=dict(name=leaf_port_blk, fromPort=fromPort, toPort=toPort))), + dict(infraRsAccBaseGrp=dict(attributes=dict(tDn='uni/infra/funcprof/accportgrp-{0}'.format(policy_group)))), + ], + ) + + # Generate config diff which will be used as POST request body + aci.get_diff(aci_class='infraHPortS') + + # Submit changes if module not in check_mode and the proposed is different than existing + aci.post_config() + + elif state == 'absent': + aci.delete_config() + + module.exit_json(**aci.result) + + +if __name__ == "__main__": + main() diff --git a/test/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases b/test/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases new file mode 100644 index 0000000000..bf1433ae69 --- /dev/null +++ b/test/integration/targets/aci_access_port_to_interface_policy_leaf_profile/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled + diff --git a/test/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/aci_access_port_to_interface_policy_leaf_profile.yml b/test/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/aci_access_port_to_interface_policy_leaf_profile.yml new file mode 100644 index 0000000000..43a4428c89 --- /dev/null +++ b/test/integration/targets/aci_access_port_to_interface_policy_leaf_profile/tasks/aci_access_port_to_interface_policy_leaf_profile.yml @@ -0,0 +1,125 @@ +# Test code for the ACI modules +# Copyright 2017, Bruno Calogero + +# 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 + +- name: Ensuring Interface Policy Leaf profile exists for kick off + aci_interface_policy_leaf_profile: &aci_interface_policy_leaf_profile_present + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + leaf_interface_profile: leafintprftest + validate_certs: no + use_ssl: no + use_proxy: no + state: present + register: leaf_profile_present + +# TODO: Ensure that leaf Policy Group Exists (module missing) (infra:AccPortGrp) + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - check mode works + aci_access_port_to_interface_policy_leaf_profile: &aci_access_port_to_interface_policy_leaf_profile_present + <<: *aci_interface_policy_leaf_profile_present + access_port_selector: anstest_accessportselector + leaf_port_blk: anstest_leafportblkname + fromPort: 13 + toPort: 16 + check_mode: yes + register: accessport_to_intf_check_mode_present + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - creation works + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + register: accessport_to_intf_present + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - idempotency works + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + register: accessport_to_intf_idempotent + +- name: Bind an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group - update works + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_present + policy_group: anstest_policygroupname + register: accessport_to_intf_update + +# TODO: also test for errors +- name: present assertions + assert: + that: + - accessport_to_intf_check_mode_present.changed == true + - accessport_to_intf_present.changed == true + - accessport_to_intf_present.existing == [] + - 'accessport_to_intf_present.config == {"infraHPortS": {"attributes": {"name": "anstest_accessportselector"}, "children": [{"infraPortBlk": {"attributes": {"fromPort": "13", "name": "anstest_leafportblkname", "toPort": "16"}}}, {"infraRsAccBaseGrp": {"attributes": {"tDn": "uni/infra/funcprof/accportgrp-None"}}}]}}' + - accessport_to_intf_idempotent.changed == false + - accessport_to_intf_idempotent.config == {} + - accessport_to_intf_update.changed == true + - 'accessport_to_intf_update.config == {"infraHPortS": {"attributes": {},"children": [{"infraRsAccBaseGrp": {"attributes": {"tDn": "uni/infra/funcprof/accportgrp-anstest_policygroupname"}}}]}}' + +- name: Query Specific access_port_selector and leaf_interface_profile binding + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + access_port_selector: anstest_accessportselector # "{{ fake_var | default(omit) }}" ? + state: query + register: binding_query + +- name: present assertions + assert: + that: + - binding_query.changed == false + - binding_query.existing | length >= 1 + - '"api/mo/uni/infra/accportprof-leafintprftest/hports-anstest_accessportselector-typ-range.json" in binding_query.url' + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - check mode + aci_access_port_to_interface_policy_leaf_profile: &aci_access_port_to_interface_policy_leaf_profile_absent + <<: *aci_interface_policy_leaf_profile_present + access_port_selector: anstest_accessportselector + state: absent + check_mode: yes + register: accessport_to_intf_check_mode_absent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - delete works + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + register: accessport_to_intf_absent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - idempotency works + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + register: accessport_to_intf_absent_idempotent + +- name: Remove binding of interface access port selector and Interface Policy Leaf Profile - check mode + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + #access_port_selector: anstest_accessportselector + state: absent + ignore_errors: yes + register: accessport_to_intf_absent_missing_param + +- name: absent assertions + assert: + that: + - accessport_to_intf_check_mode_absent.changed == true + - accessport_to_intf_check_mode_absent.existing != [] + - accessport_to_intf_absent.changed == true + - accessport_to_intf_absent.existing == accessport_to_intf_check_mode_absent.existing + - accessport_to_intf_absent_idempotent.changed == false + - accessport_to_intf_absent_idempotent.existing == [] + - accessport_to_intf_absent_missing_param.failed == true + - 'accessport_to_intf_absent_missing_param.msg == "state is absent but all of the following are missing: access_port_selector"' + + +- name: Remove an interface access port selector associated with an Interface Policy Leaf Profile - Clean up + aci_access_port_to_interface_policy_leaf_profile: + <<: *aci_access_port_to_interface_policy_leaf_profile_absent + state: absent + +- name: Remove Interface policy leaf profile - Cleanup + aci_interface_policy_leaf_profile: + <<: *aci_interface_policy_leaf_profile_present + state: absent