From 79afb2eff16253e15df69d00f0c2135c21ee6766 Mon Sep 17 00:00:00 2001 From: Jacob McGill Date: Mon, 14 Aug 2017 21:13:50 -0400 Subject: [PATCH] new module: manage Bridge Domains [network/aci/aci_bridge_domain] (#28196) * ACI Bridge Domain: Add new module * fix type in docstring * fix type in docstring --- .../modules/network/aci/aci_bridge_domain.py | 277 ++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100755 lib/ansible/modules/network/aci/aci_bridge_domain.py diff --git a/lib/ansible/modules/network/aci/aci_bridge_domain.py b/lib/ansible/modules/network/aci/aci_bridge_domain.py new file mode 100755 index 0000000000..4e7ea0f277 --- /dev/null +++ b/lib/ansible/modules/network/aci/aci_bridge_domain.py @@ -0,0 +1,277 @@ +#!/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.0', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = r''' +--- +module: aci_bridge_domain +short_description: Manage Bridge Domains on Cisco ACI Fabrics +description: +- Manages Bridge Domains on Cisco ACI Fabrics. +author: +- Swetha Chunduri (@schunduri) +- Dag Wieers (@dagwieers) +- Jacob McGill (@jmcgill298) +requirements: +- ACI Fabric 1.0(3f)+ +version_added: '2.4' +notes: +- The tenant used must exist before using this module in your playbook. The M(aci_tenant) module can be used for this. +options: + arp_flooding: + description: + - Determines if the Bridge Domain should flood ARP traffic. + - The APIC defaults new Bridge Domains to "no". + choices: [ no, yes ] + bd: + description: + - The name of the Bridge Domain. + aliases: [ bd_name, name ] + bd_type: + description: + - The type of traffic on the Bridge Domain. + - The APIC defaults new Bridge Domains to Ethernet. + choices: [ ethernet, fc ] + description: + description: + - Description for the Bridge Domain. + enable_multicast: + description: + - Determines if PIM is enabled + - The APIC defaults new Bridge Domains to disabled. + choices: [ no, yes ] + enable_routing: + description: + - Determines if IP forwarding should be allowed. + - The APIC defaults new Bridge Domains to IP forwarding enabled. + choices: [ no, yes ] + endpoint_clear: + description: + - Clears all End Points in all Leaves when enabled. + - The APIC defaults new Bridge Domains to disabled. + - The value is not reset to disabled once End Points have been cleared; that requires a second task. + choices: [ no, yes ] + endpoint_move_detect: + description: + - Determines if GARP should be enabled to detect when End Points move. + - The APIC defaults new Bridge Domains to not use GARP. + choices: [ default, garp ] + endpoint_retention_action: + description: + - Determines if the Bridge Domain should inherit or resolve the End Point Retention Policy. + - The APIC defaults new Bridge Domain to End Point Retention Policies to resolve the policy. + endpoint_retention_policy: + description: + - The name of the End Point Retention Policy the Bridge Domain should use when + overriding the default End Point Retention Policy. + choices: [ inherit, resolve ] + igmp_snoop_policy: + description: + - The name of the IGMP Snooping Policy the Bridge Domain should use when + overriding the default IGMP Snooping Policy. + ip_learning: + description: + - Determines if the Bridge Domain should learn End Point IPs. + - The APIC defaults new Bridge Domains to enable IP learning. + choices: [ no, yes ] + ipv6_nd_policy: + description: + - The name of the IPv6 Neighbor Discovery Policy the Bridge Domain should use when + overridding teh default IPV6 ND Policy. + l2_unknown_unicast: + description: + - Determines what forwarding method to use for unknown l2 destinations. + - The APIC defaults new Bridge domains to use Hardware Proxy. + choices: [ proxy, flood ] + l3_unknown_multicast: + description: + - Determines the forwarding method to use for unknown multicast destinations. + - The APCI defaults new Bridge Domains to use normal flooding. + choices: [ flood, opt-flood ] + limit_ip_learn: + description: + - Determines if the BD should limit IP learning to only subnets owned by the Bridge Domain. + - The APIC defaults new Bridge Domains to learn all IP addresses. + choices: [ no, yes ] + multi_dest: + description: + - Determines the forwarding method for L2 multicast, broadcast, and link layer traffic. + - The APIC defaults new Bridge Domains to use bd-flood. + choices: [ bd-flood, drop, encap-flood ] + 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 + tenant: + description: + - The name of the Tenant. + aliases: [ tenant_name ] + vrf: + description: + - The name of the VRF. + aliases: [ vrf_name ] +''' + +EXAMPLES = r''' +- name: Add Bridge Domain + aci_bridge_domain: + action: "{{ action }}" + tenant: "{{ tenant }}" + bd: "{{ bd }}" + vrf: "{{ vrf }}" + arp_flooding: "{{ arp_flooding }}" + l2_unknown_unicast: "{{ l2_unknown_unicast }}" + l3_unknown_multicast: "{{ l3_unknown_multicast }}" + multi_dest: "{{ multi_dest }}" + gateway_ip: "{{ gateway_ip }}" + subnet_mask: "{{ subnet_mask }}" + scope: "{{ scope }}" + host: "{{ inventory_hostname }}" + username: "{{ username }}" + password: "{{ password }}" + protocol: "{{ protocol }}" +''' + +RETURN = r''' # ''' + +from ansible.module_utils.aci import ACIModule, aci_argument_spec +from ansible.module_utils.basic import AnsibleModule + + +def main(): + argument_spec = aci_argument_spec + argument_spec.update( + arp_flooding=dict(choices=['no', 'yes']), + bd=dict(type='str', aliases=['bd_name', 'name']), + bd_type=dict(type='str', choices=['ethernet', 'fc']), + description=dict(type='str'), + enable_multicast=dict(type='str', choices=['no', 'yes']), + enable_routing=dict(type='str', choices=['no', 'yes']), + endpoint_clear=dict(type='str', choices=['no', 'yes']), + endpoint_move_detect=dict(type='str', choices=['default', 'garp']), + endpoint_retention_action=dict(type='str', choices=['inherit', 'resolve']), + endpoint_retention_policy=dict(type='str'), + igmp_snoop_policy=dict(type='str'), + ip_learning=dict(type='str', choices=['no', 'yes']), + ipv6_nd_policy=dict(type='str'), + l2_unknown_unicast=dict(choices=['proxy', 'flood']), + l3_unknown_multicast=dict(choices=['flood', 'opt-flood']), + limit_ip_learn=dict(type='str', choices=['no', 'yes']), + multi_dest=dict(choices=['bd-flood', 'drop', 'encap-flood']), + state=dict(choices=['absent', 'present', 'query'], type='str', default='present'), + tenant=dict(type='str', aliases=['tenant_name']), + vrf=dict(type='str', aliases=['vrf_name']), + gateway_ip=dict(type='str', removed_in_version='2.4'), # Deprecated starting from v2.4 + method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6 + scope=dict(type='str', removed_in_version='2.4'), # Deprecated starting from v2.4 + subnet_mask=dict(type='str', removed_in_version='2.4') # Deprecated starting from v2.4 + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[['state', 'absent', ['bd', 'tenant']], + ['state', 'present', ['bd', 'tenant']]] + ) + + l2_unknown_unicast = module.params['l2_unknown_unicast'] + l3_unknown_multicast = module.params['l3_unknown_multicast'] + arp_flooding = module.params['arp_flooding'] + bd = module.params['bd'] + bd_type = module.params['bd_type'] + if bd_type == 'ethernet': + # ethernet type is represented as regular, but that is not clear to the users + bd_type = 'regular' + description = module.params['description'] + enable_multicast = module.params['enable_multicast'] + enable_routing = module.params['enable_routing'] + endpoint_clear = module.params['endpoint_clear'] + endpoint_move_detect = module.params['endpoint_move_detect'] + if endpoint_move_detect == 'default': + # the ACI default setting is an empty string, but that is not a good input value + endpoint_move_detect = '' + endpoint_retention_action = module.params['endpoint_retention_action'] + endpoint_retention_policy = module.params['endpoint_retention_policy'] + igmp_snoop_policy = module.params['igmp_snoop_policy'] + ip_learning = module.params['ip_learning'] + ipv6_nd_policy = module.params['ipv6_nd_policy'] + limit_ip_learn = module.params['limit_ip_learn'] + multi_dest = module.params['multi_dest'] + state = module.params['state'] + tenant = module.params['tenant'] + vrf = module.params['vrf'] + + # Give warning when fvSubnet parameters are passed as those have been moved to the aci_subnet module + if module.params['gateway_ip'] or module.params['subnet_mask'] or module.params['scope']: + module._warnings = ["The support for managing Subnets has been moved to its own module, aci_subnet. \ + The new modules still supports 'gateway_ip' and 'subnet_mask' along with more features"] + + if bd is not None: + if tenant is not None: + path = 'api/mo/uni/tn-%(tenant)s/BD-%(bd)s.json' % module.params + filter_string = '?rsp-subtree=full&rsp-subtree-class=fvRsCtx,fvRsIgmpsn,fvRsBDToNdP,fvRsBdToEpRet&rsp-prop-include=config-only' + else: + path = 'api/class/fvBD.json' + filter_string = ('?query-target-filter=eq(fvBD.name, \"%(bd)s\")&rsp-subtree=children' + '&rsp-subtree-class=fvRsCtx,fvRsIgmpsn,fvRsBDToNdP,fvRsBdToEpRet') % module.params + elif tenant is not None: + path = 'api/mo/uni/tn-%(tenant)s.json' % module.params + filter_string = '?rsp-subtree=full&rsp-subtree-class=fvBD,fvRsCtx,fvRsIgmpsn,fvRsBDToNdP,fvRsBdToEpRet' + else: + path = 'api/class/fvBD.json' + filter_string = "?rsp-subtree=full&rsp-subtree-class=fvBD,fvRsCtx,fvRsIgmpsn,fvRsBDToNdP,fvRsBdToEpRet" + + aci = ACIModule(module) + + aci.result['url'] = '%(protocol)s://%(hostname)s/' % aci.params + path + + aci.get_existing(filter_string=filter_string) + + if state == 'present': + # Filter out module params with null values + aci.payload(aci_class='fvBD', + class_config=dict(arpFlood=arp_flooding, + descr=description, + epClear=endpoint_clear, + epMoveDetectMode=endpoint_move_detect, + ipLearning=ip_learning, + limitIpLearnToSubnets=limit_ip_learn, + mcastAllow=enable_multicast, + multiDstPktAct=multi_dest, + name=bd, + type=bd_type, + unicastRoute=enable_routing, + unkMacUcastAct=l2_unknown_unicast, + unkMcastAct=l3_unknown_multicast), + child_configs=[{'fvRsCtx': {'attributes': {'tnFvCtxName': vrf}}}, + {'fvRsIgmpsn': {'attributes': {'tnIgmpSnoopPolName': igmp_snoop_policy}}}, + {'fvRsBDToNdP': {'attributes': {'tnNdIfPolName': ipv6_nd_policy}}}, + {'fvRsBdToEpRet': {'attributes': {'resolveAct': endpoint_retention_action, + 'tnFvEpRetPolName': endpoint_retention_policy}}}] + ) + + # generate config diff which will be used as POST request body + aci.get_diff(aci_class='fvBD') + + # 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()