Apply all devel ACI changes missing from stable-2.5 (#36553)

pull/36558/head
Dag Wieers 8 years ago committed by Matt Davis
parent 61c1241a77
commit df78df5d90

@ -300,7 +300,6 @@ class ACIModule(object):
except Exception as e:
# Expose RAW output for troubleshooting
self.error = dict(code=-1, text="Unable to parse output as JSON, see 'raw' output. %s" % e)
# self.error = dict(code=str(self.status), text="Request failed: %s (see 'raw' output)" % self.response)
self.result['raw'] = rawoutput
return
@ -324,7 +323,6 @@ class ACIModule(object):
except Exception as e:
# Expose RAW output for troubleshooting
self.error = dict(code=-1, text="Unable to parse output as XML, see 'raw' output. %s" % e)
# self.error = dict(code=str(self.status), text="Request failed: %s (see 'raw' output)" % self.response)
self.result['raw'] = rawoutput
return
@ -750,7 +748,8 @@ class ACIModule(object):
"""
update_config = {child_class: {'attributes': {}}}
for key, value in proposed_child.items():
if value != existing_child[key]:
existing_field = existing_child.get(key)
if value != existing_field:
update_config[child_class]['attributes'][key] = value
if not update_config[child_class]['attributes']:
@ -928,36 +927,40 @@ class ACIModule(object):
self.result['changed'] = True
self.method = 'POST'
def exit_json(self):
def exit_json(self, **kwargs):
if self.params['state'] in ('absent', 'present'):
if self.params['output_level'] in ('debug', 'info'):
self.result['previous'] = self.existing
if 'state' in self.params:
if self.params['state'] in ('absent', 'present'):
if self.params['output_level'] in ('debug', 'info'):
self.result['previous'] = self.existing
# Return the gory details when we need it
if self.params['output_level'] == 'debug':
self.result['filter_string'] = self.filter_string
if 'state' in self.params:
self.result['filter_string'] = self.filter_string
self.result['method'] = self.method
# self.result['path'] = self.path # Adding 'path' in result causes state: absent in output
self.result['response'] = self.response
self.result['status'] = self.status
self.result['url'] = self.url
self.original = self.existing
if self.params['state'] in ('absent', 'present'):
self.get_existing()
if 'state' in self.params:
self.original = self.existing
if self.params['state'] in ('absent', 'present'):
self.get_existing()
# if self.module._diff and self.original != self.existing:
# self.result['diff'] = dict(
# before=json.dumps(self.original, sort_keys=True, indent=4),
# after=json.dumps(self.existing, sort_keys=True, indent=4),
# )
self.result['current'] = self.existing
self.result['current'] = self.existing
if self.params['output_level'] in ('debug', 'info'):
self.result['sent'] = self.config
self.result['proposed'] = self.proposed
if self.params['output_level'] in ('debug', 'info'):
self.result['sent'] = self.config
self.result['proposed'] = self.proposed
self.result.update(**kwargs)
self.module.exit_json(**self.result)
def fail_json(self, msg, **kwargs):
@ -966,22 +969,31 @@ class ACIModule(object):
if self.error['code'] is not None and self.error['text'] is not None:
self.result['error'] = self.error
if self.params['state'] in ('absent', 'present'):
if self.params['output_level'] in ('debug', 'info'):
self.result['previous'] = self.existing
# Return the gory details when we need it
if self.params['output_level'] == 'debug':
if self.imdata is not None:
self.result['imdata'] = self.imdata
self.result['totalCount'] = self.totalCount
if 'state' in self.params:
if self.params['state'] in ('absent', 'present'):
if self.params['output_level'] in ('debug', 'info'):
self.result['previous'] = self.existing
# Return the gory details when we need it
if self.params['output_level'] == 'debug':
if self.imdata is not None:
self.result['imdata'] = self.imdata
self.result['totalCount'] = self.totalCount
if self.params['output_level'] == 'debug':
if self.url is not None:
self.result['filter_string'] = self.filter_string
if 'state' in self.params:
self.result['filter_string'] = self.filter_string
self.result['method'] = self.method
# self.result['path'] = self.path # Adding 'path' in result causes state: absent in output
self.result['response'] = self.response
self.result['status'] = self.status
self.result['url'] = self.url
if 'state' in self.params:
if self.params['output_level'] in ('debug', 'info'):
self.result['sent'] = self.config
self.result['proposed'] = self.proposed
self.result.update(**kwargs)
self.module.fail_json(msg=msg, **self.result)

@ -17,15 +17,16 @@ module: aci_aaa_user
short_description: Manage AAA users (aaa:User)
description:
- Manage AAA users.
- More information from the internal APIC class I(aaa:User) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
notes:
- This module is not idempotent when C(aaa_password) is being used
(even if that password was already set identically). This
appears to be an inconsistency wrt. the idempotent nature
of the APIC REST API.
of the APIC REST API. The vendor has been informed.
More information in :ref:`the ACI documentation <aci_guide_known_issues>`.
- More information from the internal APIC class I(aaa:User) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
requirements:
- python-dateutil
version_added: '2.5'
@ -246,7 +247,7 @@ def main():
aaa_password=dict(type='str', no_log=True),
aaa_password_lifetime=dict(type='int'),
aaa_password_update_required=dict(type='bool'),
aaa_user=dict(type='str', required=True, aliases=['name']),
aaa_user=dict(type='str', required=True, aliases=['name']), # Not required for querying all objects
clear_password_history=dict(type='bool'),
description=dict(type='str', aliases=['descr']),
email=dict(type='str'),
@ -306,7 +307,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='aaaUser',
class_config=dict(
@ -325,10 +325,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='aaaUser')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,15 +16,15 @@ DOCUMENTATION = r'''
module: aci_aaa_user_certificate
short_description: Manage AAA user certificates (aaa:UserCert)
description:
- Manage AAA user and appuser certificates.
- Manage AAA user certificates.
notes:
- The C(aaa_user) must exist before using this module in your playbook.
The M(aci_aaa_user) module can be used for this.
- More information from the internal APIC class I(aaa:UserCert) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.5'
notes:
- The C(aaa_user) must exist before using this module in your playbook.
The M(aci_aaa_user) module can be used for this.
options:
aaa_user:
description:
@ -212,10 +212,10 @@ ACI_MAPPING = dict(
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
aaa_user=dict(type='str', required=True),
aaa_user=dict(type='str', required=True), # Not required for querying all objects
aaa_user_type=dict(type='str', default='user', choices=['appuser', 'user']),
certificate=dict(type='str', aliases=['cert_data', 'certificate_data']),
certificate_name=dict(type='str', aliases=['cert_name']),
certificate=dict(type='str', aliases=['cert_data', 'certificate_data']), # Not required for querying all objects
certificate_name=dict(type='str', aliases=['cert_name']), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -252,7 +252,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='aaaUserCert',
class_config=dict(
@ -261,10 +260,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='aaaUserCert')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,6 +17,7 @@ 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.
notes:
- More information from the internal APIC class I(infra:HPortS, infra:RsAccBaseGrp, infra:PortBlk) at
U(https://developer.cisco.com/media/mim-ref).
author:
@ -36,7 +37,6 @@ options:
description:
description:
- The description to assign to the C(access_port_selector)
required: no
leaf_port_blk:
description:
- The name of the Fabric access policy leaf interface profile access port block.
@ -59,7 +59,6 @@ options:
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:
@ -227,8 +226,8 @@ 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_interface_profile': dict(type='str', aliases=['leaf_interface_profile_name']), # Not required for querying all objects
'access_port_selector': dict(type='str', aliases=['name', 'access_port_selector_name']), # Not required for querying all objects
'description': dict(typ='str'),
'leaf_port_blk': dict(type='str', aliases=['leaf_port_blk_name']),
'leaf_port_blk_description': dict(type='str'),
@ -277,7 +276,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='infraHPortS',
class_config=dict(
@ -292,23 +290,21 @@ def main():
name=leaf_port_blk,
fromPort=from_,
toPort=to_,
)
)
),
),
),
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':

@ -17,13 +17,12 @@ short_description: Manage attachable Access Entity Profile (AEP) on Cisco ACI fa
description:
- Connect to external virtual and physical domains by using
attachable Access Entity Profiles (AEP) on Cisco ACI fabrics.
notes:
- More information from the internal APIC classes I(infra:AttEntityP) and I(infra:ProvAcc) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Swetha Chunduri (@schunduri)
version_added: '2.4'
requirements:
- ACI Fabric 1.0(3f)+
options:
aep:
description:
@ -70,19 +69,19 @@ EXAMPLES = r'''
aep: ACI-AEP
state: absent
- name: Query an AEP
- name: Query all AEPs
aci_aep:
host: apic
username: admin
password: SomeSecretPassword
aep: ACI-AEP
state: query
- name: Query all AEPs
- name: Query a specific AEP
aci_aep:
host: apic
username: admin
password: SomeSecretPassword
aep: ACI-AEP
state: query
'''
@ -198,7 +197,7 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
aep=dict(type='str', aliases=['name', 'aep_name']), # not required for querying all AEPs
aep=dict(type='str', aliases=['name', 'aep_name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr']),
infra_vlan=dict(type='bool', aliases=['infrastructure_vlan']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
@ -237,7 +236,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='infraAttEntityP',
class_config=dict(
@ -247,10 +245,8 @@ def main():
child_configs=child_configs,
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='infraAttEntityP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,14 +17,14 @@ module: aci_aep_to_domain
short_description: Bind AEPs to Physical or Virtual Domains on Cisco ACI fabrics (infra:RsDomP)
description:
- Bind AEPs to Physical or Virtual Domains on Cisco ACI fabrics.
notes:
- The C(aep) and C(domain) parameters should exist before using this module.
The M(aci_aep) and M(aci_domain) can be used for these.
- More information from the internal APIC class I(infra:RsDomP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.5'
notes:
- The C(aep) and C(domain) parameters should exist before using this module.
The M(aci_aep) and M(aci_domain) can be used for these.
options:
aep:
description:
@ -54,7 +54,44 @@ options:
extends_documentation_fragment: aci
'''
EXAMPLES = r''' # '''
EXAMPLES = r'''
- name: Add AEP to domain binding
aci_aep_to_domain: &binding_present
host: apic
username: admin
password: SomeSecretPassword
aep: test_aep
domain: phys_dom
domain_type: phys
state: present
- name: Remove AEP to domain binding
aci_aep_to_domain: &binding_absent
host: apic
username: admin
password: SomeSecretPassword
aep: test_aep
domain: phys_dom
domain_type: phys
state: absent
- name: Query our AEP to domain binding
aci_aep_to_domain:
host: apic
username: admin
password: SomeSecretPassword
aep: test_aep
domain: phys_dom
domain_type: phys
state: query
- name: Query all AEP to domain bindings
aci_aep_to_domain: &binding_query
host: apic
username: admin
password: SomeSecretPassword
state: query
'''
RETURN = r'''
current:
@ -178,9 +215,9 @@ VM_PROVIDER_MAPPING = dict(
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
aep=dict(type='str', aliases=['aep_name']),
domain=dict(type='str', aliases=['domain_name', 'domain_profile']),
domain_type=dict(type='str', choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm'], aliases=['type']),
aep=dict(type='str', aliases=['aep_name']), # Not required for querying all objects
domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects
domain_type=dict(type='str', choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm'], aliases=['type']), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']),
)
@ -194,7 +231,7 @@ def main():
['state', 'present', ['aep', 'domain', 'domain_type']],
],
required_together=[
['domain', 'domain_type']
['domain', 'domain_type'],
],
)
@ -241,16 +278,13 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='infraRsDomP',
class_config=dict(tDn=domain_mo),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='infraRsDomP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,15 +16,15 @@ module: aci_ap
short_description: Manage top level Application Profile (AP) objects on Cisco ACI fabrics (fv:Ap)
description:
- Manage top level Application Profile (AP) objects on Cisco ACI fabrics
notes:
- This module does not manage EPGs, see M(aci_epg) to do this.
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(fv:Ap) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Swetha Chunduri (@schunduri)
version_added: '2.4'
notes:
- This module does not manage EPGs, see M(aci_epg) to do this.
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
tenant:
description:
@ -198,8 +198,8 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
tenant=dict(type='str', aliases=['tenant_name']), # tenant not required for querying all APs
ap=dict(type='str', aliases=['app_profile', 'app_profile_name', 'name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
ap=dict(type='str', aliases=['app_profile', 'app_profile_name', 'name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr'], required=False),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
@ -215,7 +215,6 @@ def main():
],
)
# tenant = module.params['tenant']
ap = module.params['ap']
description = module.params['description']
state = module.params['state']
@ -240,7 +239,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvAp',
class_config=dict(
@ -249,10 +247,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvAp')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_bd
short_description: Manage Bridge Domains (BD) on Cisco ACI Fabrics (fv:BD)
description:
- Manages Bridge Domains (BD) on Cisco ACI Fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(fv:BD) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(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:
@ -319,7 +319,7 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update(
arp_flooding=dict(type='bool'),
bd=dict(type='str', aliases=['bd_name', 'name']),
bd=dict(type='str', aliases=['bd_name', 'name']), # Not required for querying all objects
bd_type=dict(type='str', choices=['ethernet', 'fc']),
description=dict(type='str'),
enable_multicast=dict(type='bool'),
@ -337,7 +337,7 @@ def main():
mac_address=dict(type='str', aliases=['mac']),
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']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
vrf=dict(type='str', aliases=['vrf_name']),
gateway_ip=dict(type='str', removed_in_version='2.4'), # Deprecated starting from v2.4
scope=dict(type='str', removed_in_version='2.4'), # Deprecated starting from v2.4
@ -409,7 +409,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='fvBD',
class_config=dict(
@ -436,10 +435,8 @@ def main():
],
)
# 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':

@ -16,16 +16,16 @@ module: aci_bd_subnet
short_description: Manage Subnets on Cisco ACI fabrics (fv:Subnet)
description:
- Manage Subnets on Cisco ACI fabrics.
- More information from the internal APIC class I(fv:Subnet) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(gateway) parameter is the root key used to access the Subnet (not name), so the C(gateway)
is required when the state is C(absent) or C(present).
- The C(tenant) and C(bd) used must exist before using this module in your playbook.
The M(aci_tenant) module and M(aci_bd) can be used for these.
- More information from the internal APIC class I(fv:Subnet) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
options:
bd:
description:
@ -316,11 +316,11 @@ from ansible.module_utils.basic import AnsibleModule, SEQUENCETYPE
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
bd=dict(type='str', aliases=['bd_name']),
bd=dict(type='str', aliases=['bd_name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr']),
enable_vip=dict(type='bool'),
gateway=dict(type='str', aliases=['gateway_ip']),
mask=dict(type='int', aliases=['subnet_mask']),
gateway=dict(type='str', aliases=['gateway_ip']), # Not required for querying all objects
mask=dict(type='int', aliases=['subnet_mask']), # Not required for querying all objects
subnet_name=dict(type='str', aliases=['name']),
nd_prefix_policy=dict(type='str'),
preferred=dict(type='bool'),
@ -329,7 +329,7 @@ def main():
scope=dict(type='list', choices=['private', 'public', 'shared']),
subnet_control=dict(type='str', choices=['nd_ra', 'no_gw', 'querier_ip', 'unspecified']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
tenant=dict(type='str', aliases=['tenant_name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
)
@ -398,7 +398,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='fvSubnet',
class_config=dict(
@ -416,10 +415,8 @@ def main():
],
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvSubnet')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_bd_to_l3out
short_description: Bind Bridge Domain to L3 Out on Cisco ACI fabrics (fv:RsBDToOut)
description:
- Bind Bridge Domain to L3 Out on Cisco ACI fabrics.
notes:
- The C(bd) and C(l3out) parameters should exist before using this module.
The M(aci_bd) and M(aci_l3out) can be used for these.
- More information from the internal APIC class I(fv:RsBDToOut) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(bd) and C(l3out) parameters should exist before using this module.
The M(aci_bd) and M(aci_l3out) can be used for these.
options:
bd:
description:
@ -162,10 +162,10 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
bd=dict(type='str', aliases=['bd_name', 'bridge_domain']),
l3out=dict(type='str'),
bd=dict(type='str', aliases=['bd_name', 'bridge_domain']), # Not required for querying all objects
l3out=dict(type='str'), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
tenant=dict(type='str', aliases=['tenant_name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
)
@ -210,16 +210,13 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='fvRsBDToOut',
class_config=dict(tnL3extOutName=l3out),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvRsBDToOut')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,6 +17,7 @@ short_description: Provides rollback and rollback preview functionality for Cisc
description:
- Provides rollback and rollback preview functionality for Cisco ACI fabric.
- Config Rollbacks are done using snapshots C(aci_snapshot) with the configImportP class.
notes:
- More information from the internal APIC class I(config:ImportP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -97,9 +98,9 @@ EXAMPLES = r'''
password: SomeSecretPassword
state: preview
export_policy: config_backup
snapshot: 'run-2017-08-28T06-24-01'
snapshot: run-2017-08-28T06-24-01
compare_export_policy: config_backup
compare_snapshot: 'run-2017-08-27T23-43-56'
compare_snapshot: run-2017-08-27T23-43-56
- name: Rollback Configuration
aci_config_rollback:
@ -109,7 +110,7 @@ EXAMPLES = r'''
state: rollback
import_policy: rollback_config
export_policy: config_backup
snapshot: 'run-2017-08-28T06-24-01'
snapshot: run-2017-08-28T06-24-01
- name: Rollback Configuration
aci_config_rollback:
@ -119,8 +120,8 @@ EXAMPLES = r'''
state: rollback
import_policy: rollback_config
export_policy: config_backup
snapshot: 'run-2017-08-28T06-24-01'
description: 'Rollback 8-27 changes'
snapshot: run-2017-08-28T06-24-01
description: Rollback 8-27 changes
import_mode: atomic
import_type: replace
fail_on_decrypt: yes
@ -241,7 +242,6 @@ def main():
aci.get_existing()
# Filter out module parameters with null values
aci.payload(
aci_class='configImportP',
class_config=dict(
@ -256,10 +256,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='configImportP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'preview':

@ -18,15 +18,15 @@ description:
- Manage Config Snapshots on Cisco ACI fabrics.
- Creating new Snapshots is done using the configExportP class.
- Removing Snapshots is done using the configSnapshot class.
notes:
- The APIC does not provide a mechanism for naming the snapshots.
- 'Snapshot files use the following naming structure: ce_<config export policy name>-<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>.<mss>+<hh>:<mm>.'
- 'Snapshot objects use the following naming structure: run-<yyyy>-<mm>-<dd>T<hh>-<mm>-<ss>.'
- More information from the internal APIC classes I(config:Snapshot) and I(config:ExportP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The APIC does not provide a mechanism for naming the snapshots.
- 'Snapshot files use the following naming structure: ce_<config export policy name>-<yyyy>-<mm>-<dd>T<hh>:<mm>:<ss>.<mss>+<hh>:<mm>.'
- 'Snapshot objects use the following naming structure: run-<yyyy>-<mm>-<dd>T<hh>-<mm>-<ss>.'
options:
description:
description:
@ -215,7 +215,7 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update(
description=dict(type='str', aliases=['descr']),
export_policy=dict(type='str', aliases=['name']),
export_policy=dict(type='str', aliases=['name']), # Not required for querying all objects
format=dict(type='str', choices=['json', 'xml']),
include_secure=dict(type='bool'),
max_count=dict(type='int'),
@ -261,7 +261,6 @@ def main():
aci.get_existing()
# Filter out module params with null values
aci.payload(
aci_class='configExportP',
class_config=dict(

@ -16,16 +16,16 @@ module: aci_contract
short_description: Manage contract resources on Cisco ACI fabrics (vz:BrCP)
description:
- Manage Contract resources on Cisco ACI fabrics.
- More information from the internal APIC class I(vz:BrCP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- This module does not manage Contract Subjects, see M(aci_contract_subject) to do this.
Contract Subjects can still be removed using this module.
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(vz:BrCP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
options:
contract:
description:
@ -66,18 +66,42 @@ options:
extends_documentation_fragment: aci
'''
# FIXME: Add more, better examples
EXAMPLES = r'''
- aci_contract:
host: '{{ inventory_hostname }}'
username: '{{ username }}'
password: '{{ password }}'
contract: '{{ contract }}'
description: '{{ descr }}'
tenant: '{{ tenant }}'
scope: '{{ scope }}'
priority: '{{ priority }}'
target: '{{ target }}'
- name: Add a new contract
aci_contract:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
description: Communication between web-servers and database
scope: application-profile
state: present
- name: Remove an existing contract
aci_contract:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
state: absent
- name: Query a specific contract
aci_contract:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
state: query
- name: Query all contracts
aci_contract:
host: apic
username: admin
password: SomeSecretPassword
state: query
'''
RETURN = r'''
@ -210,8 +234,8 @@ def main():
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
['state', 'absent', ['tenant', 'contract']],
['state', 'present', ['tenant', 'contract']],
['state', 'absent', ['contract', 'tenant']],
['state', 'present', ['contract', 'tenant']],
],
)
@ -242,7 +266,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='vzBrCP',
class_config=dict(
@ -254,10 +277,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='vzBrCP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_contract_subject
short_description: Manage initial Contract Subjects on Cisco ACI fabrics (vz:Subj)
description:
- Manage initial Contract Subjects on Cisco ACI fabrics.
notes:
- The C(tenant) and C(contract) used must exist before using this module in your playbook.
- The M(aci_tenant) and M(aci_contract) modules can be used for this.
- More information from the internal APIC class I(vz:Subj) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Swetha Chunduri (@schunduri)
version_added: '2.4'
notes:
- The C(tenant) and C(contract) used must exist before using this module in your playbook.
- The M(aci_tenant) and M(aci_contract) modules can be used for this.
options:
tenant:
description:
@ -240,9 +240,9 @@ MATCH_MAPPING = dict(all='All', at_least_one='AtleastOne', at_most_one='AtmostOn
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
contract=dict(type='str', aliases=['contract_name']),
subject=dict(type='str', aliases=['contract_subject', 'name', 'subject_name']),
tenant=dict(type='str', aliases=['tenant_name']),
contract=dict(type='str', aliases=['contract_name']), # Not required for querying all objects
subject=dict(type='str', aliases=['contract_subject', 'name', 'subject_name']), # Not required for querying all objects
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
priority=dict(type='str', choices=['unspecified', 'level1', 'level2', 'level3']),
reverse_filter=dict(type='bool'),
dscp=dict(type='str', aliases=['target']),
@ -311,7 +311,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='vzSubj',
class_config=dict(
@ -325,10 +324,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='vzSubj')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,16 +16,14 @@ module: aci_contract_subject_to_filter
short_description: Bind Contract Subjects to Filters on Cisco ACI fabrics (vz:RsSubjFiltAtt)
description:
- Bind Contract Subjects to Filters on Cisco ACI fabrics.
notes:
- The C(tenant), C(contract), C(subject), and C(filter_name) must exist before using this module in your playbook.
- The M(aci_tenant), M(aci_contract), M(aci_contract_subject), and M(aci_filter) modules can be used for these.
- More information from the internal APIC class I(vz:RsSubjFiltAtt) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
requirements:
- ACI Fabric 1.0(3f)+
notes:
- The C(tenant), C(contract), C(subject), and C(filter_name) must exist before using this module in your playbook.
- The M(aci_tenant), M(aci_contract), M(aci_contract_subject), and M(aci_filter) modules can be used for these.
options:
contract:
description:
@ -60,17 +58,51 @@ options:
extends_documentation_fragment: aci
'''
# FIXME: Add more, better examples
EXAMPLES = r'''
- aci_subject_filter_binding:
host: '{{ inventory_hostname }}'
username: '{{ username }}'
password: '{{ password }}'
tenant: '{{ tenant }}'
contract: '{{ contract }}'
subject: '{{ subject }}'
- name: Add a new contract subject to filer binding
aci_subject_filter_binding:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
subject: test
filter: '{{ filter }}'
log: '{{ log }}'
state: present
- name: Remove an existing contract subject to filter binding
aci_subject_filter_binding:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
subject: test
filter: '{{ filter }}'
log: '{{ log }}'
state: present
- name: Query a specific contract subject to filter binding
aci_subject_filter_binding:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
subject: test
filter: '{{ filter }}'
state: query
- name: Query all contract subject to filter bindings
aci_subject_filter_binding:
host: apic
username: admin
password: SomeSecretPassword
tenant: production
contract: web_to_db
subject: test
state: query
'''
RETURN = r'''
@ -185,11 +217,11 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
contract=dict(type='str', aliases=['contract_name']),
filter=dict(type='str', aliases=['filter_name']),
contract=dict(type='str', aliases=['contract_name']), # Not required for querying all objects
filter=dict(type='str', aliases=['filter_name']), # Not required for querying all objects
log=dict(tyep='str', choices=['log', 'none'], aliases=['directive']),
subject=dict(type='str', aliases=['contract_subject', 'subject_name']),
tenant=dict(type='str', aliases=['tenant_name']),
subject=dict(type='str', aliases=['contract_subject', 'subject_name']), # Not required for querying all objects
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
@ -249,7 +281,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='vzRsSubjFiltAtt',
class_config=dict(
@ -258,10 +289,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='vzRsSubjFiltAtt')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_domain
short_description: Manage physical, virtual, bridged, routed or FC domain profiles (*:DomP)
description:
- Manage physical, virtual, bridged, routed or FC domain profiles.
notes:
- More information from the internal APIC classes I(phys:DomP),
I(vmm:DomP), I(l2ext:DomP), I(l3ext:DomP), I(fc:DomP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
@ -259,8 +260,8 @@ def main():
choices=['AF11', 'AF12', 'AF13', 'AF21', 'AF22', 'AF23', 'AF31', 'AF32', 'AF33', 'AF41', 'AF42', 'AF43',
'CS0', 'CS1', 'CS2', 'CS3', 'CS4', 'CS5', 'CS6', 'CS7', 'EF', 'VA', 'unspecified'],
aliases=['target']),
domain=dict(type='str', aliases=['domain_name', 'domain_profile', 'name']),
domain_type=dict(type='str', choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm'], aliases=['type']),
domain=dict(type='str', aliases=['domain_name', 'domain_profile', 'name']), # Not required for querying all objects
domain_type=dict(type='str', required=True, choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm'], aliases=['type']), # Not required for querying all objects
encap_mode=dict(type='str', choices=['unknown', 'vlan', 'vxlan']),
multicast_address=dict(type='str'),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
@ -291,13 +292,13 @@ def main():
if domain_type != 'vmm':
if vm_provider is not None:
module.fail_json(msg="Domain type '{0}' cannot have a 'vm_provider'".format(domain_type))
module.fail_json(msg="Domain type '{0}' cannot have parameter 'vm_provider'".format(domain_type))
if encap_mode is not None:
module.fail_json(msg="Domain type '{0}' cannot have an 'encap_mode'".format(domain_type))
module.fail_json(msg="Domain type '{0}' cannot have parameter 'encap_mode'".format(domain_type))
if multicast_address is not None:
module.fail_json(msg="Domain type '{0}' cannot have a 'multicast_address'".format(domain_type))
module.fail_json(msg="Domain type '{0}' cannot have parameter 'multicast_address'".format(domain_type))
if vswitch is not None:
module.fail_json(msg="Domain type '{0}' cannot have a 'vswitch'".format(domain_type))
module.fail_json(msg="Domain type '{0}' cannot have parameter 'vswitch'".format(domain_type))
if dscp is not None and domain_type not in ['l2dom', 'l3dom']:
module.fail_json(msg="DSCP values can only be assigned to 'l2ext and 'l3ext' domains")
@ -324,6 +325,10 @@ def main():
domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain)
domain_rn = 'vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain)
# Ensure that querying all objects works when only domain_type is provided
if domain is None:
domain_mo = None
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
@ -337,7 +342,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class=domain_class,
class_config=dict(
@ -349,10 +353,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class=domain_class)
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -1,310 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
# 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_domain_to_encap_pool
short_description: Bind Domain to Encap Pools on Cisco ACI fabrics (infra:RsVlanNs)
description:
- Bind Domain to Encap Pools on Cisco ACI fabrics.
- More information from the internal APIC class I(infra:RsVlanNs) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.5'
notes:
- The C(domain) and C(encap_pool) parameters should exist before using this module.
The M(aci_domain) and M(aci_encap_pool) can be used for these.
options:
domain:
description:
- Name of the domain being associated with the Encap Pool.
aliases: [ domain_name, domain_profile ]
domain_type:
description:
- Determines if the Domain is physical (phys) or virtual (vmm).
choices: [ fc, l2dom, l3dom, phys, vmm ]
pool:
description:
- The name of the pool.
aliases: [ pool_name ]
pool_allocation_mode:
description:
- The method used for allocating encaps to resources.
- Only vlan and vsan support allocation modes.
choices: [ dynamic, static]
aliases: [ allocation_mode, mode ]
pool_type:
description:
- The encap type of C(pool).
required: yes
choices: [ vlan, vsan, vxsan ]
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
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.
choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
extends_documentation_fragment: aci
'''
EXAMPLES = r''' # '''
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: string
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
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: string
sample: ?rsp-prop-include=config-only
method:
description: The HTTP method used for the request to the APIC
returned: failure or debug
type: string
sample: POST
response:
description: The HTTP response from the APIC
returned: failure or debug
type: string
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: string
sample: https://10.11.12.13/api/mo/uni/tn-production.json
'''
from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
from ansible.module_utils.basic import AnsibleModule
VM_PROVIDER_MAPPING = dict(
cloudfoundry='CloudFoundry',
kubernetes='Kubernetes',
microsoft='Microsoft',
openshift='OpenShift',
openstack='OpenStack',
redhat='Redhat',
vmware='VMware',
)
POOL_MAPPING = dict(
vlan=dict(
aci_mo='uni/infra/vlanns-',
child_class='infraRsVlanNs',
),
vxlan=dict(
aci_mo='uni/infra/vxlanns-',
child_class='vmmRsVxlanNs',
),
vsan=dict(
aci_mo='uni/infra/vsanns-',
child_class='fcRsVsanNs',
),
)
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
domain=dict(type='str', aliases=['domain_name', 'domain_profile']),
domain_type=dict(type='str', choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm']),
pool=dict(type='str', aliases=['pool_name']),
pool_allocation_mode=dict(type='str', aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']),
pool_type=dict(type='str', required=True, choices=['vlan', 'vsan', 'vxlan']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']),
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
['domain_type', 'vmm', ['vm_provider']],
['state', 'absent', ['domain', 'domain_type', 'pool', 'pool_type']],
['state', 'present', ['domain', 'domain_type', 'pool', 'pool_type']],
],
)
domain = module.params['domain']
domain_type = module.params['domain_type']
pool = module.params['pool']
pool_allocation_mode = module.params['pool_allocation_mode']
pool_type = module.params['pool_type']
vm_provider = module.params['vm_provider']
state = module.params['state']
# Report when vm_provider is set when type is not virtual
if domain_type != 'vmm' and vm_provider is not None:
module.fail_json(msg="Domain type '{0}' cannot have a 'vm_provider'".format(domain_type))
# ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static)
pool_name = pool
if pool_type != 'vxlan' and pool is not None:
if pool_allocation_mode is not None:
pool_name = '[{0}]-{1}'.format(pool, pool_allocation_mode)
else:
module.fail_json(msg="ACI requires the 'pool_allocation_mode' for 'pool_type' of 'vlan' and 'vsan' when 'pool' is provided")
# Vxlan pools do not support allocation modes
if pool_type == 'vxlan' and pool_allocation_mode is not None:
module.fail_json(msg='vxlan pools do not support setting the allocation_mode; please remove this parameter from the task')
# Compile the full domain for URL building
if domain_type == 'fc':
domain_class = 'fcDomP'
domain_mo = 'uni/fc-{0}'.format(domain)
domain_rn = 'fc-{0}'.format(domain)
elif domain_type == 'l2ext':
domain_class = 'l2extDomP'
domain_mo = 'uni/l2dom-{0}'.format(domain)
domain_rn = 'l2dom-{0}'.format(domain)
elif domain_type == 'l3ext':
domain_class = 'l3extDomP'
domain_mo = 'uni/l3dom-{0}'.format(domain)
domain_rn = 'l3dom-{0}'.format(domain)
elif domain_type == 'phys':
domain_class = 'physDomP'
domain_mo = 'uni/phys-{0}'.format(domain)
domain_rn = 'phys-{0}'.format(domain)
elif domain_type == 'vmm':
domain_class = 'vmmDomP'
domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain)
domain_rn = 'vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain)
pool_mo = POOL_MAPPING[pool_type]["aci_mo"] + pool_name
child_class = POOL_MAPPING[pool_type]['child_class']
aci = ACIModule(module)
aci.construct_url(
root_class=dict(
aci_class=domain_class,
aci_rn=domain_rn,
filter_target='eq({0}.name, "{1}")'.format(domain_class, domain),
module_object=domain_mo,
),
child_classes=[child_class],
)
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class=domain_class,
class_config=dict(name=domain),
child_configs=[
{child_class: {'attributes': {'tDn': pool_mo}}},
]
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class=domain_class)
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':
aci.delete_config()
aci.exit_json()
if __name__ == "__main__":
main()

@ -17,14 +17,14 @@ module: aci_domain_to_vlan_pool
short_description: Bind Domain to VLAN Pools on Cisco ACI fabrics (infra:RsVlanNs)
description:
- Bind Domain to VLAN Pools on Cisco ACI fabrics.
notes:
- The C(domain) and C(vlan_pool) parameters should exist before using this module.
The M(aci_domain) and M(aci_vlan_pool) can be used for these.
- More information from the internal APIC class I(infra:RsVlanNs) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.5'
notes:
- The C(domain) and C(vlan_pool) parameters should exist before using this module.
The M(aci_domain) and M(aci_vlan_pool) can be used for these.
options:
domain:
description:
@ -122,6 +122,7 @@ EXAMPLES = r'''
host: apic
username: admin
password: SomeSecretPassword
domain_type: phys
state: query
'''
@ -234,7 +235,10 @@ from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
from ansible.module_utils.basic import AnsibleModule
VM_PROVIDER_MAPPING = dict(
cloudfoundry='CloudFoundry',
kubernetes='Kubernetes',
microsoft='Microsoft',
openshift='OpenShift',
openstack='OpenStack',
redhat='Redhat',
vmware='VMware',
@ -244,9 +248,9 @@ VM_PROVIDER_MAPPING = dict(
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
domain=dict(type='str', aliases=['domain_name', 'domain_profile']),
domain_type=dict(type='str', choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm']),
pool=dict(type='str', aliases=['pool_name', 'vlan_pool']),
domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects
domain_type=dict(type='str', required=True, choices=['fc', 'l2dom', 'l3dom', 'phys', 'vmm']), # Not required for querying all objects
pool=dict(type='str', aliases=['pool_name', 'vlan_pool']), # Not required for querying all objects
pool_allocation_mode=dict(type='str', required=True, aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']),
@ -300,7 +304,11 @@ def main():
domain_mo = 'uni/vmmp-{0}/dom-{1}'.format(VM_PROVIDER_MAPPING[vm_provider], domain)
domain_rn = 'dom-{0}'.format(domain)
aci_mo = 'uni/infra/vlanns-' + pool_name
# Ensure that querying all objects works when only domain_type is provided
if domain is None:
domain_mo = None
aci_mo = 'uni/infra/vlanns-{0}'.format(pool_name)
aci = ACIModule(module)
aci.construct_url(
@ -316,7 +324,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class=domain_class,
class_config=dict(name=domain),
@ -325,10 +332,8 @@ def main():
]
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class=domain_class)
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_encap_pool
short_description: Manage encap pools on Cisco ACI fabrics (fvns:VlanInstP, fvns:VxlanInstP, fvns:VsanInstP)
description:
- Manage vlan, vxlan, and vsan pools on Cisco ACI fabrics.
notes:
- More information from the internal APIC class
I(fvns:VlanInstP), I(fvns:VxlanInstP), and I(fvns:VsanInstP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
@ -218,7 +219,7 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update(
description=dict(type='str', aliases=['descr']),
pool=dict(type='str', aliases=['name', 'pool_name']),
pool=dict(type='str', aliases=['name', 'pool_name']), # Not required for querying all objects
pool_allocation_mode=dict(type='str', aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']),
pool_type=dict(type='str', aliases=['type'], choices=['vlan', 'vxlan', 'vsan'], required=True),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),

@ -16,13 +16,13 @@ module: aci_encap_pool_range
short_description: Manage encap ranges assigned to pools on Cisco ACI fabrics (fvns:EncapBlk, fvns:VsanEncapBlk)
description:
- Manage vlan, vxlan, and vsan ranges that are assigned to pools on Cisco ACI fabrics.
notes:
- The C(pool) must exist in order to add or delete a range.
- More information from the internal APIC class I(fvns:EncapBlk) and I(fvns:VsanEncapBlk) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.5'
requirements:
- The C(pool) must exist in order to add or delete a range.
options:
allocation_mode:
description:
@ -252,12 +252,12 @@ def main():
argument_spec.update(
allocation_mode=dict(type='str', aliases=['mode'], choices=['dynamic', 'inherit', 'static']),
description=dict(type='str', aliases=['descr']),
pool=dict(type='str', aliases=['pool_name']),
pool=dict(type='str', aliases=['pool_name']), # Not required for querying all objects
pool_allocation_mode=dict(type='str', aliases=['pool_mode'], choices=['dynamic', 'static']),
pool_type=dict(type='str', aliases=['type'], choices=['vlan', 'vxlan', 'vsan'], required=True),
range_end=dict(type='int', aliases=['end']),
range_name=dict(type='str', aliases=["name", "range"]),
range_start=dict(type='int', aliases=["start"]),
range_end=dict(type='int', aliases=['end']), # Not required for querying all objects
range_name=dict(type='str', aliases=["name", "range"]), # Not required for querying all objects
range_start=dict(type='int', aliases=["start"]), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -383,7 +383,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class=aci_range_class,
class_config={
@ -392,13 +391,11 @@ def main():
"from": encap_start,
"name": range_name,
"to": encap_end,
}
},
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class=aci_range_class)
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_epg
short_description: Manage End Point Groups (EPG) on Cisco ACI fabrics (fv:AEPg)
description:
- Manage End Point Groups (EPG) on Cisco ACI fabrics.
notes:
- The C(tenant) and C(app_profile) used must exist before using this module in your playbook.
The M(aci_tenant) and M(aci_ap) modules can be used for this.
- More information from the internal APIC class I(fv:AEPg) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Swetha Chunduri (@schunduri)
version_added: '2.4'
notes:
- The C(tenant) and C(app_profile) used must exist before using this module in your playbook.
The M(aci_tenant) and M(aci_ap) modules can be used for this.
options:
tenant:
description:
@ -270,10 +270,10 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
epg=dict(type='str', aliases=['name', 'epg_name']),
epg=dict(type='str', aliases=['name', 'epg_name']), # Not required for querying all objects
bd=dict(type='str', aliases=['bd_name', 'bridge_domain']),
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']),
tenant=dict(type='str', aliases=['tenant_name']),
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr']),
priority=dict(type='str', choices=['level1', 'level2', 'level3', 'unspecified']),
intra_epg_isolation=dict(choices=['enforced', 'unenforced']),
@ -331,7 +331,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvAEPg',
class_config=dict(
@ -347,10 +346,8 @@ def main():
],
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvAEPg')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,16 +16,14 @@ module: aci_epg_monitoring_policy
short_description: Manage monitoring policies on Cisco ACI fabrics (mon:EPGPol)
description:
- Manage monitoring policies on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(mon:EPGPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
requirements:
- ACI Fabric 1.0(3f)+
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
monitoring_policy:
description:
@ -214,7 +212,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='monEPGPol',
class_config=dict(
@ -223,10 +220,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='monEPGPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_epg_to_contract
short_description: Bind EPGs to Contracts on Cisco ACI fabrics (fv:RsCons and fv:RsProv)
description:
- Bind EPGs to Contracts on Cisco ACI fabrics.
notes:
- The C(tenant), C(app_profile), C(EPG), and C(Contract) used must exist before using this module in your playbook.
The M(aci_tenant), M(aci_ap), M(aci_epg), and M(aci_contract) modules can be used for this.
- More information from the internal APIC classes I(fv:RsCons) and I(fv:RsProv) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(tenant), C(app_profile), C(EPG), and C(Contract) used must exist before using this module in your playbook.
The M(aci_tenant), M(aci_ap), M(aci_epg), and M(aci_contract) modules can be used for this.
options:
ap:
description:
@ -67,7 +67,51 @@ options:
extends_documentation_fragment: aci
'''
EXAMPLES = r''' # '''
EXAMPLES = r'''
- name: Add a new contract to EPG binding
aci_epg_to_contract:
host: apic
username: admin
password: SomeSecretPassword
tenant: anstest
ap: anstest
epg: anstest
contract: anstest_http
contract_type: provider
state: present
- name: Remove an existing contract to EPG binding
aci_epg_to_contract:
host: apic
username: admin
password: SomeSecretPassword
tenant: anstest
ap: anstest
epg: anstest
contract: anstest_http
contract_type: provider
state: absent
- name: Query a specific contract to EPG binding
aci_epg_to_contract:
host: apic
username: admin
password: SomeSecretPassword
tenant: anstest
ap: anstest
epg: anstest
contract: anstest_http
contract_type: provider
state: query
- name: Query all provider contract to EPG bindings
aci_epg_to_contract:
host: apic
username: admin
password: SomeSecretPassword
contract_type: provider
state: query
'''
RETURN = r'''
current:
@ -184,14 +228,14 @@ PROVIDER_MATCH_MAPPING = {"all": "All", "at_least_one": "AtleastOne", "at_most_o
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']),
epg=dict(type='str', aliases=['epg_name']),
contract=dict(type='str', aliases=['contract_name']),
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects
epg=dict(type='str', aliases=['epg_name']), # Not required for querying all objects
contract=dict(type='str', aliases=['contract_name']), # Not required for querying all objects
contract_type=dict(type='str', required=True, choices=['consumer', 'provider']),
priority=dict(type='str', choices=['level1', 'level2', 'level3', 'unspecified']),
provider_match=dict(type='str', choices=['all', 'at_least_one', 'at_most_one', 'none']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
tenant=dict(type='str', aliases=['tenant_name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
)
@ -253,7 +297,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class=aci_class,
class_config=dict(
@ -263,10 +306,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class=aci_class)
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,17 +16,17 @@ module: aci_epg_to_domain
short_description: Bind EPGs to Domains on Cisco ACI fabrics (fv:RsDomAtt)
description:
- Bind EPGs to Physical and Virtual Domains on Cisco ACI fabrics.
- More information from the internal APIC class I(fv:RsDomAtt) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(tenant), C(ap), C(epg), and C(domain) used must exist before using this module in your playbook.
The M(aci_tenant) M(aci_ap), M(aci_epg) M(aci_domain) modules can be used for this.
- OpenStack VMM domains must not be created using this module. The OpenStack VMM domain is created directly
by the Cisco APIC Neutron plugin as part of the installation and configuration.
This module can be used to query status of an OpenStack VMM domain.
- More information from the internal APIC class I(fv:RsDomAtt) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
options:
allow_useg:
description:
@ -103,7 +103,50 @@ options:
extends_documentation_fragment: aci
'''
EXAMPLES = r''' # '''
EXAMPLES = r'''
- name: Add a new physical domain to EPG binding
aci_epg_to_domain:
host: apic
username: admin
password: SomeSecretPassword
tenant: anstest
ap: anstest
epg: anstest
domain: anstest
domain_type: phys
state: present
- name: Remove an existing physical domain to EPG binding
aci_epg_to_domain:
host: apic
username: admin
password: SomeSecretPassword
tenant: anstest
ap: anstest
epg: anstest
domain: anstest
domain_type: phys
state: absent
- name: Query a specific physical domain to EPG binding
aci_epg_to_domain:
host: apic
username: admin
password: SomeSecretPassword
tenant: anstest
ap: anstest
epg: anstest
domain: anstest
domain_type: phys
state: query
- name: Query all domain to EPG bindings
aci_epg_to_domain:
host: apic
username: admin
password: SomeSecretPassword
state: query
'''
RETURN = r'''
current:
@ -228,18 +271,18 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update(
allow_useg=dict(type='str', choices=['encap', 'useg']),
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']),
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects
deploy_immediacy=dict(type='str', choices=['immediate', 'on-demand']),
domain=dict(type='str', aliases=['domain_name', 'domain_profile']),
domain_type=dict(type='str', choices=['phys', 'vmm'], aliases=['type']),
domain=dict(type='str', aliases=['domain_name', 'domain_profile']), # Not required for querying all objects
domain_type=dict(type='str', choices=['phys', 'vmm'], aliases=['type']), # Not required for querying all objects
encap=dict(type='int'),
encap_mode=dict(type='str', choices=['auto', 'vlan', 'vxlan']),
epg=dict(type='str', aliases=['name', 'epg_name']),
epg=dict(type='str', aliases=['name', 'epg_name']), # Not required for querying all objects
netflow=dict(type='raw'), # Turn into a boolean in v2.9
primary_encap=dict(type='int'),
resolution_immediacy=dict(type='str', choices=['immediate', 'lazy', 'pre-provision']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
tenant=dict(type='str', aliases=['tenant_name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
vm_provider=dict(type='str', choices=['cloudfoundry', 'kubernetes', 'microsoft', 'openshift', 'openstack', 'redhat', 'vmware']),
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
@ -323,7 +366,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvRsDomAtt',
class_config=dict(
@ -337,10 +379,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvRsDomAtt')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,6 +17,7 @@ module: aci_fabric_node
short_description: Add a new Fabric Node Member on Cisco ACI fabrics (fabric:NodeIdentP)
description:
- Add a new Fabric Node Member on Cisco ACI fabrics.
notes:
- More information from the internal APIC class
I(fabric:NodeIdentP) at U(https://developer.cisco.com/site/aci/docs/apis/apic-mim-ref/).
author:
@ -36,7 +37,7 @@ options:
switch:
description:
- Switch Name for the new Fabric Node Member.
aliases: [ switch_name ]
aliases: [ name, switch_name ]
description:
description:
- Description for the new Fabric Node Member.
@ -71,7 +72,108 @@ EXAMPLES = r'''
'''
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: string
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
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: string
sample: '?rsp-prop-include=config-only'
method:
description: The HTTP method used for the request to the APIC
returned: failure or debug
type: string
sample: POST
response:
description: The HTTP response from the APIC
returned: failure or debug
type: string
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: string
sample: https://10.11.12.13/api/mo/uni/tn-production.json
'''
from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
@ -84,12 +186,12 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
pod_id=dict(type='int'),
serial=dict(type='str', aliases=['serial_number']),
node_id=dict(type='int'),
switch=dict(type='str', aliases=['switch_name']),
description=dict(type='str', aliases=['descr']),
node_id=dict(type='int'), # Not required for querying all objects
pod_id=dict(type='int'),
role=dict(type='str', choices=['leaf', 'spine', 'unspecified'], aliases=['role_name']),
serial=dict(type='str', aliases=['serial_number']), # Not required for querying all objects
switch=dict(type='str', aliases=['name', 'switch_name']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -97,8 +199,8 @@ def main():
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
['state', 'absent', ['serial', 'node_id']],
['state', 'present', ['serial', 'node_id']],
['state', 'absent', ['node_id', 'serial']],
['state', 'present', ['node_id', 'serial']],
],
)
@ -123,31 +225,27 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fabricNodeIdentP',
class_config=dict(
dn='uni/controller/nodeidentpol/nodep-{0}'.format(serial),
podId=pod_id,
serial=serial,
nodeId=node_id,
descr=description,
name=switch,
role=role,
nodeId=node_id,
podId=pod_id,
rn='nodep-{0}'.format(serial),
descr=description,
role=role,
serial=serial,
)
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fabricNodeIdentP')
# 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)
aci.exit_json(**aci.result)
if __name__ == "__main__":

@ -16,15 +16,15 @@ module: aci_filter
short_description: Manages top level filter objects on Cisco ACI fabrics (vz:Filter)
description:
- Manages top level filter objects on Cisco ACI fabrics.
- This modules does not manage filter entries, see M(aci_filter_entry) for this functionality.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(vz:Filter) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
- This modules does not manage filter entries, see M(aci_filter_entry) for this functionality.
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
filter:
description:
@ -240,7 +240,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='vzFilter',
class_config=dict(
@ -249,10 +248,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='vzFilter')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_filter_entry
short_description: Manage filter entries on Cisco ACI fabrics (vz:Entry)
description:
- Manage filter entries for a filter on Cisco ACI fabrics.
notes:
- The C(tenant) and C(filter) used must exist before using this module in your playbook.
The M(aci_tenant) and M(aci_filter) modules can be used for this.
- More information from the internal APIC class I(vz:Entry) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(tenant) and C(filter) used must exist before using this module in your playbook.
The M(aci_tenant) and M(aci_filter) modules can be used for this.
options:
arp_flag:
description:
@ -247,15 +247,15 @@ def main():
dst_port=dict(type='str'),
dst_port_end=dict(type='str'),
dst_port_start=dict(type='str'),
entry=dict(type='str', aliases=['entry_name', 'filter_entry', 'name']),
entry=dict(type='str', aliases=['entry_name', 'filter_entry', 'name']), # Not required for querying all objects
ether_type=dict(choices=VALID_ETHER_TYPES, type='str'),
filter=dict(type='str', aliases=['filter_name']),
filter=dict(type='str', aliases=['filter_name']), # Not required for querying all objects
icmp_msg_type=dict(type='str', choices=VALID_ICMP_TYPES),
icmp6_msg_type=dict(type='str', choices=VALID_ICMP6_TYPES),
ip_protocol=dict(choices=VALID_IP_PROTOCOLS, type='str'),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
stateful=dict(type='bool'),
tenant=dict(type="str", aliases=['tenant_name']),
tenant=dict(type="str", aliases=['tenant_name']), # Not required for querying all objects
)
module = AnsibleModule(
@ -327,7 +327,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='vzEntry',
class_config=dict(
@ -344,10 +343,8 @@ def main():
),
)
# generate config diff which will be used as POST request body
aci.get_diff(aci_class='vzEntry')
# submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,11 +17,12 @@ module: aci_firmware_source
short_description: Manage firmware image sources on Cisco ACI fabrics (firmware:OSource)
description:
- Manage firmware image sources on Cisco ACI fabrics.
- More information from the internal APIC class I(firmware:OSource) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.5'
notes:
- More information from the internal APIC class I(firmware:OSource) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
options:
source:
description:
@ -58,35 +59,35 @@ extends_documentation_fragment: aci
EXAMPLES = r'''
- name: Add firmware source
aci_firmware_source:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
source: aci-msft-pkg-3.1.1i.zip
url: foobar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip
url: foo.bar.cisco.com/download/cisco/aci/aci-msft-pkg-3.1.1i.zip
url_protocol: http
state: present
- name: Remove firmware source
aci_firmware_source:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
source: aci-msft-pkg-3.1.1i.zip
state: absent
- name: Query all firmware sources
- name: Query a specific firmware source
aci_firmware_source:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
source: aci-msft-pkg-3.1.1i.zip
state: query
- name: Query a specific firmware source
- name: Query all firmware sources
aci_firmware_source:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
source: aci-msft-pkg-3.1.1i.zip
host: apic
username: admin
password: SomeSecretPassword
state: query
'''
@ -241,7 +242,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='firmwareOSource',
class_config=dict(
@ -254,10 +254,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='firmwareOSource')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,11 +16,12 @@ module: aci_interface_policy_fc
short_description: Manage Fibre Channel interface policies on Cisco ACI fabrics (fc:IfPol)
description:
- Manage ACI Fiber Channel interface policies on Cisco ACI fabrics.
- More information from the internal APIC class I(fc:IfPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- More information from the internal APIC class I(fc:IfPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
options:
fc_policy:
description:
@ -203,7 +204,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fcIfPol',
class_config=dict(
@ -213,10 +213,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fcIfPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,11 +16,12 @@ module: aci_interface_policy_l2
short_description: Manage Layer 2 interface policies on Cisco ACI fabrics (l2:IfPol)
description:
- Manage Layer 2 interface policies on Cisco ACI fabrics.
- More information from the internal APIC class I(l2:IfPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- More information from the internal APIC class I(l2:IfPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
options:
l2_policy:
description:
@ -222,7 +223,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='l2IfPol',
class_config=dict(
@ -233,10 +233,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='l2IfPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,14 +17,14 @@ module: aci_interface_policy_leaf_policy_group
short_description: Add Fabric Interface Policy Leaf Policy Groups on Cisco ACI fabrics.
description:
- Add Fabric Interface Policy Leaf Policy Groups on Cisco ACI fabrics.
notes:
- When using the module please select the appropriate link_aggregation_type (lag_type).
C(link) for Port Channel(PC), C(node) for Virtual Port Channel(VPC) and C(leaf) for Leaf Access Port Policy Group.
- More information from the internal APIC class I(infra:AccBndlGrp), I(infra:AccPortGrp) at
U(https://developer.cisco.com/site/aci/docs/apis/apic-mim-ref/).
author:
- Bruno Calogero (@brunocalogero)
version_added: '2.5'
notes:
- When using the module please select the appropriate link_aggregation_type (lag_type).
C(link) for Port Channel(PC), C(node) for Virtual Port Channel(VPC) and C(leaf) for Leaf Access Port Policy Group.
options:
policy_group:
description:
@ -37,7 +37,11 @@ options:
lag_type:
description:
- Selector for the type of leaf policy group we want to create.
- C(leaf) for Leaf Access Port Policy Group
- C(link) for Port Channel (PC)
- C(node) for Virtual Port Channel (VPC)
aliases: [ lag_type_name ]
choices: [ leaf, link, node ]
link_level_policy:
description:
- Choice of link_level_policy to be used as part of the leaf policy group to be created.
@ -111,12 +115,13 @@ options:
extends_documentation_fragment: aci
'''
# FIXME: Add query examples
EXAMPLES = r'''
- name: creating a Port Channel (PC) Interface Policy Group
- name: Create a Port Channel (PC) Interface Policy Group
aci_interface_policy_leaf_policy_group:
host: apic
username: yourusername
password: yourpassword
username: admin
password: SomeSecretPassword
policy_group: policygroupname
description: policygroupname description
lag_type: link
@ -124,39 +129,39 @@ EXAMPLES = r'''
fibre_channel_interface_policy: whateverfcpolicy
state: present
- name: creating a Virtual Port Channel (VPC) Interface Policy Group (no description)
- name: Create a Virtual Port Channel (VPC) Interface Policy Group (no description)
aci_interface_policy_leaf_policy_group:
host: apic
username: yourusername
password: yourpassword
username: admin
password: SomeSecretPassword
policy_group: policygroupname
lag_type: node
link_level_policy: whateverlinklevelpolicy
fibre_channel_interface_policy: whateverfcpolicy
state: present
- name: creating a Leaf Access Port Policy Group (no description)
- name: Create a Leaf Access Port Policy Group (no description)
aci_interface_policy_leaf_policy_group:
host: apic
username: yourusername
password: yourpassword
username: admin
password: SomeSecretPassword
policy_group: policygroupname
lag_type: leaf
link_level_policy: whateverlinklevelpolicy
fibre_channel_interface_policy: whateverfcpolicy
state: present
- name: deleting an Interface policy Leaf Policy Group
- name: Delete an Interface policy Leaf Policy Group
aci_interface_policy_leaf_policy_group:
host: apic
username: yourusername
password: yourpassword
username: admin
password: SomeSecretPassword
policy_group: policygroupname
lag_type: type_name
state: absent
'''
RETURN = '''
RETURN = r'''
current:
description: The existing configuration from the APIC after the module has finished
returned: success
@ -268,11 +273,11 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update({
'policy_group': dict(type='str', aliases=['name', 'policy_group_name']),
'policy_group': dict(type='str', aliases=['name', 'policy_group_name']), # Not required for querying all objects
'description': dict(type='str', aliases=['descr']),
# NOTE: Since this module needs to include both infra:AccBndlGrp (for PC andVPC) and infra:AccPortGrp (for leaf access port policy group):
# NOTE: Since this module needs to include both infra:AccBndlGrp (for PC and VPC) and infra:AccPortGrp (for leaf access port policy group):
# NOTE: I'll allow the user to make the choice here (link(PC), node(VPC), leaf(leaf-access port policy group))
'lag_type': dict(type='str', aliases=['lag_type_name']),
'lag_type': dict(type='str', aliases=['lag_type_name'], choices=['leaf', 'link', 'node']), # Not required for querying all objects
'link_level_policy': dict(type='str', aliases=['link_level_policy_name']),
'cdp_policy': dict(type='str', aliases=['cdp_policy_name']),
'mcp_policy': dict(type='str', aliases=['mcp_policy_name']),
@ -289,16 +294,16 @@ def main():
'l2_interface_policy': dict(type='str', aliases=['l2_interface_policy_name']),
'port_security_policy': dict(type='str', aliases=['port_security_policy_name']),
'aep': dict(type='str', aliases=['aep_name']),
'state': dict(type='str', default='present', choices=['absent', 'present', 'query'])
'state': dict(type='str', default='present', choices=['absent', 'present', 'query']),
})
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
['state', 'absent', ['policy_group', 'lag_type']],
['state', 'present', ['policy_group', 'lag_type']]
]
['state', 'absent', ['lag_type', 'policy_group']],
['state', 'present', ['lag_type', 'policy_group']],
],
)
policy_group = module.params['policy_group']
@ -321,9 +326,6 @@ def main():
port_security_policy = module.params['port_security_policy']
aep = module.params['aep']
state = module.params['state']
aci_class_name = ''
dn_name = ''
class_config_dict = {}
if lag_type == 'leaf':
aci_class_name = 'infraAccPortGrp'
@ -331,7 +333,6 @@ def main():
class_config_dict = dict(
name=policy_group,
descr=description,
dn='uni/infra/funcprof/{0}-{1}'.format(dn_name, policy_group)
)
elif lag_type == 'link' or lag_type == 'node':
aci_class_name = 'infraAccBndlGrp'
@ -340,7 +341,6 @@ def main():
name=policy_group,
descr=description,
lagT=lag_type,
dn='uni/infra/funcprof/{0}-{1}'.format(dn_name, policy_group)
)
aci = ACIModule(module)
@ -349,144 +349,152 @@ def main():
aci_class=aci_class_name,
aci_rn='infra/funcprof/{0}-{1}'.format(dn_name, policy_group),
filter_target='eq({0}.name, "{1}")'.format(aci_class_name, policy_group),
module_object=policy_group
module_object=policy_group,
),
child_classes=[
'infraRsMonIfInfraPol', 'infraRsLldpIfPol', 'infraRsFcIfPol',
'infraRsLacpPol', 'infraRsL2PortSecurityPol', 'infraRsHIfPol',
'infraRsQosPfcIfPol', 'infraRsStpIfPol', 'infraRsQosIngressDppIfPol',
'infraRsStormctrlIfPol', 'infraRsQosEgressDppIfPol', 'infraRsQosSdIfPol',
'infraRsAttEntP', 'infraRsMcpIfPol', 'infraRsCdpIfPol', 'infraRsL2IfPol'
]
'infraRsAttEntP',
'infraRsCdpIfPol',
'infraRsFcIfPol',
'infraRsHIfPol',
'infraRsL2IfPol',
'infraRsL2PortSecurityPol',
'infraRsLacpPol',
'infraRsLldpIfPol',
'infraRsMcpIfPol',
'infraRsMonIfInfraPol',
'infraRsQosEgressDppIfPol',
'infraRsQosIngressDppIfPol',
'infraRsQosPfcIfPol',
'infraRsQosSdIfPol',
'infraRsStormctrlIfPol',
'infraRsStpIfPol',
],
)
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class=aci_class_name,
class_config=class_config_dict,
child_configs=[
dict(
infraRsMonIfInfraPol=dict(
infraRsAttEntP=dict(
attributes=dict(
tnMonInfraPolName=monitoring_policy
)
)
tDn='uni/infra/attentp-{0}'.format(aep),
),
),
),
dict(
infraRsLldpIfPol=dict(
infraRsCdpIfPol=dict(
attributes=dict(
tnLldpIfPolName=lldp_policy
)
)
tnCdpIfPolName=cdp_policy,
),
),
),
dict(
infraRsFcIfPol=dict(
attributes=dict(
tnFcIfPolName=fibre_channel_interface_policy
)
)
tnFcIfPolName=fibre_channel_interface_policy,
),
),
),
dict(
infraRsLacpPol=dict(
infraRsHIfPol=dict(
attributes=dict(
tnLacpLagPolName=port_channel_policy
)
)
tnFabricHIfPolName=link_level_policy,
),
),
),
dict(
infraRsL2PortSecurityPol=dict(
infraRsL2IfPol=dict(
attributes=dict(
tnL2PortSecurityPolName=port_security_policy
)
)
tnL2IfPolName=l2_interface_policy,
),
),
),
dict(
infraRsHIfPol=dict(
infraRsL2PortSecurityPol=dict(
attributes=dict(
tnFabricHIfPolName=link_level_policy
)
)
tnL2PortSecurityPolName=port_security_policy,
),
),
),
dict(
infraRsQosPfcIfPol=dict(
infraRsLacpPol=dict(
attributes=dict(
tnQosPfcIfPolName=priority_flow_control_policy
)
)
tnLacpLagPolName=port_channel_policy,
),
),
),
dict(
infraRsStpIfPol=dict(
infraRsLldpIfPol=dict(
attributes=dict(
tnStpIfPolName=stp_interface_policy
)
)
tnLldpIfPolName=lldp_policy,
),
),
),
dict(
infraRsQosIngressDppIfPol=dict(
infraRsMcpIfPol=dict(
attributes=dict(
tnQosDppPolName=ingress_data_plane_policing_policy
)
)
tnMcpIfPolName=mcp_policy,
),
),
),
dict(
infraRsStormctrlIfPol=dict(
infraRsMonIfInfraPol=dict(
attributes=dict(
tnStormctrlIfPolName=storm_control_interface_policy
)
)
tnMonInfraPolName=monitoring_policy,
),
),
),
dict(
infraRsQosEgressDppIfPol=dict(
attributes=dict(
tnQosDppPolName=egress_data_plane_policing_policy
)
)
tnQosDppPolName=egress_data_plane_policing_policy,
),
),
),
dict(
infraRsQosSdIfPol=dict(
infraRsQosIngressDppIfPol=dict(
attributes=dict(
tnQosSdIfPolName=slow_drain_policy
)
)
tnQosDppPolName=ingress_data_plane_policing_policy,
),
),
),
dict(
infraRsMcpIfPol=dict(
infraRsQosPfcIfPol=dict(
attributes=dict(
tnMcpIfPolName=mcp_policy
)
)
tnQosPfcIfPolName=priority_flow_control_policy,
),
),
),
dict(
infraRsCdpIfPol=dict(
infraRsQosSdIfPol=dict(
attributes=dict(
tnCdpIfPolName=cdp_policy
)
)
tnQosSdIfPolName=slow_drain_policy,
),
),
),
dict(
infraRsL2IfPol=dict(
infraRsStormctrlIfPol=dict(
attributes=dict(
tnL2IfPolName=l2_interface_policy
)
)
tnStormctrlIfPolName=storm_control_interface_policy,
),
),
),
dict(
infraRsAttEntP=dict(
infraRsStpIfPol=dict(
attributes=dict(
tDn='uni/infra/attentp-{0}'.format(aep)
)
)
)
tnStpIfPolName=stp_interface_policy,
),
),
),
],
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class=aci_class_name)
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,6 +17,7 @@ module: aci_interface_policy_leaf_profile
short_description: Manage Fabric interface policy leaf profiles on Cisco ACI fabrics (infra:AccPortP)
description:
- Manage Fabric interface policy leaf profiles on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(infra:AccPortP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -217,7 +218,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='infraAccPortP',
class_config=dict(
@ -226,10 +226,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='infraAccPortP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_interface_policy_lldp
short_description: Manage LLDP interface policies on Cisco ACI fabrics (lldp:IfPol)
description:
- Manage LLDP interface policies on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(lldp:IfPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -33,13 +34,13 @@ options:
aliases: [ descr ]
receive_state:
description:
- Enable or disable Receive state (FIXME!)
- Enable or disable Receive state.
required: yes
choices: [ disabled, enabled ]
default: enabled
transmit_state:
description:
- Enable or Disable Transmit state (FIXME!)
- Enable or Disable Transmit state.
required: false
choices: [ disabled, enabled ]
default: enabled
@ -176,7 +177,7 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
lldp_policy=dict(type='str', require=False, aliases=['name']),
lldp_policy=dict(type='str', require=False, aliases=['name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr']),
receive_state=dict(type='raw'), # Turn into a boolean in v2.9
transmit_state=dict(type='raw'), # Turn into a boolean in v2.9
@ -214,7 +215,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='lldpIfPol',
class_config=dict(
@ -225,10 +225,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='lldpIfPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_interface_policy_mcp
short_description: Manage MCP interface policies on Cisco ACI fabrics (mcp:IfPol)
description:
- Manage MCP interface policies on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(mcp:IfPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -204,7 +205,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='mcpIfPol',
class_config=dict(
@ -214,10 +214,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='mcpIfPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_interface_policy_port_channel
short_description: Manage port channel interface policies on Cisco ACI fabrics (lacp:LagPol)
description:
- Manage port channel interface policies on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(lacp:LagPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -288,7 +289,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='lacpLagPol',
class_config=dict(
@ -301,10 +301,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='lacpLagPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_interface_policy_port_security
short_description: Manage port security on Cisco ACI fabrics (l2:PortSecurityPol)
description:
- Manage port security on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(l2:PortSecurityPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -204,7 +205,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='l2PortSecurityPol',
class_config=dict(
@ -214,10 +214,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='l2PortSecurityPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,15 +17,13 @@ module: aci_interface_selector_to_switch_policy_leaf_profile
short_description: Associates an Interface Selector Profile to a Switch Policy Leaf Profile (infra:RsAccPortP)
description:
- Associates an Interface Profile (Selector) to a Switch Policy Leaf Profile on Cisco ACI fabrics.
notes:
- This module requires an existing leaf profile, the module M(aci_switch_policy_leaf_profile) can be used for this.
- More information from the internal APIC class I(infra:RsAccPortP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Bruno Calogero (@brunocalogero)
version_added: '2.5'
notes:
- This module can be used with M(aci_switch_policy_leaf_profile).
One first creates a leaf profile (infra:NodeP),
Finally, associates an interface profile using the provided interface selector profile (infra:RsAccPortP)
options:
leaf_profile:
description:
@ -48,8 +46,8 @@ EXAMPLES = r'''
- name: Associating an interface selector profile to a switch policy leaf profile
aci_interface_selector_to_switch_policy_leaf_profile:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
interface_selector: interface_profile_name
state: present
@ -57,8 +55,8 @@ EXAMPLES = r'''
- name: Remove an interface selector profile associated with a switch policy leaf profile
aci_interface_selector_to_switch_policy_leaf_profile:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
interface_selector: interface_profile_name
state: absent
@ -66,8 +64,8 @@ EXAMPLES = r'''
- name: Query an interface selector profile associated with a switch policy leaf profile
aci_interface_selector_to_switch_policy_leaf_profile:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
interface_selector: interface_profile_name
state: query
@ -185,8 +183,8 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
leaf_profile=dict(type='str', aliases=['leaf_profile_name']),
interface_selector=dict(type='str', aliases=['name', 'interface_selector_name', 'interface_profile_name']),
leaf_profile=dict(type='str', aliases=['leaf_profile_name']), # Not required for querying all objects
interface_selector=dict(type='str', aliases=['interface_profile_name', 'interface_selector_name', 'name']), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query'])
)
@ -226,16 +224,13 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='infraRsAccPortP',
class_config=dict(tDn=interface_selector_tDn)
class_config=dict(tDn=interface_selector_tDn),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='infraRsAccPortP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_l3out_route_tag_policy
short_description: Manage route tag policies on Cisco ACI fabrics (l3ext:RouteTagPol)
description:
- Manage route tag policies on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(l3ext:RouteTagPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
rtp:
description:
@ -219,7 +219,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='l3extRouteTagPol',
class_config=dict(
@ -228,10 +227,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='l3extRouteTagPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -1,6 +1,7 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
@ -16,6 +17,15 @@ module: aci_rest
short_description: Direct access to the Cisco APIC REST API
description:
- Enables the management of the Cisco ACI fabric through direct access to the Cisco APIC REST API.
- Thanks to the idempotent nature of the APIC, this module is idempotent and reports changes.
notes:
- Certain payloads are known not to be idempotent, so be careful when constructing payloads,
e.g. using C(status="created") will cause idempotency issues, use C(status="modified") instead.
More information in :ref:`the ACI documentation <aci_guide_known_issues>`.
- Certain payloads (and used paths) are known to report no changes happened when changes did happen.
This is a known APIC problem and has been reported to the vendor. A workaround for this issue exists.
More information in :ref:`the ACI documentation <aci_guide_known_issues>`.
- XML payloads require the C(lxml) and C(xmljson) python libraries. For JSON payloads nothing special is needed.
- More information regarding the Cisco APIC REST API is available from
U(http://www.cisco.com/c/en/us/td/docs/switches/datacenter/aci/apic/sw/2-x/rest_cfg/2_1_x/b_Cisco_APIC_REST_API_Configuration_Guide.html).
author:
@ -25,7 +35,6 @@ requirements:
- lxml (when using XML payload)
- xmljson >= 0.1.8 (when using XML payload)
- python 2.7+ (when using xmljson)
extends_documentation_fragment: aci
options:
method:
description:
@ -52,21 +61,14 @@ options:
- Name of the absolute path of the filname that includes the body
of the http request being sent to the ACI fabric.
aliases: [ config_file ]
notes:
- Certain payloads are known not to be idempotent, so be careful when constructing payloads,
e.g. using C(status="created") will cause idempotency issues, use C(status="modified") instead.
More information at U(https://github.com/ansible/community/wiki/Network:-ACI-Documentation#known-issues)
- Certain payloads (or used paths) are known to report no changes happened when changes did happen.
This is a known APIC problem and has been reported to the vendor.
More information at U(https://github.com/ansible/community/wiki/Network:-ACI-Documentation#known-issues)
- XML payloads require the C(lxml) and C(xmljson) python libraries. For JSON payloads nothing special is needed.
extends_documentation_fragment: aci
'''
EXAMPLES = r'''
- name: Add a tenant using certifcate authentication
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/admin.key
method: post
path: /api/mo/uni.xml
@ -75,8 +77,8 @@ EXAMPLES = r'''
- name: Add a tenant using inline YAML
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/admin.key
validate_certs: no
path: /api/mo/uni.json
@ -90,8 +92,8 @@ EXAMPLES = r'''
- name: Add a tenant using a JSON string
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/admin.key
validate_certs: no
path: /api/mo/uni.json
@ -109,8 +111,8 @@ EXAMPLES = r'''
- name: Add a tenant using an XML string
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/{{ aci_username}}.key
validate_certs: no
path: /api/mo/uni.xml
@ -120,17 +122,17 @@ EXAMPLES = r'''
- name: Get tenants using password authentication
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
method: get
path: /api/node/class/fvTenant.json
delegate_to: localhost
- name: Configure contracts
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/admin.key
method: post
path: /api/mo/uni.xml
@ -139,8 +141,8 @@ EXAMPLES = r'''
- name: Register leaves and spines
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/admin.key
validate_certs: no
method: post
@ -155,8 +157,8 @@ EXAMPLES = r'''
- name: Wait for all controllers to become ready
aci_rest:
host: '{{ inventory_hostname }}'
username: '{{ aci_username }}'
host: apic
username: admin
private_key: pki/admin.key
validate_certs: no
path: /api/node/class/topSystem.json?query-target-filter=eq(topSystem.role,"controller")
@ -285,7 +287,7 @@ class ACIRESTModule(ACIModule):
return False
def response_any(self, rawoutput, rest_type='xml'):
def response_type(self, rawoutput, rest_type='xml'):
''' Handle APIC response output '''
if rest_type == 'json':
@ -313,8 +315,8 @@ def main():
mutually_exclusive=[['content', 'src']],
)
path = module.params['path']
content = module.params['content']
path = module.params['path']
src = module.params['src']
# Report missing file
@ -380,39 +382,36 @@ def main():
if aci.params['private_key'] is not None:
aci.cert_auth(path=path, payload=payload)
aci.method = aci.params['method'].upper()
# Perform request
resp, info = fetch_url(module, aci.url,
data=payload,
headers=aci.headers,
method=aci.params['method'].upper(),
method=aci.method,
timeout=aci.params['timeout'],
use_proxy=aci.params['use_proxy'])
if aci.params['output_level'] == 'debug':
aci.result['filter_string'] = aci.filter_string
aci.result['method'] = aci.params['method'].upper()
# aci.result['path'] = aci.path # Adding 'path' in result causes state: absent in output
aci.result['response'] = info['msg']
aci.result['status'] = info['status']
aci.result['url'] = aci.url
aci.response = info['msg']
aci.status = info['status']
# Report failure
if info['status'] != 200:
try:
# APIC error
aci.response(info['body'], rest_type)
aci.fail_json(msg='Request failed: %(code)s %(text)s' % aci.error)
aci.response_type(info['body'], rest_type)
aci.fail_json(msg='APIC Error %(code)s: %(text)s' % aci.error)
except KeyError:
# Connection error
aci.fail_json(msg='Request connection failed for %(url)s. %(msg)s' % info)
aci.fail_json(msg='Connection failed for %(url)s. %(msg)s' % info)
aci.response_any(resp.read(), rest_type)
aci.response_type(resp.read(), rest_type)
aci.result['imdata'] = aci.imdata
aci.result['totalCount'] = aci.totalCount
# Report success
module.exit_json(**aci.result)
aci.exit_json(**aci.result)
if __name__ == '__main__':

@ -17,14 +17,14 @@ module: aci_static_binding_to_epg
short_description: Bind static paths to EPGs on Cisco ACI fabrics (fv:RsPathAtt)
description:
- Bind static paths to EPGs on Cisco ACI fabrics.
notes:
- The C(tenant), C(ap), C(epg) used must exist before using this module in your playbook.
The M(aci_tenant), M(aci_ap), M(aci_epg) modules can be used for this.
- More information from the internal APIC classes I(fv:RsPathAtt) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Bruno Calogero (@brunocalogero)
version_added: '2.5'
notes:
- The C(tenant), C(ap), C(epg) used must exist before using this module in your playbook.
The M(aci_tenant), M(aci_ap), M(aci_epg) modules can be used for this.
options:
tenant:
description:
@ -59,27 +59,31 @@ options:
interface_mode:
description:
- Determines how layer 2 tags will be read from and added to frames.
- Values C(802.1p) and C(native) are identical.
- Values C(access) and C(untagged) are identical.
- Values C(regular), C(tagged) and C(trunk) are identical.
- The APIC defaults the mode to C(trunk).
choices: [ access, trunk, 802.1p ]
choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ]
default: trunk
aliases: [ mode, interface_mode_name ]
aliases: [ interface_mode_name, mode ]
interface_type:
description:
- The type of interface for the static EPG deployement.
- The APIC defaults the C(interface_type) to C(switch_port).
choices: [ switch_port, vpc, port_channel, fex ]
choices: [ fex, port_channel, switch_port, vpc ]
default: switch_port
pod:
pod_id:
description:
- The pod number part of the tDn.
- C(pod) is usually an integer below 10.
aliases: [ pod_number ]
- C(pod_id) is usually an integer below 10.
aliases: [ pod, pod_number ]
leafs:
description:
- The switch ID(s) that the C(interface) belongs to.
- When C(interface_type) is C(switch_port), C(port_channel), or C(fex), then C(leafs) is a string of the leaf ID.
- When C(interface_type) is C(vpc), then C(leafs) is a list with both leaf IDs.
aliases: [ paths, leaves, nodes, switches ]
- The C(leafs) value is usually something like '101' or '101-102' depending on C(connection_type).
aliases: [ leaves, nodes, paths, switches ]
interface:
description:
- The C(interface) string value part of the tDn.
@ -109,16 +113,117 @@ EXAMPLES = r'''
epg: accessport_epg1
encap_id: 222
deploy_immediacy: lazy
interface_mode: access
interface_mode: untagged
interface_type: switch_port
pod: 1
pod_id: 1
leafs: 101
interface: '1/7'
state: present
'''
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: string
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
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: string
sample: ?rsp-prop-include=config-only
method:
description: The HTTP method used for the request to the APIC
returned: failure or debug
type: string
sample: POST
response:
description: The HTTP response from the APIC
returned: failure or debug
type: string
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: string
sample: https://10.11.12.13/api/mo/uni/tn-production.json
'''
from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
@ -130,21 +235,18 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
tenant=dict(type='str', aliases=['tenant_name']),
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']),
epg=dict(type='str', aliases=['epg_name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
ap=dict(type='str', aliases=['app_profile', 'app_profile_name']), # Not required for querying all objects
epg=dict(type='str', aliases=['epg_name']), # Not required for querying all objects
encap_id=dict(type='int', aliases=['vlan', 'vlan_id']),
primary_encap_id=dict(type='int', aliases=['primary_vlan', 'primary_vlan_id']),
deploy_immediacy=dict(type='str', choices=['immediate', 'lazy']),
interface_mode=dict(type='str', choices=['access', 'tagged', '802.1p'], aliases=['mode', 'interface_mode_name']),
interface_type=dict(type='str', choices=['switch_port', 'vpc', 'port_channel', 'fex'], required=True),
# NOTE: C(pod) is usually an integer below 10.
pod=dict(type='int', aliases=['pod_number']),
# NOTE: C(leafs) is usually something like '101' or '101-102' depending on C(connection_type).
leafs=dict(type='list', aliases=['paths', 'leaves', 'nodes', 'switches']),
# NOTE: C(interface) is usually a policy group like: "test-IntPolGrp" or an interface of the following format: "1/7" depending on C(interface_type).
interface_mode=dict(type='str', choices=['802.1p', 'access', 'native', 'regular', 'tagged', 'trunk', 'untagged'],
aliases=['interface_mode_name', 'mode']),
interface_type=dict(type='str', default='switch_port', choices=['fex', 'port_channel', 'switch_port', 'vpc']),
pod_id=dict(type='int', aliases=['pod', 'pod_number']), # Not required for querying all objects
leafs=dict(type='list', aliases=['leaves', 'nodes', 'paths', 'switches']),
interface=dict(type='str'),
# NOTE: C(extpaths) is only used if C(interface_type) is C(fex), it is usually something like '1011'(int)
extpaths=dict(type='int'),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -153,9 +255,9 @@ def main():
argument_spec=argument_spec,
supports_check_mode=True,
required_if=[
['state', 'absent', ['tenant', 'ap', 'epg', 'interface_type', 'pod', 'leafs', 'interface']],
['state', 'present', ['tenant', 'ap', 'epg', 'encap_id', 'interface_type', 'pod', 'leafs', 'interface']],
['interface_type', 'fex', ['extpaths']],
['state', 'absent', ['ap', 'epg', 'interface', 'leafs', 'pod_id', 'tenant']],
['state', 'present', ['ap', 'encap_id', 'epg', 'interface', 'leafs', 'pod_id', 'tenant']],
],
)
@ -167,7 +269,7 @@ def main():
deploy_immediacy = module.params['deploy_immediacy']
interface_mode = module.params['interface_mode']
interface_type = module.params['interface_type']
pod = module.params['pod']
pod_id = module.params['pod_id']
# Users are likely to use integers for leaf IDs, which would raise an exception when using the join method
leafs = [str(leaf) for leaf in module.params['leafs']]
if leafs is not None:
@ -201,18 +303,26 @@ def main():
else:
module.fail_json(msg='Valid VLAN assigments are from 1 to 4096')
INTERFACE_MODE_MAPPING = {
'802.1p': 'native',
'access': 'untagged',
'native': 'native',
'regular': 'regular',
'tagged': 'regular',
'trunk': 'regular',
'untagged': 'untagged',
}
INTERFACE_TYPE_MAPPING = dict(
# NOTE: C(interface) can be a policy group like: 'test-IntPolGrp' or of following format: '1/7', C(leafs) can only be something like '101'
switch_port='topology/pod-{0}/paths-{1}/pathep-[eth{2}]'.format(pod, leafs, interface),
# NOTE: C(interface) can be a policy group like: 'test-IntPolGrp' or of following format: '1/7', C(leafs) can only be something like '101'
port_channel='topology/pod-{0}/paths-{1}/pathep-[eth{2}]'.format(pod, leafs, interface),
# NOTE: C(interface) can be a policy group like: 'test-IntPolGrp', C(leafs) can be something like '101-102'
vpc='topology/pod-{0}/protpaths-{1}/pathep-[{2}]'.format(pod, leafs, interface),
# NOTE: C(interface) can be of the following format: '1/7', C(leafs) can only be like '101', C(extpaths) can only be like '1011'
fex='topology/pod-{0}/paths-{1}/extpaths-{2}/pathep-[eth{3}]'.format(pod, leafs, extpaths, interface),
fex='topology/pod-{0}/paths-{1}/extpaths-{2}/pathep-[eth{3}]'.format(pod_id, leafs, extpaths, interface),
port_channel='topology/pod-{0}/paths-{1}/pathep-[eth{2}]'.format(pod_id, leafs, interface),
switch_port='topology/pod-{0}/paths-{1}/pathep-[eth{2}]'.format(pod_id, leafs, interface),
vpc='topology/pod-{0}/protpaths-{1}/pathep-[{2}]'.format(pod_id, leafs, interface),
)
static_path = INTERFACE_TYPE_MAPPING[interface_type]
if interface_mode is not None:
interface_mode = INTERFACE_MODE_MAPPING[interface_mode]
aci = ACIModule(module)
aci.construct_url(
@ -245,7 +355,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvRsPathAtt',
class_config=dict(
@ -257,16 +366,14 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvRsPathAtt')
# 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)
aci.exit_json()
if __name__ == "__main__":

@ -17,14 +17,14 @@ module: aci_switch_leaf_selector
short_description: Add a leaf Selector with Node Block Range and Policy Group to a Switch Policy Leaf Profile on Cisco ACI fabrics
description:
- Add a leaf Selector with Node Block range and Policy Group to a Switch Policy Leaf Profile on Cisco ACI fabrics.
notes:
- This module is to be used with M(aci_switch_policy_leaf_profile)
One first creates a leaf profile (infra:NodeP) and then creates an associated selector (infra:LeafS),
- More information from the internal APIC class I(infra:LeafS), I(infra:NodeBlk), I(infra:RsAccNodePGrp) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Bruno Calogero (@brunocalogero)
version_added: '2.5'
notes:
- This module is to be used with M(aci_switch_policy_leaf_profile)
One first creates a leaf profile (infra:NodeP) and then creates an associated selector (infra:LeafS),
options:
description:
description:
@ -69,8 +69,8 @@ EXAMPLES = r'''
- name: adding a switch policy leaf profile selector associated Node Block range (w/ policy group)
aci_switch_leaf_selector:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
leaf: leaf_selector_name
leaf_node_blk: node_blk_name
@ -82,8 +82,8 @@ EXAMPLES = r'''
- name: adding a switch policy leaf profile selector associated Node Block range (w/o policy group)
aci_switch_leaf_selector:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
leaf: leaf_selector_name
leaf_node_blk: node_blk_name
@ -94,8 +94,8 @@ EXAMPLES = r'''
- name: Removing a switch policy leaf profile selector
aci_switch_leaf_selector:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
leaf: leaf_selector_name
state: absent
@ -103,8 +103,8 @@ EXAMPLES = r'''
- name: Querying a switch policy leaf profile selector
aci_switch_leaf_selector:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
leaf: leaf_selector_name
state: query
@ -223,8 +223,8 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update({
'description': dict(type='str'),
'leaf_profile': dict(type='str', aliases=['leaf_profile_name']),
'leaf': dict(type='str', aliases=['name', 'leaf_name', 'leaf_profile_leaf_name', 'leaf_selector_name']),
'leaf_profile': dict(type='str', aliases=['leaf_profile_name']), # Not required for querying all objects
'leaf': dict(type='str', aliases=['name', 'leaf_name', 'leaf_profile_leaf_name', 'leaf_selector_name']), # Not required for querying all objects
'leaf_node_blk': dict(type='str', aliases=['leaf_node_blk_name', 'node_blk_name']),
'leaf_node_blk_description': dict(type='str'),
'from': dict(type='int', aliases=['node_blk_range_from', 'from_range', 'range_from']),
@ -268,14 +268,13 @@ def main():
module_object=leaf,
),
# NOTE: infraNodeBlk is not made into a subclass because there is a 1-1 mapping between node block and leaf selector name
child_classes=['infraNodeBlk', 'infraRsAccNodePGrp']
child_classes=['infraNodeBlk', 'infraRsAccNodePGrp'],
)
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='infraLeafS',
class_config=dict(
@ -290,23 +289,21 @@ def main():
name=leaf_node_blk,
from_=from_,
to_=to_,
)
)
),
),
),
dict(
infraRsAccNodePGrp=dict(
attributes=dict(
tDn='uni/infra/funcprof/accnodepgrp-{0}'.format(policy_group),
)
)
),
),
),
],
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='infraLeafS')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,6 +17,7 @@ module: aci_switch_policy_leaf_profile
short_description: Create switch policy leaf profiles on Cisco ACI fabrics (infra:NodeP)
description:
- Create switch policy leaf profiles on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(infra:NodeP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -26,7 +27,7 @@ options:
leaf_profile:
description:
- The name of the Leaf Profile.
aliases: [ name, leaf_profile_name ]
aliases: [ leaf_profile_name, name ]
description:
description:
- Description for the Leaf Profile.
@ -44,8 +45,8 @@ EXAMPLES = r'''
- name: creating a Leaf Profile with description
aci_switch_policy_leaf_profile:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
description: sw_description
state: present
@ -53,16 +54,16 @@ EXAMPLES = r'''
- name: Deleting a Leaf Profile
aci_switch_policy_leaf_profile:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
state: absent
- name: Query a Leaf Profile
aci_switch_policy_leaf_profile:
host: apic
username: someusername
password: somepassword
username: admin
password: SomeSecretPassword
leaf_profile: sw_name
state: query
'''
@ -179,7 +180,7 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
leaf_profile=dict(type='str', aliases=['name', 'leaf_profile_name']),
leaf_profile=dict(type='str', aliases=['name', 'leaf_profile_name']), # Not required for querying all objects
description=dict(type='str', aliases=['descr']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -203,26 +204,23 @@ def main():
aci_class='infraNodeP',
aci_rn='infra/nprof-{0}'.format(leaf_profile),
filter_target='eq(infraNodeP.name, "{0}")'.format(leaf_profile),
module_object=leaf_profile
)
module_object=leaf_profile,
),
)
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='infraNodeP',
class_config=dict(
name=leaf_profile,
descr=description,
)
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='infraNodeP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,6 +17,7 @@ module: aci_switch_policy_vpc_protection_group
short_description: Create switch policy Explicit vPC Protection Group on Cisco ACI fabrics (fabric:ExplicitGEp, fabric:NodePEp).
description:
- Create switch policy Explicit vPC Protection Group on Cisco ACI fabrics.
notes:
- More information from the internal APIC class
I(fabric:ExplicitGEp) and I(fabric:NodePEp) at U(https://developer.cisco.com/site/aci/docs/apis/apic-mim-ref/).
author:
@ -37,7 +38,6 @@ options:
description:
- The vPC domain policy to be associated with the Explicit vPC Protection Group.
aliases: [ vpc_domain_policy_name ]
required: no
switch_1_id:
description:
- The ID of the first Leaf Switch for the Explicit vPC Protection Group.
@ -58,9 +58,9 @@ extends_documentation_fragment: aci
EXAMPLES = r'''
- name: Add Explicit vPC Protection Group
aci_switch_policy_vpc_protection_group:
host: "{{ aci_hostname }}"
username: "{{ aci_username }}"
password: "{{ aci_password }}"
host: apic
username: admin
password: SomeSecretPassword
protection_group: protectiongroupname
protection_group_id: 6
vpc_domain_policy: vpcdomainpolicyname
@ -70,15 +70,116 @@ EXAMPLES = r'''
- name: Remove Explicit vPC Protection Group
aci_switch_policy_vpc_protection_group:
host: "{{ aci_hostname }}"
username: "{{ aci_username }}"
password: "{{ aci_password }}"
host: apic
username: admin
password: SomeSecretPassword
protection_group: protectiongroupname
state: absent
'''
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: string
sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
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: string
sample: ?rsp-prop-include=config-only
method:
description: The HTTP method used for the request to the APIC
returned: failure or debug
type: string
sample: POST
response:
description: The HTTP response from the APIC
returned: failure or debug
type: string
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: string
sample: https://10.11.12.13/api/mo/uni/tn-production.json
'''
from ansible.module_utils.network.aci.aci import ACIModule, aci_argument_spec
@ -88,7 +189,7 @@ from ansible.module_utils.basic import AnsibleModule
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
protection_group=dict(type='str', aliases=['name', 'protection_group_name']),
protection_group=dict(type='str', aliases=['name', 'protection_group_name']), # Not required for querying all objects
protection_group_id=dict(type='int', aliases=['id']),
vpc_domain_policy=dict(type='str', aliases=['vpc_domain_policy_name']),
switch_1_id=dict(type='int'),
@ -118,19 +219,17 @@ def main():
aci_class='fabricExplicitGEp',
aci_rn='fabric/protpol/expgep-{0}'.format(protection_group),
filter_target='eq(fabricExplicitGEp.name, "{0}")'.format(protection_group),
module_object=protection_group
module_object=protection_group,
),
child_classes=['fabricNodePEp', 'fabricNodePEp', 'fabricRsVpcInstPol']
child_classes=['fabricNodePEp', 'fabricNodePEp', 'fabricRsVpcInstPol'],
)
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fabricExplicitGEp',
class_config=dict(
dn='uni/fabric/protpol/expgep-{0}'.format(protection_group),
name=protection_group,
id=protection_group_id,
rn='expgep-{0}'.format(protection_group),
@ -139,35 +238,31 @@ def main():
dict(
fabricNodePEp=dict(
attributes=dict(
dn='uni/fabric/protpol/expgep-{0}/nodepep-{1}'.format(protection_group, switch_1_id),
id='{0}'.format(switch_1_id),
rn='nodepep-{0}'.format(switch_1_id),
)
)
),
),
),
dict(
fabricNodePEp=dict(
attributes=dict(
dn='uni/fabric/protpol/expgep-{0}/nodepep-{1}'.format(protection_group, switch_2_id),
id='{0}'.format(switch_2_id),
rn='nodepep-{0}'.format(switch_2_id),
)
)
),
),
),
dict(
fabricRsVpcInstPol=dict(
attributes=dict(
tnVpcInstPolName=vpc_domain_policy,
)
)
),
),
),
]
],
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fabricExplicitGEp')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,14 +17,14 @@ module: aci_taboo_contract
short_description: Manage taboo contracts on Cisco ACI fabrics (vz:BrCP)
description:
- Manage taboo contracts on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(vz:BrCP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
taboo_contract:
description:
@ -58,34 +58,34 @@ extends_documentation_fragment: aci
EXAMPLES = r'''
- name: Add taboo contract
aci_taboo_contract:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
tenant: ansible_test
taboo_contract: taboo_contract_test
state: present
- name: Remove taboo contract
aci_taboo_contract:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
tenant: ansible_test
taboo_contract: taboo_contract_test
state: absent
- name: Query all taboo contracts
aci_taboo_contract:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
state: query
- name: Query a specific taboo contract
aci_taboo_contract:
host: '{{ aci_hostname }}'
username: '{{ aci_username }}'
password: '{{ aci_password }}'
host: apic
username: admin
password: SomeSecretPassword
tenant: ansible_test
taboo_contract: taboo_contract_test
state: query
@ -246,7 +246,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='vzTaboo',
class_config=dict(
@ -256,10 +255,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='vzTaboo')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,6 +16,7 @@ module: aci_tenant
short_description: Manage tenants on Cisco ACI fabrics (fv:Tenant)
description:
- Manage tenants on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(fv:Tenant) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -202,9 +203,9 @@ def main():
],
)
tenant = module.params['tenant']
description = module.params['description']
state = module.params['state']
tenant = module.params['tenant']
aci = ACIModule(module)
aci.construct_url(
@ -218,7 +219,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvTenant',
class_config=dict(
@ -227,10 +227,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvTenant')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_tenant_action_rule_profile
short_description: Manage action rule profiles on Cisco ACI fabrics (rtctrl:AttrP)
description:
- Manage action rule profiles on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(rtctrl:AttrP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
action_rule:
description:
@ -49,9 +49,9 @@ extends_documentation_fragment: aci
# FIXME: Add more, better examples
EXAMPLES = r'''
- aci_tenant_action_rule_profile:
host: '{{ inventory_hostname }}'
username: '{{ username }}'
password: '{{ password }}'
host: apic
username: admin
password: SomeSecretPassword
action_rule: '{{ action_rule }}'
description: '{{ descr }}'
tenant: '{{ tenant }}'
@ -210,20 +210,16 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='rtctrlAttrP',
class_config=dict(
name=action_rule,
descr=description,
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='rtctrlAttrP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_tenant_ep_retention_policy
short_description: Manage End Point (EP) retention protocol policies on Cisco ACI fabrics (fv:EpRetPol)
description:
- Manage End Point (EP) retention protocol policies on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(fv:EpRetPol) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Swetha Chunduri (@schunduri)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
tenant:
description:
@ -231,7 +231,7 @@ BOUNCE_TRIG_MAPPING = dict(coop='protocol', rarp='rarp-flood')
def main():
argument_spec = aci_argument_spec()
argument_spec.update(
tenant=dict(type='str', aliases=['tenant_name']), # not required for querying all EPRs
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
epr_policy=dict(type='str', aliases=['epr_name', 'name']),
bounce_age=dict(type='int'),
bounce_trigger=dict(type='str', choices=['coop', 'flood']),
@ -304,7 +304,6 @@ def main():
aci.get_existing()
if state == 'present':
# filter out module parameters with null values
aci.payload(
aci_class='fvEpRetPol',
class_config=dict(
@ -319,10 +318,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvEpRetPol')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_tenant_span_dst_group
short_description: Manage SPAN destination groups on Cisco ACI fabrics (span:DestGrp)
description:
- Manage SPAN destination groups on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(span:DestGrp) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Dag Wieers (@dagwieers)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
dst_group:
description:
@ -51,9 +51,9 @@ extends_documentation_fragment: aci
# FIXME: Add more, better examples
EXAMPLES = r'''
- aci_tenant_span_dst_group:
host: '{{ inventory_hostname }}'
username: '{{ username }}'
password: '{{ password }}'
host: apic
username: admin
password: SomeSecretPassword
dst_group: '{{ dst_group }}'
description: '{{ descr }}'
tenant: '{{ tenant }}'
@ -212,7 +212,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='spanDestGrp',
class_config=dict(
@ -221,10 +220,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='spanDestGrp')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_tenant_span_src_group
short_description: Manage SPAN source groups on Cisco ACI fabrics (span:SrcGrp)
description:
- Manage SPAN source groups on Cisco ACI fabrics.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(span:SrcGrp) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
admin_state:
description:
@ -56,14 +56,14 @@ extends_documentation_fragment: aci
EXAMPLES = r'''
- aci_tenant_span_src_group:
host:"{{ inventory_hostname }}"
username:"{{ username }}"
password:"{{ password }}"
tenant:"{{ tenant }}"
src_group:"{{ src_group }}"
dst_group:"{{ dst_group }}"
admin_state:"{{ admin_state }}"
description:"{{ description }}"
host: apic
username: admin
password: SomeSecretPassword
tenant: production
src_group: "{{ src_group }}"
dst_group: "{{ dst_group }}"
admin_state: "{{ admin_state }}"
description: "{{ description }}"
'''
RETURN = r'''
@ -225,7 +225,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='spanSrcGrp',
class_config=dict(
@ -236,10 +235,8 @@ def main():
child_configs=[{'spanSpanLbl': {'attributes': {'name': dst_group}}}],
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='spanSrcGrp')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -16,14 +16,14 @@ module: aci_tenant_span_src_group_to_dst_group
short_description: Manage SPAN source group to destination group bindings on Cisco ACI fabrics (span:SpanLbl)
description:
- Manage SPAN source groups' associated destinaton group on Cisco ACI fabrics.
notes:
- The C(tenant), C(src_group), and C(dst_group) must exist before using this module in your playbook.
The M(aci_tenant), M(aci_tenant_span_src_group), and M(aci_tenant_span_dst_group) modules can be used for this.
- More information from the internal APIC class I(span:SrcGrp) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(tenant), C(src_group), and C(dst_group) must exist before using this module in your playbook.
The M(aci_tenant), M(aci_tenant_span_src_group), and M(aci_tenant_span_dst_group) modules can be used for this.
options:
description:
description:
@ -50,13 +50,13 @@ extends_documentation_fragment: aci
EXAMPLES = r'''
- aci_tenant_span_src_group_to_dst_group:
host:"{{ inventory_hostname }}"
username:"{{ username }}"
password:"{{ password }}"
tenant:"{{ tenant }}"
src_group:"{{ src_group }}"
dst_group:"{{ dst_group }}"
description:"{{ description }}"
host: apic
username: admin
password: SomeSecretPassword
tenant: production
src_group: "{{ src_group }}"
dst_group: "{{ dst_group }}"
description: "{{ description }}"
'''
RETURN = r'''
@ -172,10 +172,10 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update(
description=dict(type='str', aliases=['descr']),
dst_group=dict(type='str'),
src_group=dict(type='str'),
dst_group=dict(type='str'), # Not required for querying all objects
src_group=dict(type='str'), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
tenant=dict(type='str', aliases=['tenant_name']),
tenant=dict(type='str', aliases=['tenant_name']), # Not required for querying all objects
method=dict(type='str', choices=['delete', 'get', 'post'], aliases=['action'], removed_in_version='2.6'), # Deprecated starting from v2.6
protocol=dict(type='str', removed_in_version='2.6'), # Deprecated in v2.6
)
@ -220,7 +220,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='spanSpanLbl',
class_config=dict(
@ -229,10 +228,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='spanSpanLbl')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -18,6 +18,7 @@ module: aci_vlan_pool
short_description: Manage VLAN pools on Cisco ACI fabrics (fvns:VlanInstP)
description:
- Manage VLAN pools on Cisco ACI fabrics.
notes:
- More information from the internal APIC class I(fvns:VlanInstP) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
@ -197,7 +198,7 @@ def main():
argument_spec = aci_argument_spec()
argument_spec.update(
description=dict(type='str', aliases=['descr']),
pool=dict(type='str', aliases=['name', 'pool_name']),
pool=dict(type='str', aliases=['name', 'pool_name']), # Not required for querying all objects
pool_allocation_mode=dict(type='str', aliases=['allocation_mode', 'mode'], choices=['dynamic', 'static']),
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -238,20 +239,17 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvnsVlanInstP',
class_config=dict(
allocMode=pool_allocation_mode,
descr=description,
name=pool,
)
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvnsVlanInstP')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -18,14 +18,14 @@ module: aci_vlan_pool_encap_block
short_description: Manage encap blocks assigned to VLAN pools on Cisco ACI fabrics (fvns:EncapBlk)
description:
- Manage VLAN encap blocks that are assigned to VLAN pools on Cisco ACI fabrics.
notes:
- The C(pool) must exist in order to add or delete a encap block.
- More information from the internal APIC class I(fvns:EncapBlk) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
- Dag Wieers (@dagwieers)
version_added: '2.5'
requirements:
- The C(pool) must exist in order to add or delete a encap block.
options:
allocation_mode:
description:
@ -227,11 +227,11 @@ def main():
argument_spec.update(
allocation_mode=dict(type='str', aliases=['mode'], choices=['dynamic', 'inherit', 'static']),
description=dict(type='str', aliases=['descr']),
pool=dict(type='str', aliases=['pool_name']),
pool=dict(type='str', aliases=['pool_name']), # Not required for querying all objects
pool_allocation_mode=dict(type='str', aliases=['pool_mode'], choices=['dynamic', 'static']),
block_name=dict(type='str', aliases=['name']),
block_end=dict(type='int', aliases=['end']),
block_start=dict(type='int', aliases=["start"]),
block_name=dict(type='str', aliases=['name']), # Not required for querying all objects
block_end=dict(type='int', aliases=['end']), # Not required for querying all objects
block_start=dict(type='int', aliases=["start"]), # Not required for querying all objects
state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
)
@ -327,7 +327,6 @@ def main():
aci.get_existing()
if state == 'present':
# Filter out module parameters with null values
aci.payload(
aci_class='fvnsEncapBlk',
class_config={
@ -336,13 +335,11 @@ def main():
"from": encap_start,
"name": block_name,
"to": encap_end,
}
},
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvnsEncapBlk')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -17,14 +17,14 @@ short_description: Manage VRF (private networks aka. contexts) on Cisco ACI fabr
description:
- Manage VRF (private networks aka. contexts) on Cisco ACI fabrics.
- Each context is a private network associated to a tenant, i.e. VRF.
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
- More information from the internal APIC class I(fv:Ctx) at
U(https://developer.cisco.com/docs/apic-mim-ref/).
author:
- Jacob McGill (@jmcgill298)
version_added: '2.4'
notes:
- The C(tenant) used must exist before using this module in your playbook.
The M(aci_tenant) module can be used for this.
options:
tenant:
description:
@ -247,10 +247,10 @@ def main():
module_object=vrf,
),
)
aci.get_existing()
if state == 'present':
# Filter out module params with null values
aci.payload(
aci_class='fvCtx',
class_config=dict(
@ -261,10 +261,8 @@ def main():
),
)
# Generate config diff which will be used as POST request body
aci.get_diff(aci_class='fvCtx')
# Submit changes if module not in check_mode and the proposed is different than existing
aci.post_config()
elif state == 'absent':

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Bruno Calogero <bcalogero@cisco.com>
# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -65,6 +65,15 @@
aci_aep_to_domain: *binding_present
register: nm_add_binding
- name: Verify add_binding
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- 'cm_add_binding.sent == nm_add_binding.sent == {"infraRsDomP": {"attributes": {"tDn": "uni/phys-phys_dom"}}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"infraRsDomP": {"attributes": {"tDn": "uni/phys-phys_dom"}}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"infraRsDomP": {"attributes": {"dn": "uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]", "tDn": "uni/phys-phys_dom"}}}]'
- name: Add AEP to domain binding again (check_mode)
aci_aep_to_domain: *binding_present
check_mode: yes
@ -74,15 +83,10 @@
aci_aep_to_domain: *binding_present
register: nm_add_binding_again
- name: Verify add_binding
- name: Verify add_binding_again
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- cm_add_binding_again.changed == nm_add_binding_again.changed == false
- 'cm_add_binding.sent == nm_add_binding.sent == {"infraRsDomP": {"attributes": {"tDn": "uni/phys-phys_dom"}}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"infraRsDomP": {"attributes": {"tDn": "uni/phys-phys_dom"}}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"infraRsDomP": {"attributes": {"dn": "uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]", "tDn": "uni/phys-phys_dom"}}}]'
# QUERY ALL BINDINGS
@ -148,6 +152,13 @@
aci_aep_to_domain: *binding_absent
register: nm_remove_binding
- name: Verify remove_binding
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"infraRsDomP": {"attributes": {"dn": "uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]", "tDn": "uni/phys-phys_dom"}}}]'
- nm_remove_binding.current == []
- name: Remove AEP to domain binding again (check_mode)
aci_aep_to_domain: *binding_absent
check_mode: yes
@ -157,13 +168,10 @@
aci_aep_to_domain: *binding_absent
register: nm_remove_binding_again
- name: Verify remove_binding
- name: Verify remove_binding_again
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- cm_remove_binding_again.changed == nm_remove_binding_again.changed == false
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"infraRsDomP": {"attributes": {"dn": "uni/infra/attentp-test_aep/rsdomP-[uni/phys-phys_dom]", "tDn": "uni/phys-phys_dom"}}}]'
- nm_remove_binding.current == []
# QUERY NON-EXISTING BINDING

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -39,6 +39,15 @@
aci_domain: *domain_present
register: nm_add_domain
- name: Verify add_domain
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- 'cm_add_domain.sent == nm_add_domain.sent == {"fcDomP": {"attributes": {"name": "fc_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"fcDomP": {"attributes": {"name": "fc_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"fcDomP": {"attributes": {"dn": "uni/fc-fc_dom", "name": "fc_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- name: Add FC domain again (check_mode)
aci_domain: *domain_present
check_mode: yes
@ -48,15 +57,10 @@
aci_domain: *domain_present
register: nm_add_domain_again
- name: Verify add_domain
- name: Verify add_domain_again
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- cm_add_domain_again.changed == nm_add_domain_again.changed == false
- 'cm_add_domain.sent == nm_add_domain.sent == {"fcDomP": {"attributes": {"name": "fc_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"fcDomP": {"attributes": {"name": "fc_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"fcDomP": {"attributes": {"dn": "uni/fc-fc_dom", "name": "fc_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
# QUERY ALL DOMAINS
@ -82,8 +86,8 @@
assert:
that:
- cm_query_all_domains.changed == nm_query_all_domains.changed == false
# NOTE: Order of domains is not stable between calls
#- cm_query_all_domains == nm_query_all_domains
- cm_query_all_domains == nm_query_all_domains
- nm_query_all_domains.current|length >= 1
# QUERY A DOMAIN
@ -119,6 +123,13 @@
aci_domain: *domain_absent
register: nm_remove_domain
- name: Verify remove_domain
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"fcDomP": {"attributes": {"dn": "uni/fc-fc_dom", "name": "fc_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
- name: Remove FC domain again (check_mode)
aci_domain: *domain_absent
check_mode: yes
@ -128,23 +139,24 @@
aci_domain: *domain_absent
register: nm_remove_domain_again
- name: Verify remove_domain
- name: Verify remove_domain_again
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- cm_remove_domain_again.changed == nm_remove_domain_again.changed == false
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"fcDomP": {"attributes": {"dn": "uni/fc-fc_dom", "name": "fc_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
# QUERY NON-EXISTING DOMAIN
- name: Query non-existing FC domain (check_mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: fc_dom
check_mode: yes
register: cm_query_non_domain
- name: Query non-existing FC domain (normal mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: fc_dom
register: nm_query_non_domain
- name: Verify query_non_domain

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -39,6 +39,15 @@
aci_domain: *domain_present
register: nm_add_domain
- name: Verify add_domain
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- 'cm_add_domain.sent == nm_add_domain.sent == {"l2extDomP": {"attributes": {"name": "l2_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"l2extDomP": {"attributes": {"name": "l2_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"l2extDomP": {"attributes": {"dn": "uni/l2dom-l2_dom", "name": "l2_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- name: Add L2 domain again (check_mode)
aci_domain: *domain_present
check_mode: yes
@ -48,15 +57,10 @@
aci_domain: *domain_present
register: nm_add_domain_again
- name: Verify add_domain
- name: Verify add_domain_again
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- cm_add_domain_again.changed == nm_add_domain_again.changed == false
- 'cm_add_domain.sent == nm_add_domain.sent == {"l2extDomP": {"attributes": {"name": "l2_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"l2extDomP": {"attributes": {"name": "l2_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"l2extDomP": {"attributes": {"dn": "uni/l2dom-l2_dom", "name": "l2_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
# QUERY ALL DOMAINS
@ -82,8 +86,8 @@
assert:
that:
- cm_query_all_domains.changed == nm_query_all_domains.changed == false
# NOTE: Order of domains is not stable between calls
#- cm_query_all_domains == nm_query_all_domains
- cm_query_all_domains == nm_query_all_domains
- nm_query_all_domains.current|length >= 1
# QUERY A DOMAIN
@ -119,6 +123,13 @@
aci_domain: *domain_absent
register: nm_remove_domain
- name: Verify remove_domain
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"l2extDomP": {"attributes": {"dn": "uni/l2dom-l2_dom", "name": "l2_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
- name: Remove L2 domain again (check_mode)
aci_domain: *domain_absent
check_mode: yes
@ -128,23 +139,24 @@
aci_domain: *domain_absent
register: nm_remove_domain_again
- name: Verify remove_domain
- name: Verify remove_domain_again
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- cm_remove_domain_again.changed == nm_remove_domain_again.changed == false
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"l2extDomP": {"attributes": {"dn": "uni/l2dom-l2_dom", "name": "l2_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
# QUERY NON-EXISTING DOMAIN
- name: Query non-existing L2 domain (check_mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: l2_dom
check_mode: yes
register: cm_query_non_domain
- name: Query non-existing L2 domain (normal mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: l2_dom
register: nm_query_non_domain
- name: Verify query_non_domain

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -39,6 +39,15 @@
aci_domain: *domain_present
register: nm_add_domain
- name: Verify add_domain
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- 'cm_add_domain.sent == nm_add_domain.sent == {"l3extDomP": {"attributes": {"name": "l3_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"l3extDomP": {"attributes": {"name": "l3_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"l3extDomP": {"attributes": {"dn": "uni/l3dom-l3_dom", "name": "l3_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- name: Add L3 domain again (check_mode)
aci_domain: *domain_present
check_mode: yes
@ -48,15 +57,10 @@
aci_domain: *domain_present
register: nm_add_domain_again
- name: Verify add_domain
- name: Verify add_domain_again
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- cm_add_domain_again.changed == nm_add_domain_again.changed == false
- 'cm_add_domain.sent == nm_add_domain.sent == {"l3extDomP": {"attributes": {"name": "l3_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"l3extDomP": {"attributes": {"name": "l3_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"l3extDomP": {"attributes": {"dn": "uni/l3dom-l3_dom", "name": "l3_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
# QUERY ALL DOMAINS
@ -82,8 +86,8 @@
assert:
that:
- cm_query_all_domains.changed == nm_query_all_domains.changed == false
# NOTE: Order of domains is not stable between calls
#- cm_query_all_domains == nm_query_all_domains
- cm_query_all_domains == nm_query_all_domains
- nm_query_all_domains.current|length >= 1
# QUERY A DOMAIN
@ -119,6 +123,13 @@
aci_domain: *domain_absent
register: nm_remove_domain
- name: Verify remove_domain
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"l3extDomP": {"attributes": {"dn": "uni/l3dom-l3_dom", "name": "l3_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
- name: Remove L3 domain again (check_mode)
aci_domain: *domain_absent
check_mode: yes
@ -128,23 +139,24 @@
aci_domain: *domain_absent
register: nm_remove_domain_again
- name: Verify remove_domain
- name: Verify remove_domain_again
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- cm_remove_domain_again.changed == nm_remove_domain_again.changed == false
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"l3extDomP": {"attributes": {"dn": "uni/l3dom-l3_dom", "name": "l3_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
# QUERY NON-EXISTING DOMAIN
- name: Query non-existing L3 domain (check_mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: l3_dom
check_mode: yes
register: cm_query_non_domain
- name: Query non-existing L3 domain (normal mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: l3_dom
register: nm_query_non_domain
- name: Verify query_non_domain

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -39,6 +39,15 @@
aci_domain: *domain_present
register: nm_add_domain
- name: Verify add_domain
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- 'cm_add_domain.sent == nm_add_domain.sent == {"physDomP": {"attributes": {"name": "phys_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"physDomP": {"attributes": {"name": "phys_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- name: Add physical domain again (check_mode)
aci_domain: *domain_present
check_mode: yes
@ -48,15 +57,10 @@
aci_domain: *domain_present
register: nm_add_domain_again
- name: Verify add_domain
- name: Verify add_domain_again
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- cm_add_domain_again.changed == nm_add_domain_again.changed == false
- 'cm_add_domain.sent == nm_add_domain.sent == {"physDomP": {"attributes": {"name": "phys_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"physDomP": {"attributes": {"name": "phys_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- 'nm_add_domain.current == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
# QUERY ALL DOMAINS
@ -82,8 +86,8 @@
assert:
that:
- cm_query_all_domains.changed == nm_query_all_domains.changed == false
# NOTE: Order of domains is not stable between calls
#- cm_query_all_domains == nm_query_all_domains
- cm_query_all_domains == nm_query_all_domains
- nm_query_all_domains.current|length >= 1
# QUERY A DOMAIN
@ -119,6 +123,13 @@
aci_domain: *domain_absent
register: nm_remove_domain
- name: Verify remove_domain
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
- name: Remove physical domain again (check_mode)
aci_domain: *domain_absent
check_mode: yes
@ -128,23 +139,24 @@
aci_domain: *domain_absent
register: nm_remove_domain_again
- name: Verify remove_domain
- name: Verify remove_domain_again
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- cm_remove_domain_again.changed == nm_remove_domain_again.changed == false
- 'cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}}}]'
- nm_remove_domain.current == []
# QUERY NON-EXISTING DOMAIN
- name: Query non-existing physical domain (check_mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: phys_dom
check_mode: yes
register: cm_query_non_domain
- name: Query non-existing physical domain (normal mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: phys_dom
register: nm_query_non_domain
- name: Verify query_non_domain

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -41,6 +41,16 @@
aci_domain: *domain_present
register: nm_add_domain
- name: Verify add_domain
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- 'cm_add_domain.sent == nm_add_domain.sent == {"vmmDomP": {"attributes": {"name": "vmm_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"vmmDomP": {"attributes": {"name": "vmm_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- nm_add_domain.current.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom'
- nm_add_domain.current.0.vmmDomP.attributes.name == 'vmm_dom'
- name: Add VMM domain again (check_mode)
aci_domain: *domain_present
check_mode: yes
@ -50,16 +60,10 @@
aci_domain: *domain_present
register: nm_add_domain_again
- name: Verify add_domain
- name: Verify add_domain_again
assert:
that:
- cm_add_domain.changed == nm_add_domain.changed == true
- cm_add_domain_again.changed == nm_add_domain_again.changed == false
- 'cm_add_domain.sent == nm_add_domain.sent == {"vmmDomP": {"attributes": {"name": "vmm_dom"}}}'
- 'cm_add_domain.proposed == nm_add_domain.proposed == {"vmmDomP": {"attributes": {"name": "vmm_dom"}}}'
- cm_add_domain.current == cm_add_domain.previous == nm_add_domain.previous == []
- nm_add_domain.current.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom'
- nm_add_domain.current.0.vmmDomP.attributes.name == 'vmm_dom'
# QUERY ALL DOMAINS
@ -86,8 +90,8 @@
assert:
that:
- cm_query_all_domains.changed == nm_query_all_domains.changed == false
# NOTE: Order of domains is not stable between calls
#- cm_query_all_domains == nm_query_all_domains
- cm_query_all_domains == nm_query_all_domains
- nm_query_all_domains.current|length >= 1
# QUERY A DOMAIN
@ -125,6 +129,15 @@
aci_domain: *domain_absent
register: nm_remove_domain
- name: Verify remove_domain
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous
- nm_remove_domain.previous.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom'
- nm_remove_domain.previous.0.vmmDomP.attributes.name == 'vmm_dom'
- nm_remove_domain.current == []
- name: Remove VMM domain again (check_mode)
aci_domain: *domain_absent
check_mode: yes
@ -134,25 +147,26 @@
aci_domain: *domain_absent
register: nm_remove_domain_again
- name: Verify remove_domain
- name: Verify remove_domain_again
assert:
that:
- cm_remove_domain.changed == nm_remove_domain.changed == true
- cm_remove_domain_again.changed == nm_remove_domain_again.changed == false
- cm_remove_domain.current == cm_remove_domain.previous == nm_remove_domain.previous
- nm_remove_domain.previous.0.vmmDomP.attributes.dn == 'uni/vmmp-VMware/dom-vmm_dom'
- nm_remove_domain.previous.0.vmmDomP.attributes.name == 'vmm_dom'
- nm_remove_domain.current == []
# QUERY NON-EXISTING DOMAIN
- name: Query non-existing VMM domain (check_mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: vmm_dom
vm_provider: vmware
check_mode: yes
register: cm_query_non_domain
- name: Query non-existing VMM domain (normal mode)
aci_domain: *domain_query
aci_domain:
<<: *domain_query
domain: vmm_dom
vm_provider: vmware
register: nm_query_non_domain
- name: Verify query_non_domain

@ -1,18 +0,0 @@
# Test code for the ACI modules
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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: vlan.yml
when: vlan is not defined or vlan
- include_tasks: vsan.yml
when: vsan is not defined or vsan
- include_tasks: vxlan.yml
when: vxlan is not defined or vxlan

@ -1,215 +0,0 @@
# Test code for the ACI modules
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# CLEAN ENVIRONMENT
- name: Remove domain to VLAN pool binding
aci_domain_to_encap_pool: &binding_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: info
domain: phys_dom
domain_type: phys
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
state: absent
- name: Remove physical domain
aci_domain:
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) }}'
domain: phys_dom
domain_type: phys
state: absent
- name: Create VLAN pool
aci_encap_pool:
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) }}'
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
description: Test VLAN pool
state: present
# ADD BINDING
- name: Add domain to VLAN pool binding (check_mode)
aci_domain_to_encap_pool: &binding_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: info
domain: phys_dom
domain_type: phys
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
state: present
check_mode: yes
register: cm_add_binding
- name: Add domain to VLAN pool binding (normal mode)
aci_domain_to_encap_pool: *binding_present
register: nm_add_binding
- name: Verify add_binding
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- 'cm_add_binding.sent == nm_add_binding.sent == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}]'
- name: Add domain to VLAN pool binding again (check_mode)
aci_domain_to_encap_pool: *binding_present
check_mode: yes
register: cm_add_binding_again
- name: Add domain to VLAN pool binding again (normal mode)
aci_domain_to_encap_pool: *binding_present
register: nm_add_binding_again
- name: Verify add_binding_again
assert:
that:
- cm_add_binding_again.changed == nm_add_binding_again.changed == false
# QUERY ALL BINDINGS
- name: Query all domain to VLAN pool bindings (check_mode)
aci_domain_to_encap_pool: &binding_query
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: info
domain_type: phys
pool_type: vlan
pool_allocation_mode: dynamic
state: query
check_mode: yes
register: cm_query_all_bindings
- name: Query all domain to VLAN pool bindings (normal mode)
aci_domain_to_encap_pool: *binding_query
register: nm_query_all_bindings
- name: Verify query_all_bindings
assert:
that:
- cm_query_all_bindings.changed == nm_query_all_bindings.changed == false
- cm_query_all_bindings == nm_query_all_bindings
- nm_query_all_bindings.current|length >= 1
# QUERY A BINDING
- name: Query our domain to VLAN pool binding (check_mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
check_mode: yes
register: cm_query_binding
- name: Query our domain to VLAN pool binding (normal mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
register: nm_query_binding
- name: Verify query_binding
assert:
that:
- cm_query_binding.changed == nm_query_binding.changed == false
- cm_query_binding == nm_query_binding
- nm_query_binding.current.0.physDomP.attributes.dn == 'uni/phys-phys_dom'
- nm_query_binding.current.0.physDomP.attributes.name == 'phys_dom'
- nm_query_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tCl == 'fvnsVlanInstP'
- nm_query_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vlanns-[test_pool]-dynamic'
# REMOVE BINDING
- name: Remove domain to VLAN pool binding (check_mode)
aci_domain_to_encap_pool: *binding_absent
check_mode: yes
register: cm_remove_binding
- name: Remove domain to VLAN pool binding (normal mode)
aci_domain_to_encap_pool: *binding_absent
register: nm_remove_binding
- name: Verify remove_binding
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}]'
- nm_remove_binding.current == []
- name: Remove domain to VLAN pool binding again (check_mode)
aci_domain_to_encap_pool: *binding_absent
check_mode: yes
register: cm_remove_binding_again
- name: Remove domain to VLAN pool binding again (normal mode)
aci_domain_to_encap_pool: *binding_absent
register: nm_remove_binding_again
- name: Verify remove_binding_again
assert:
that:
- cm_remove_binding_again.changed == nm_remove_binding_again.changed == false
# QUERY NON-EXISTING BINDING
- name: Query non-existing domain to VLAN pool binding (check_mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
check_mode: yes
register: cm_query_non_binding
- name: Query non-existing domain to VLAN pool binding (normal mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vlan
pool_allocation_mode: dynamic
register: nm_query_non_binding
- name: Verify query_non_binding
assert:
that:
- cm_query_non_binding.changed == nm_query_non_binding.changed == false
- cm_query_non_binding == nm_query_non_binding
- nm_query_non_binding.current == []

@ -1,215 +0,0 @@
# Test code for the ACI modules
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# CLEAN ENVIRONMENT
- name: Remove domain to VSAN pool binding
aci_domain_to_encap_pool: &binding_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: info
domain: phys_dom
domain_type: phys
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
state: absent
- name: Remove physical domain
aci_domain:
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) }}'
domain: phys_dom
domain_type: phys
state: absent
- name: Create VSAN pool
aci_encap_pool:
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) }}'
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
description: Test VSAN pool
state: present
# ADD BINDING
- name: Add domain to VSAN pool binding (check_mode)
aci_domain_to_encap_pool: &binding_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: info
domain: phys_dom
domain_type: phys
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
state: present
check_mode: yes
register: cm_add_binding
- name: Add domain to VSAN pool binding (normal mode)
aci_domain_to_encap_pool: *binding_present
register: nm_add_binding
- name: Verify add_binding
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- 'cm_add_binding.sent == nm_add_binding.sent == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vsanns-[test_pool]-static"}}}]}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vsanns-[test_pool]-static"}}}]}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vsanns-[test_pool]-static"}}}]}}]'
- name: Add domain to VSAN pool binding again (check_mode)
aci_domain_to_encap_pool: *binding_present
check_mode: yes
register: cm_add_binding_again
- name: Add domain to VSAN pool binding again (normal mode)
aci_domain_to_encap_pool: *binding_present
register: nm_add_binding_again
- name: Verify add_binding_again
assert:
that:
- cm_add_binding_again.changed == nm_add_binding_again.changed == false
# QUERY ALL BINDINGS
- name: Query all domain to VSAN pool bindings (check_mode)
aci_domain_to_encap_pool: &binding_query
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: info
domain_type: phys
pool_type: vsan
pool_allocation_mode: static
state: query
check_mode: yes
register: cm_query_all_bindings
- name: Query all domain to VSAN pool bindings (normal mode)
aci_domain_to_encap_pool: *binding_query
register: nm_query_all_bindings
- name: Verify query_all_bindings
assert:
that:
- cm_query_all_bindings.changed == nm_query_all_bindings.changed == false
- cm_query_all_bindings == nm_query_all_bindings
- nm_query_all_bindings.current|length >= 1
# QUERY A BINDING
- name: Query our domain to VSAN pool binding (check_mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
check_mode: yes
register: cm_query_binding
- name: Query our domain to VSAN pool binding (normal mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
register: nm_query_binding
- name: Verify query_binding
assert:
that:
- cm_query_binding.changed == nm_query_binding.changed == false
- cm_query_binding == nm_query_binding
- nm_query_binding.current.0.physDomP.attributes.dn == 'uni/phys-phys_dom'
- nm_query_binding.current.0.physDomP.attributes.name == 'phys_dom'
- nm_query_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tCl == 'fvnsVlanInstP'
- nm_query_binding.current.0.physDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vsanns-[test_pool]-static'
# REMOVE BINDING
- name: Remove domain to VSAN pool binding (check_mode)
aci_domain_to_encap_pool: *binding_absent
check_mode: yes
register: cm_remove_binding
- name: Remove domain to VSAN pool binding (normal mode)
aci_domain_to_encap_pool: *binding_absent
register: nm_remove_binding
- name: Verify remove_binding
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vsanns-[test_pool]-static"}}}]}}]'
- nm_remove_binding.current == []
- name: Remove domain to VSAN pool binding again (check_mode)
aci_domain_to_encap_pool: *binding_absent
check_mode: yes
register: cm_remove_binding_again
- name: Remove domain to VSAN pool binding again (normal mode)
aci_domain_to_encap_pool: *binding_absent
register: nm_remove_binding_again
- name: Verify remove_binding_again
assert:
that:
- cm_remove_binding_again.changed == nm_remove_binding_again.changed == false
# QUERY NON-EXISTING BINDING
- name: Query non-existing domain to VSAN pool binding (check_mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
check_mode: yes
register: cm_query_non_binding
- name: Query non-existing domain to VSAN pool binding (normal mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_type: vsan
pool_allocation_mode: static
register: nm_query_non_binding
- name: Verify query_non_binding
assert:
that:
- cm_query_non_binding.changed == nm_query_non_binding.changed == false
- cm_query_non_binding == nm_query_non_binding
- nm_query_non_binding.current == []

@ -1,215 +0,0 @@
# Test code for the ACI modules
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# CLEAN ENVIRONMENT
- name: Remove domain to VXLAN pool binding
aci_domain_to_encap_pool: &binding_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: info
domain: vmm_dom
domain_type: vmm
vm_provider: vmware
pool: test_pool
pool_type: vxlan
state: absent
- name: Remove VMM domain
aci_domain:
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) }}'
domain: vmm_dom
domain_type: vmm
vm_provider: vmware
state: absent
- name: Create VXLAN pool
aci_encap_pool:
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) }}'
pool: test_pool
pool_type: vxlan
description: Test VXLAN pool
state: present
# ADD BINDING
- name: Add domain to VXLAN pool binding (check_mode)
aci_domain_to_encap_pool: &binding_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: info
domain: vmm_dom
domain_type: vmm
vm_provider: vmware
pool: test_pool
pool_type: vxlan
state: present
check_mode: yes
register: cm_add_binding
- name: Add domain to VXLAN pool binding (normal mode)
aci_domain_to_encap_pool: *binding_present
register: nm_add_binding
- name: Verify add_binding
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- 'cm_add_binding.sent == nm_add_binding.sent == {"vmmDomP": {"attributes": {"name": "vmm_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vxlanns-[test_pool]-dynamic"}}}]}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"vmmDomP": {"attributes": {"name": "vmm_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vxlanns-[test_pool]-dynamic"}}}]}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"vmmDomP": {"attributes": {"dn": "uni/vmm-vmm_dom", "name": "vmm_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vxlanns-[test_pool]-dynamic"}}}]}}]'
- name: Add domain to VXLAN pool binding again (check_mode)
aci_domain_to_encap_pool: *binding_present
check_mode: yes
register: cm_add_binding_again
- name: Add domain to VXLAN pool binding again (normal mode)
aci_domain_to_encap_pool: *binding_present
register: nm_add_binding_again
- name: Verify add_binding_again
assert:
that:
- cm_add_binding_again.changed == nm_add_binding_again.changed == false
# QUERY ALL BINDINGS
- name: Query all domain to VXLAN pool bindings (check_mode)
aci_domain_to_encap_pool: &binding_query
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: info
domain_type: vmm
vm_provider: vmware
pool_type: vxlan
state: query
check_mode: yes
register: cm_query_all_bindings
- name: Query all domain to VXLAN pool bindings (normal mode)
aci_domain_to_encap_pool: *binding_query
register: nm_query_all_bindings
- name: Verify query_all_bindings
assert:
that:
- cm_query_all_bindings.changed == nm_query_all_bindings.changed == false
- cm_query_all_bindings == nm_query_all_bindings
- nm_query_all_bindings.current|length >= 1
# QUERY A BINDING
- name: Query our domain to VXLAN pool binding (check_mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: vmm_dom
vm_provider: vmware
pool: test_pool
pool_type: vxlan
check_mode: yes
register: cm_query_binding
- name: Query our domain to VXLAN pool binding (normal mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: vmm_dom
vm_provider: vmware
pool: test_pool
pool_type: vxlan
register: nm_query_binding
- name: Verify query_binding
assert:
that:
- cm_query_binding.changed == nm_query_binding.changed == false
- cm_query_binding == nm_query_binding
- nm_query_binding.current.0.vmmDomP.attributes.dn == 'uni/vmm-vmm_dom'
- nm_query_binding.current.0.vmmDomP.attributes.name == 'vmm_dom'
- nm_query_binding.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tCl == 'fvnsVlanInstP'
- nm_query_binding.current.0.vmmDomP.children.0.infraRsVlanNs.attributes.tDn == 'uni/infra/vxlanns-[test_pool]-dynamic'
# REMOVE BINDING
- name: Remove domain to VXLAN pool binding (check_mode)
aci_domain_to_encap_pool: *binding_absent
check_mode: yes
register: cm_remove_binding
- name: Remove domain to VXLAN pool binding (normal mode)
aci_domain_to_encap_pool: *binding_absent
register: nm_remove_binding
- name: Verify remove_binding
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"vmmDomP": {"attributes": {"dn": "uni/vmm-vmm_dom", "name": "vmm_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vxlanns-[test_pool]-dynamic"}}}]}}]'
- nm_remove_binding.current == []
- name: Remove domain to VXLAN pool binding again (check_mode)
aci_domain_to_encap_pool: *binding_absent
check_mode: yes
register: cm_remove_binding_again
- name: Remove domain to VXLAN pool binding again (normal mode)
aci_domain_to_encap_pool: *binding_absent
register: nm_remove_binding_again
- name: Verify remove_binding_again
assert:
that:
- cm_remove_binding_again.changed == nm_remove_binding_again.changed == false
# QUERY NON-EXISTING BINDING
- name: Query non-existing domain to VXLAN pool binding (check_mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: vmm_dom
vm_provider: vmware
pool: test_pool
pool_type: vxlan
check_mode: yes
register: cm_query_non_binding
- name: Query non-existing domain to VXLAN pool binding (normal mode)
aci_domain_to_encap_pool:
<<: *binding_query
domain: vmm_dom
vm_provider: vmware
pool: test_pool
pool_type: vxlan
register: nm_query_non_binding
- name: Verify query_non_binding
assert:
that:
- cm_query_non_binding.changed == nm_query_non_binding.changed == false
- cm_query_non_binding == nm_query_non_binding
- nm_query_non_binding.current == []

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -68,6 +68,15 @@
aci_domain_to_vlan_pool: *binding_present
register: nm_add_binding
- name: Verify add_binding
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- 'cm_add_binding.sent == nm_add_binding.sent == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}]'
- name: Add domain to VLAN pool binding again (check_mode)
aci_domain_to_vlan_pool: *binding_present
check_mode: yes
@ -77,15 +86,10 @@
aci_domain_to_vlan_pool: *binding_present
register: nm_add_binding_again
- name: Verify add_binding
- name: Verify add_binding_again
assert:
that:
- cm_add_binding.changed == nm_add_binding.changed == true
- cm_add_binding_again.changed == nm_add_binding_again.changed == false
- 'cm_add_binding.sent == nm_add_binding.sent == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}'
- 'cm_add_binding.proposed == nm_add_binding.proposed == {"physDomP": {"attributes": {"name": "phys_dom"}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}'
- cm_add_binding.current == cm_add_binding.previous == nm_add_binding.previous == []
- 'nm_add_binding.current == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}]'
# QUERY ALL BINDINGS
@ -99,7 +103,6 @@
use_proxy: '{{ aci_use_proxy | default(true) }}'
output_level: info
domain_type: phys
pool: test_pool
pool_allocation_mode: dynamic
state: query
check_mode: yes
@ -113,8 +116,8 @@
assert:
that:
- cm_query_all_bindings.changed == nm_query_all_bindings.changed == false
# NOTE: Order of bindings is not stable between calls
#- cm_query_all_bindings == nm_query_all_bindings
- cm_query_all_bindings == nm_query_all_bindings
- nm_query_all_bindings.current|length >= 1
# QUERY A BINDING
@ -131,6 +134,8 @@
aci_domain_to_vlan_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_allocation_mode: dynamic
register: nm_query_binding
- name: Verify query_binding
@ -154,6 +159,13 @@
aci_domain_to_vlan_pool: *binding_absent
register: nm_remove_binding
- name: Verify remove_binding
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}]'
- nm_remove_binding.current == []
- name: Remove domain to VLAN pool binding again (check_mode)
aci_domain_to_vlan_pool: *binding_absent
check_mode: yes
@ -163,23 +175,28 @@
aci_domain_to_vlan_pool: *binding_absent
register: nm_remove_binding_again
- name: Verify remove_binding
- name: Verify remove_binding_again
assert:
that:
- cm_remove_binding.changed == nm_remove_binding.changed == true
- cm_remove_binding_again.changed == nm_remove_binding_again.changed == false
- 'cm_remove_binding.current == cm_remove_binding.previous == nm_remove_binding.previous == [{"physDomP": {"attributes": {"dn": "uni/phys-phys_dom", "name": "phys_dom", "nameAlias": "", "ownerKey": "", "ownerTag": ""}, "children": [{"infraRsVlanNs": {"attributes": {"tDn": "uni/infra/vlanns-[test_pool]-dynamic"}}}]}}]'
- nm_remove_binding.current == []
# QUERY NON-EXISTING BINDING
- name: Query non-existing domain to VLAN pool binding (check_mode)
aci_domain_to_vlan_pool: *binding_query
aci_domain_to_vlan_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_allocation_mode: dynamic
check_mode: yes
register: cm_query_non_binding
- name: Query non-existing domain to VLAN pool binding (normal mode)
aci_domain_to_vlan_pool: *binding_query
aci_domain_to_vlan_pool:
<<: *binding_query
domain: phys_dom
pool: test_pool
pool_allocation_mode: dynamic
register: nm_query_non_binding
- name: Verify query_non_binding

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
---
- name: ensure vxlan pool does not exist for tests to kick off
- name: ensure vxlan pool anstest does not exist for tests to kick off
aci_encap_pool: &aci_vxlan_absent
host: "{{ aci_hostname }}"
username: "{{ aci_username }}"
@ -12,6 +12,16 @@
pool: anstest
pool_type: vxlan
- name: ensure vxlan pool anstest_2 does not exist for tests to kick off
aci_encap_pool:
<<: *aci_vxlan_absent
pool: anstest_2
- name: ensure vxlan pool anstest_3 does not exist for tests to kick off
aci_encap_pool:
<<: *aci_vxlan_absent
pool: anstest_3
- name: create vxlan pool - check mode works
aci_encap_pool: &aci_vxlan_present
<<: *aci_vxlan_absent
@ -85,7 +95,7 @@
assert:
that:
- create_vxlan_alloc_mode.failed == true
- 'create_vxlan_alloc_mode.msg == "vxlan pools do not support setting the pool_allocation_mode; please remove this parameter from the task"'
- "create_vxlan_alloc_mode.msg == 'vxlan pools do not support setting the \\'pool_allocation_mode\\'; please remove this parameter from the task'"
- name: get vxlan pool - get object works
aci_encap_pool: &aci_vxlan_query

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -176,7 +176,7 @@
state: absent
register: provide_absent2
- name: delte consume binding - idempotency works
- name: delete consume binding - idempotency works
aci_epg_to_contract:
<<: *aci_epg_consume_absent
register: consume_absent_idempotent

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Bruno Calogero <bcaloger@cisco.com>
# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@ -90,7 +90,7 @@
- intf_policy_leaf_polgrp_check_mode_present.changed == true
- intf_policy_leaf_polgrp_present.changed == true
- intf_policy_leaf_polgrp_present.previous == []
- 'intf_policy_leaf_polgrp_present.sent == {"infraAccBndlGrp": {"attributes": {"dn": "uni/infra/funcprof/accbundle-policygroupname","lagT": "link","name": "policygroupname"},"children": [{"infraRsFcIfPol": {"attributes": {"tnFcIfPolName": "fiberchannelpolicy"}}},{"infraRsHIfPol": {"attributes": {"tnFabricHIfPolName": "linklevelpolicy"}}},{"infraRsAttEntP": {"attributes": {"tDn": "uni/infra/attentp-None"}}}]}}'
- 'intf_policy_leaf_polgrp_present.sent == {"infraAccBndlGrp": {"attributes": {"lagT": "link","name": "policygroupname"},"children": [{"infraRsFcIfPol": {"attributes": {"tnFcIfPolName": "fiberchannelpolicy"}}},{"infraRsHIfPol": {"attributes": {"tnFabricHIfPolName": "linklevelpolicy"}}},{"infraRsAttEntP": {"attributes": {"tDn": "uni/infra/attentp-None"}}}]}}'
- intf_policy_leaf_polgrp_idempotent.changed == false
- intf_policy_leaf_polgrp_idempotent.sent == {}
- intf_policy_leaf_polgrp_update.changed == true
@ -209,7 +209,7 @@
- intf_policy_leaf_polgrp_check_mode_present.changed == true
- intf_policy_leaf_polgrp_present.changed == true
- intf_policy_leaf_polgrp_present.previous == []
- 'intf_policy_leaf_polgrp_present.sent == {"infraAccBndlGrp": {"attributes": {"dn": "uni/infra/funcprof/accbundle-policygroupname","lagT": "node","name": "policygroupname"},"children": [{"infraRsFcIfPol": {"attributes": {"tnFcIfPolName": "fiberchannelpolicy"}}},{"infraRsHIfPol": {"attributes": {"tnFabricHIfPolName": "linklevelpolicy"}}},{"infraRsAttEntP": {"attributes": {"tDn": "uni/infra/attentp-None"}}}]}}'
- 'intf_policy_leaf_polgrp_present.sent == {"infraAccBndlGrp": {"attributes": {"lagT": "node","name": "policygroupname"},"children": [{"infraRsFcIfPol": {"attributes": {"tnFcIfPolName": "fiberchannelpolicy"}}},{"infraRsHIfPol": {"attributes": {"tnFabricHIfPolName": "linklevelpolicy"}}},{"infraRsAttEntP": {"attributes": {"tDn": "uni/infra/attentp-None"}}}]}}'
- intf_policy_leaf_polgrp_idempotent.changed == false
- intf_policy_leaf_polgrp_idempotent.sent == {}
- intf_policy_leaf_polgrp_update.changed == true
@ -336,7 +336,7 @@
- intf_policy_leaf_polgrp_check_mode_present.changed == true
- intf_policy_leaf_polgrp_present.changed == true
- intf_policy_leaf_polgrp_present.previous == []
- 'intf_policy_leaf_polgrp_present.sent == {"infraAccPortGrp": {"attributes": {"dn": "uni/infra/funcprof/accportgrp-policygroupname","name": "policygroupname"},"children": [{"infraRsFcIfPol": {"attributes": {"tnFcIfPolName": "fiberchannelpolicy"}}},{"infraRsHIfPol": {"attributes": {"tnFabricHIfPolName": "linklevelpolicy"}}},{"infraRsAttEntP": {"attributes": {"tDn": "uni/infra/attentp-None"}}}]}}'
- 'intf_policy_leaf_polgrp_present.sent == {"infraAccPortGrp": {"attributes": {"name": "policygroupname"},"children": [{"infraRsFcIfPol": {"attributes": {"tnFcIfPolName": "fiberchannelpolicy"}}},{"infraRsHIfPol": {"attributes": {"tnFabricHIfPolName": "linklevelpolicy"}}},{"infraRsAttEntP": {"attributes": {"tDn": "uni/infra/attentp-None"}}}]}}'
- intf_policy_leaf_polgrp_idempotent.changed == false
- intf_policy_leaf_polgrp_idempotent.sent == {}
- intf_policy_leaf_polgrp_update.changed == true

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Bruno Calogero <bcaloger@cisco.com>
# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Bruno Calogero <bcalogero@cisco.com>
# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,12 +1,8 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
# CLEAN ENVIRONMENT
- name: Remove tenant
@ -19,7 +15,6 @@
use_proxy: '{{ aci_use_proxy | default(true) }}'
path: /api/mo/uni/tn-[ansible_test].json
method: delete
delegate_to: localhost
# ADD TENANT
- name: Add tenant (normal mode)

@ -1,12 +1,8 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
# CLEAN ENVIRONMENT
- name: Remove tenant

@ -1,8 +1,13 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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: yaml_inline.yml
tags: yaml_inline
@ -17,3 +22,6 @@
- include_tasks: xml_string.yml
tags: xml_string
- include_tasks: error_handling.yml
tags: error_handling

@ -1,12 +1,8 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
# CLEAN ENVIRONMENT
- name: Remove tenant

@ -1,12 +1,8 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
# CLEAN ENVIRONMENT
- name: Remove tenant

@ -1,12 +1,8 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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
# CLEAN ENVIRONMENT
- name: Remove tenant

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Bruno Calogero <bcaloger@cisco.com>
# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Bruno Calogero <bcaloger@cisco.com>
# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -268,7 +268,7 @@
ignore_errors: yes
register: error_on_missing_required_param
- name: Assertion test - present
- name: Verify error_on_missing_required_param
assert:
that:
- error_on_missing_required_param.failed == true

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Dag Wieers <dag@wieers.com>
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

@ -258,7 +258,7 @@
ignore_errors: yes
register: error_on_missing_required_param
- name: Assertion test - present
- name: Verify error_on_missing_required_param
assert:
that:
- error_on_missing_required_param.failed == true

@ -258,7 +258,7 @@
ignore_errors: yes
register: error_on_missing_required_param
- name: Assertion test - present
- name: Verify error_on_missing_required_param
assert:
that:
- error_on_missing_required_param.failed == true

@ -1,5 +1,5 @@
# Test code for the ACI modules
# Copyright 2017, Jacob McGill <jmcgill298
# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

Loading…
Cancel
Save