summaryrefslogtreecommitdiffstats
path: root/ansible_collections/cisco/aci/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/cisco/aci/plugins')
-rw-r--r--ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py0
-rw-r--r--ansible_collections/cisco/aci/plugins/doc_fragments/aci.py103
-rw-r--r--ansible_collections/cisco/aci/plugins/doc_fragments/annotation.py20
-rw-r--r--ansible_collections/cisco/aci/plugins/doc_fragments/owner.py26
-rw-r--r--ansible_collections/cisco/aci/plugins/lookup/__init__.py0
-rw-r--r--ansible_collections/cisco/aci/plugins/lookup/interface_range.py70
-rw-r--r--ansible_collections/cisco/aci/plugins/module_utils/__init__.py0
-rw-r--r--ansible_collections/cisco/aci/plugins/module_utils/aci.py1627
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/__init__.py0
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_custom_privilege.py366
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_domain.py280
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_role.py380
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_ssh_auth.py273
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py371
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py300
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_domain.py317
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_role.py365
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py438
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py493
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py365
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aep.py289
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py314
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_aep_to_epg.py333
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_ap.py293
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bd.py489
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py317
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py468
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py283
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_asn.py239
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_node.py271
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_bulk_static_binding_to_epg.py608
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_ap.py309
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_aws_provider.py290
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_bgp_asn.py250
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py287
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py309
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg.py294
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg_selector.py371
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg.py301
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg_selector.py183
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py177
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py216
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py320
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py235
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py224
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py324
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py348
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_contract.py340
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_contract_export.py287
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py433
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py373
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_service_graph.py253
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay.py274
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay_provider.py386
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_dns_domain.py273
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_dns_profile.py246
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_dns_provider.py273
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_domain.py460
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py379
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py367
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py319
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py450
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_epg.py429
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py279
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py391
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_interface.py306
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py297
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py552
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_esg.py374
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_esg_contract_master.py296
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_esg_epg_selector.py348
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_esg_ip_subnet_selector.py301
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_esg_tag_selector.py344
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_profile.py253
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_switch_assoc.py283
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py290
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_pod_policy_group.py320
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py353
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_profile.py253
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_switch_assoc.py286
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_block.py311
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_policy_group.py486
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_filter.py282
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py434
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py267
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py264
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py250
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py304
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_blacklist.py321
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_description.py347
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py267
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py268
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py296
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py262
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py587
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py316
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile_fex_policy_group.py292
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py298
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py278
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py269
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py408
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py352
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py283
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py252
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l2out.py306
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py305
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg_to_contract.py349
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_path.py372
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_profile.py299
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_node_profile.py279
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out.py389
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_bgp_peer.py616
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py335
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py347
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py352
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface.py409
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface_secondary_ip.py405
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile.py315
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile_ospf_policy.py344
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py411
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node.py316
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node_profile.py335
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py289
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py366
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes_nexthop.py306
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py263
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py261
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py300
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_node_mgmt_epg.py317
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_ntp_policy.py291
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_ntp_server.py323
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_rest.py451
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_snmp_client.py280
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_snmp_client_group.py284
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_snmp_community_policy.py263
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_snmp_policy.py272
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_snmp_user.py301
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py481
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_static_node_mgmt_address.py346
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py352
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py253
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py305
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_syslog_group.py337
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_syslog_remote_dest.py333
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_syslog_source.py280
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_system.py194
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py289
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tag.py270
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant.py264
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py277
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py364
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py391
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py296
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_src.py319
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py295
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py286
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py367
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vmm_controller.py372
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py319
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink.py273
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink_container.py261
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vmm_vswitch_policy.py471
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vrf.py328
-rw-r--r--ansible_collections/cisco/aci/plugins/modules/aci_vzany_to_contract.py280
164 files changed, 52552 insertions, 0 deletions
diff --git a/ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py b/ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/doc_fragments/__init__.py
diff --git a/ansible_collections/cisco/aci/plugins/doc_fragments/aci.py b/ansible_collections/cisco/aci/plugins/doc_fragments/aci.py
new file mode 100644
index 00000000..b1a494c1
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/doc_fragments/aci.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
+# Copyright: (c) 2017, Swetha Chunduri (@schunduri)
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+ # Standard files documentation fragment
+ DOCUMENTATION = r"""
+options:
+ host:
+ description:
+ - IP Address or hostname of APIC resolvable by Ansible control host.
+ - If the value is not specified in the task, the value of environment variable C(ACI_HOST) will be used instead.
+ type: str
+ required: yes
+ aliases: [ hostname ]
+ port:
+ description:
+ - Port number to be used for REST connection.
+ - The default value depends on parameter C(use_ssl).
+ - If the value is not specified in the task, the value of environment variable C(ACI_PORT) will be used instead.
+ type: int
+ username:
+ description:
+ - The username to use for authentication.
+ - If the value is not specified in the task, the value of environment variables C(ACI_USERNAME) or C(ANSIBLE_NET_USERNAME) will be used instead.
+ type: str
+ default: admin
+ aliases: [ user ]
+ password:
+ description:
+ - The password to use for authentication.
+ - This option is mutual exclusive with C(private_key). If C(private_key) is provided too, it will be used instead.
+ - If the value is not specified in the task, the value of environment variables C(ACI_PASSWORD) or C(ANSIBLE_NET_PASSWORD) will be used instead.
+ type: str
+ private_key:
+ description:
+ - Either a PEM-formatted private key file or the private key content used for signature-based authentication.
+ - This value also influences the default C(certificate_name) that is used.
+ - This option is mutual exclusive with C(password). If C(password) is provided too, it will be ignored.
+ - If the value is not specified in the task, the value of environment variable C(ACI_PRIVATE_KEY) or C(ANSIBLE_NET_SSH_KEYFILE) will be used instead.
+ type: str
+ aliases: [ cert_key ]
+ certificate_name:
+ description:
+ - The X.509 certificate name attached to the APIC AAA user used for signature-based authentication.
+ - If a C(private_key) filename was provided, this defaults to the C(private_key) basename, without extension.
+ - If PEM-formatted content was provided for C(private_key), this defaults to the C(username) value.
+ - If the value is not specified in the task, the value of environment variable C(ACI_CERTIFICATE_NAME) will be used instead.
+ type: str
+ aliases: [ cert_name ]
+ output_level:
+ description:
+ - Influence the output of this ACI module.
+ - C(normal) means the standard output, incl. C(current) dict
+ - C(info) adds informational output, incl. C(previous), C(proposed) and C(sent) dicts
+ - C(debug) adds debugging output, incl. C(filter_string), C(method), C(response), C(status) and C(url) information
+ - If the value is not specified in the task, the value of environment variable C(ACI_OUTPUT_LEVEL) will be used instead.
+ type: str
+ choices: [ debug, info, normal ]
+ default: normal
+ timeout:
+ description:
+ - The socket level timeout in seconds.
+ - If the value is not specified in the task, the value of environment variable C(ACI_TIMEOUT) will be used instead.
+ type: int
+ default: 30
+ use_proxy:
+ description:
+ - If C(no), it will not use a proxy, even if one is defined in an environment variable on the target hosts.
+ - If the value is not specified in the task, the value of environment variable C(ACI_USE_PROXY) will be used instead.
+ type: bool
+ default: yes
+ use_ssl:
+ description:
+ - If C(no), an HTTP connection will be used instead of the default HTTPS connection.
+ - If the value is not specified in the task, the value of environment variable C(ACI_USE_SSL) will be used instead.
+ type: bool
+ default: yes
+ validate_certs:
+ description:
+ - If C(no), SSL certificates will not be validated.
+ - This should only set to C(no) when used on personally controlled sites using self-signed certificates.
+ - If the value is not specified in the task, the value of environment variable C(ACI_VALIDATE_CERTS) will be used instead.
+ type: bool
+ default: yes
+ output_path:
+ description:
+ - Path to a file that will be used to dump the ACI JSON configuration objects generated by the module.
+ - If the value is not specified in the task, the value of environment variable C(ACI_OUTPUT_PATH) will be used instead.
+ type: str
+seealso:
+- ref: aci_guide
+ description: Detailed information on how to manage your ACI infrastructure using Ansible.
+- ref: aci_dev_guide
+ description: Detailed guide on how to write your own Cisco ACI modules to contribute.
+"""
diff --git a/ansible_collections/cisco/aci/plugins/doc_fragments/annotation.py b/ansible_collections/cisco/aci/plugins/doc_fragments/annotation.py
new file mode 100644
index 00000000..ec299631
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/doc_fragments/annotation.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+options:
+ annotation:
+ description:
+ - User-defined string for annotating an object.
+ - If the value is not specified in the task, the value of environment variable C(ACI_ANNOTATION) will be used instead.
+ - If the value is not specified in the task and environment variable C(ACI_ANNOTATION) then the default value will be used.
+ type: str
+ default: orchestrator:ansible
+'''
diff --git a/ansible_collections/cisco/aci/plugins/doc_fragments/owner.py b/ansible_collections/cisco/aci/plugins/doc_fragments/owner.py
new file mode 100644
index 00000000..7b0a70ce
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/doc_fragments/owner.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+ # Standard files documentation fragment
+ DOCUMENTATION = r'''
+options:
+ owner_key:
+ description:
+ - User-defined string for the ownerKey attribute of an ACI object.
+ - This attribute represents a key for enabling clients to own their data for entity correlation.
+ - If the value is not specified in the task, the value of environment variable C(ACI_OWNER_KEY) will be used instead.
+ type: str
+ owner_tag:
+ description:
+ - User-defined string for the ownerTag attribute of an ACI object.
+ - This attribute represents a tag for enabling clients to add their own data.
+ - For example, to indicate who created this object.
+ - If the value is not specified in the task, the value of environment variable C(ACI_OWNER_TAG) will be used instead.
+ type: str
+'''
diff --git a/ansible_collections/cisco/aci/plugins/lookup/__init__.py b/ansible_collections/cisco/aci/plugins/lookup/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/lookup/__init__.py
diff --git a/ansible_collections/cisco/aci/plugins/lookup/interface_range.py b/ansible_collections/cisco/aci/plugins/lookup/interface_range.py
new file mode 100644
index 00000000..8aa85f20
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/lookup/interface_range.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Akini Ross (@akinross) <akinross@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = """
+ name: interface_range
+ short_description: query interfaces from a range or comma separated list of ranges
+ description:
+ - this lookup returns interfaces from a range or comma separated list of ranges given to it
+ notes:
+ - duplicate interfaces from overlapping ranges will only be returned once
+ options:
+ _terms:
+ description: comma separated strings of interface ranges
+ required: True
+"""
+
+EXAMPLES = """
+- name: "loop through range of interfaces"
+ ansible.builtin.debug:
+ msg: "{{ item }}"
+ with_items: "{{ query('cisco.aci.interface_range', '1/1-4,1/20-25', '1/5', '1/2/3/8-10', '5/0-2') }}"
+"""
+
+RETURN = """
+ _list:
+ description: list of interfaces
+ type: list
+ elements: str
+"""
+
+import re
+
+from ansible.errors import AnsibleError
+from ansible.plugins.lookup import LookupBase
+
+
+class LookupModule(LookupBase):
+
+ def run(self, terms, **kwargs):
+
+ interfaces = []
+ errors = []
+
+ for interface_range in ','.join(terms).replace(" ", "").split(","):
+ if re.fullmatch(r"((\d+/)+\d+-\d+$)", interface_range):
+ slots = interface_range.rsplit("/", 1)[0]
+ range_start, range_stop = interface_range.rsplit("/", 1)[1].split("-")
+ if int(range_stop) > int(range_start):
+ for x in range(int(range_start), int(range_stop) + 1):
+ interfaces.append("{0}/{1}".format(slots, x))
+ else:
+ errors.append(interface_range)
+ elif re.fullmatch(r"((\d+/)+\d+$)", interface_range):
+ interfaces.append(interface_range)
+ else:
+ errors.append(interface_range)
+ if errors:
+ raise AnsibleError("Invalid range inputs, {0}".format(errors))
+
+ # Sorted functionality for visual aid only, will result in 1/25, 1/3, 1/31
+ # If full sort is needed leverage natsort package (https://github.com/SethMMorton/natsort)
+ return sorted(set(interfaces))
diff --git a/ansible_collections/cisco/aci/plugins/module_utils/__init__.py b/ansible_collections/cisco/aci/plugins/module_utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/module_utils/__init__.py
diff --git a/ansible_collections/cisco/aci/plugins/module_utils/aci.py b/ansible_collections/cisco/aci/plugins/module_utils/aci.py
new file mode 100644
index 00000000..d94b1042
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/module_utils/aci.py
@@ -0,0 +1,1627 @@
+# -*- coding: utf-8 -*-
+
+# This code is part of Ansible, but is an independent component
+
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
+
+# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
+# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
+# Copyright: (c) 2017, Swetha Chunduri (@schunduri)
+# Copyright: (c) 2019, Rob Huelga (@RobW3LGA)
+# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com>
+# Copyright: (c) 2020, Anvitha Jain (@anvitha-jain) <anvjain@cisco.com>
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import base64
+import json
+import os
+from copy import deepcopy
+
+from ansible.module_utils.urls import fetch_url
+from ansible.module_utils._text import to_bytes, to_native
+from ansible.module_utils.basic import env_fallback
+
+# Optional, only used for APIC signature-based authentication
+try:
+ from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign
+
+ HAS_OPENSSL = True
+except ImportError:
+ HAS_OPENSSL = False
+
+# Signature-based authentication using cryptography
+try:
+ from cryptography.hazmat.primitives import serialization, hashes
+ from cryptography.hazmat.primitives.asymmetric import padding
+ from cryptography.hazmat.backends import default_backend
+
+ HAS_CRYPTOGRAPHY = True
+except ImportError:
+ HAS_CRYPTOGRAPHY = False
+
+# Optional, only used for XML payload
+try:
+ import lxml.etree
+
+ HAS_LXML_ETREE = True
+except ImportError:
+ HAS_LXML_ETREE = False
+
+# Optional, only used for XML payload
+try:
+ from xmljson import cobra
+
+ HAS_XMLJSON_COBRA = True
+except ImportError:
+ HAS_XMLJSON_COBRA = False
+
+
+def aci_argument_spec():
+ return dict(
+ host=dict(
+ type="str",
+ required=True,
+ aliases=["hostname"],
+ fallback=(env_fallback, ["ACI_HOST"]),
+ ),
+ port=dict(type="int", required=False, fallback=(env_fallback, ["ACI_PORT"])),
+ username=dict(
+ type="str",
+ default="admin",
+ aliases=["user"],
+ fallback=(env_fallback, ["ACI_USERNAME", "ANSIBLE_NET_USERNAME"]),
+ ),
+ password=dict(
+ type="str",
+ no_log=True,
+ fallback=(env_fallback, ["ACI_PASSWORD", "ANSIBLE_NET_PASSWORD"]),
+ ),
+ # Beware, this is not the same as client_key !
+ private_key=dict(
+ type="str",
+ aliases=["cert_key"],
+ no_log=True,
+ fallback=(env_fallback, ["ACI_PRIVATE_KEY", "ANSIBLE_NET_SSH_KEYFILE"]),
+ ),
+ # Beware, this is not the same as client_cert !
+ certificate_name=dict(
+ type="str",
+ aliases=["cert_name"],
+ fallback=(env_fallback, ["ACI_CERTIFICATE_NAME"]),
+ ),
+ output_level=dict(
+ type="str",
+ default="normal",
+ choices=["debug", "info", "normal"],
+ fallback=(env_fallback, ["ACI_OUTPUT_LEVEL"]),
+ ),
+ timeout=dict(type="int", default=30, fallback=(env_fallback, ["ACI_TIMEOUT"])),
+ use_proxy=dict(type="bool", default=True, fallback=(env_fallback, ["ACI_USE_PROXY"])),
+ use_ssl=dict(type="bool", default=True, fallback=(env_fallback, ["ACI_USE_SSL"])),
+ validate_certs=dict(type="bool", default=True, fallback=(env_fallback, ["ACI_VALIDATE_CERTS"])),
+ output_path=dict(type="str", fallback=(env_fallback, ["ACI_OUTPUT_PATH"])),
+ )
+
+
+def aci_annotation_spec():
+ return dict(
+ annotation=dict(
+ type="str",
+ default="orchestrator:ansible",
+ fallback=(env_fallback, ["ACI_ANNOTATION"]),
+ ),
+ )
+
+
+def aci_owner_spec():
+ return dict(
+ owner_key=dict(type="str", no_log=False, fallback=(env_fallback, ["ACI_OWNER_KEY"])),
+ owner_tag=dict(type="str", fallback=(env_fallback, ["ACI_OWNER_TAG"])),
+ )
+
+
+def enhanced_lag_spec():
+ return dict(
+ name=dict(type="str", required=True),
+ lacp_mode=dict(type="str", choices=["active", "passive"]),
+ load_balancing_mode=dict(
+ type="str",
+ choices=[
+ "dst-ip",
+ "dst-ip-l4port",
+ "dst-ip-vlan",
+ "dst-ip-l4port-vlan",
+ "dst-mac",
+ "dst-l4port",
+ "src-ip",
+ "src-ip-l4port",
+ "src-ip-vlan",
+ "src-ip-l4port-vlan",
+ "src-mac",
+ "src-l4port",
+ "src-dst-ip",
+ "src-dst-ip-l4port",
+ "src-dst-ip-vlan",
+ "src-dst-ip-l4port-vlan",
+ "src-dst-mac",
+ "src-dst-l4port",
+ "src-port-id",
+ "vlan",
+ ],
+ ),
+ number_uplinks=dict(type="int"),
+ )
+
+
+def netflow_spec():
+ return dict(
+ name=dict(type="str", required=True),
+ active_flow_timeout=dict(type="int"),
+ idle_flow_timeout=dict(type="int"),
+ sampling_rate=dict(type="int"),
+ )
+
+
+def expression_spec():
+ return dict(
+ key=dict(type="str", required=True, no_log=False),
+ operator=dict(
+ type="str",
+ choices=[
+ "not_in",
+ "in",
+ "equals",
+ "not_equals",
+ "has_key",
+ "does_not_have_key",
+ ],
+ required=True,
+ ),
+ value=dict(type="str"),
+ )
+
+
+def aci_contract_qos_spec():
+ return dict(type="str", choices=["level1", "level2", "level3", "unspecified"])
+
+
+def aci_contract_dscp_spec(direction=None):
+ return dict(
+ type="str",
+ aliases=["target" if not direction else "target_{0}".format(direction)],
+ choices=[
+ "AF11",
+ "AF12",
+ "AF13",
+ "AF21",
+ "AF22",
+ "AF23",
+ "AF31",
+ "AF32",
+ "AF33",
+ "AF41",
+ "AF42",
+ "AF43",
+ "CS0",
+ "CS1",
+ "CS2",
+ "CS3",
+ "CS4",
+ "CS5",
+ "CS6",
+ "CS7",
+ "EF",
+ "VA",
+ "unspecified",
+ ],
+ )
+
+
+def route_control_profile_spec():
+ return dict(
+ profile=dict(type="str", required=True),
+ l3out=dict(type="str"),
+ direction=dict(type="str", required=True),
+ tenant=dict(type="str", required=True),
+ )
+
+
+class ACIModule(object):
+ def __init__(self, module):
+ self.module = module
+ self.params = module.params
+ self.result = dict(changed=False)
+ self.headers = dict()
+ self.child_classes = set()
+
+ # error output
+ self.error = dict(code=None, text=None)
+
+ # normal output
+ self.existing = None
+
+ # info output
+ self.config = dict()
+ self.original = None
+ self.proposed = dict()
+ self.stdout = None
+
+ # debug output
+ self.filter_string = ""
+ self.obj_filter = None
+ self.method = None
+ self.path = None
+ self.response = None
+ self.status = None
+ self.url = None
+
+ # aci_rest output
+ self.imdata = None
+ self.totalCount = None
+
+ # Ensure protocol is set
+ self.define_protocol()
+
+ if self.module._debug:
+ self.module.warn("Enable debug output because ANSIBLE_DEBUG was set.")
+ self.params["output_level"] = "debug"
+
+ if self.params.get("private_key"):
+ # Perform signature-based authentication, no need to log on separately
+ if not HAS_CRYPTOGRAPHY and not HAS_OPENSSL:
+ self.module.fail_json(msg="Cannot use signature-based authentication because cryptography (preferred) or pyopenssl are not available")
+ elif self.params.get("password") is not None:
+ self.module.warn("When doing ACI signatured-based authentication, providing parameter 'password' is not required")
+ elif self.params.get("password"):
+ # Perform password-based authentication, log on using password
+ self.login()
+ else:
+ self.module.fail_json(msg="Either parameter 'password' or 'private_key' is required for authentication")
+
+ def boolean(self, value, true="yes", false="no"):
+ """Return an acceptable value back"""
+
+ # When we expect value is of type=bool
+ if value is None:
+ return None
+ elif value is True:
+ return true
+ elif value is False:
+ return false
+
+ # If all else fails, escalate back to user
+ self.module.fail_json(msg="Boolean value '%s' is an invalid ACI boolean value.")
+
+ def iso8601_format(self, dt):
+ """Return an ACI-compatible ISO8601 formatted time: 2123-12-12T00:00:00.000+00:00"""
+ try:
+ return dt.isoformat(timespec="milliseconds")
+ except Exception:
+ tz = dt.strftime("%z")
+ return "%s.%03d%s:%s" % (
+ dt.strftime("%Y-%m-%dT%H:%M:%S"),
+ dt.microsecond / 1000,
+ tz[:3],
+ tz[3:],
+ )
+
+ def define_protocol(self):
+ """Set protocol based on use_ssl parameter"""
+
+ # Set protocol for further use
+ self.params["protocol"] = "https" if self.params.get("use_ssl", True) else "http"
+
+ def define_method(self):
+ """Set method based on state parameter"""
+
+ # Set method for further use
+ state_map = dict(absent="delete", present="post", query="get")
+ self.params["method"] = state_map.get(self.params.get("state"))
+
+ def login(self):
+ """Log in to APIC"""
+
+ # Perform login request
+ if self.params.get("port") is not None:
+ url = "%(protocol)s://%(host)s:%(port)s/api/aaaLogin.json" % self.params
+ else:
+ url = "%(protocol)s://%(host)s/api/aaaLogin.json" % self.params
+ payload = {
+ "aaaUser": {
+ "attributes": {
+ "name": self.params.get("username"),
+ "pwd": self.params.get("password"),
+ }
+ }
+ }
+ resp, auth = fetch_url(
+ self.module,
+ url,
+ data=json.dumps(payload),
+ method="POST",
+ timeout=self.params.get("timeout"),
+ use_proxy=self.params.get("use_proxy"),
+ )
+
+ # Handle APIC response
+ if auth.get("status") != 200:
+ self.response = auth.get("msg")
+ self.status = auth.get("status")
+ try:
+ # APIC error
+ self.response_json(auth["body"])
+ self.fail_json(msg="Authentication failed: %(code)s %(text)s" % self.error)
+ except KeyError:
+ # Connection error
+ self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % auth)
+
+ # Retain cookie for later use
+ self.headers["Cookie"] = resp.headers.get("Set-Cookie")
+
+ def cert_auth(self, path=None, payload="", method=None):
+ """Perform APIC signature-based authentication, not the expected SSL client certificate authentication."""
+
+ if method is None:
+ method = self.params.get("method").upper()
+
+ # NOTE: ACI documentation incorrectly uses complete URL
+ if path is None:
+ path = self.path
+ path = "/" + path.lstrip("/")
+
+ if payload is None:
+ payload = ""
+
+ # Check if we got a private key. This allows the use of vaulting the private key.
+ try:
+ if HAS_CRYPTOGRAPHY:
+ key = self.params.get("private_key").encode()
+ sig_key = serialization.load_pem_private_key(
+ key,
+ password=None,
+ backend=default_backend(),
+ )
+ else:
+ sig_key = load_privatekey(FILETYPE_PEM, self.params.get("private_key"))
+ except Exception:
+ if os.path.exists(self.params.get("private_key")):
+ try:
+ permission = "r"
+ if HAS_CRYPTOGRAPHY:
+ permission = "rb"
+ with open(self.params.get("private_key"), permission) as fh:
+ private_key_content = fh.read()
+ except Exception:
+ self.module.fail_json(msg="Cannot open private key file '%(private_key)s'." % self.params)
+ try:
+ if HAS_CRYPTOGRAPHY:
+ sig_key = serialization.load_pem_private_key(
+ private_key_content,
+ password=None,
+ backend=default_backend(),
+ )
+ else:
+ sig_key = load_privatekey(FILETYPE_PEM, private_key_content)
+ except Exception:
+ self.module.fail_json(msg="Cannot load private key file '%(private_key)s'." % self.params)
+ if self.params.get("certificate_name") is None:
+ self.params["certificate_name"] = os.path.basename(os.path.splitext(self.params.get("private_key"))[0])
+ else:
+ self.module.fail_json(msg="Provided private key '%(private_key)s' does not appear to be a private key." % self.params)
+
+ if self.params.get("certificate_name") is None:
+ self.params["certificate_name"] = self.params.get("username")
+ # NOTE: ACI documentation incorrectly adds a space between method and path
+ sig_request = method + path + payload
+ if HAS_CRYPTOGRAPHY:
+ sig_signature = sig_key.sign(sig_request.encode(), padding.PKCS1v15(), hashes.SHA256())
+ else:
+ sig_signature = sign(sig_key, sig_request, "sha256")
+ sig_dn = "uni/userext/user-%(username)s/usercert-%(certificate_name)s" % self.params
+ self.headers["Cookie"] = (
+ "APIC-Certificate-Algorithm=v1.0; "
+ + "APIC-Certificate-DN=%s; " % sig_dn
+ + "APIC-Certificate-Fingerprint=fingerprint; "
+ + "APIC-Request-Signature=%s" % to_native(base64.b64encode(sig_signature))
+ )
+
+ def response_json(self, rawoutput):
+ """Handle APIC JSON response output"""
+ try:
+ jsondata = json.loads(rawoutput)
+ 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.result["raw"] = rawoutput
+ return
+
+ # Extract JSON API output
+ self.imdata = jsondata.get("imdata")
+ if self.imdata is None:
+ self.imdata = dict()
+ self.totalCount = int(jsondata.get("totalCount"))
+
+ # Handle possible APIC error information
+ self.response_error()
+
+ def response_xml(self, rawoutput):
+ """Handle APIC XML response output"""
+
+ # NOTE: The XML-to-JSON conversion is using the "Cobra" convention
+ try:
+ xml = lxml.etree.fromstring(to_bytes(rawoutput))
+ xmldata = cobra.data(xml)
+ 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.result["raw"] = rawoutput
+ return
+
+ # Reformat as ACI does for JSON API output
+ self.imdata = xmldata.get("imdata", {}).get("children")
+ if self.imdata is None:
+ self.imdata = dict()
+ self.totalCount = int(xmldata.get("imdata", {}).get("attributes", {}).get("totalCount"))
+
+ # Handle possible APIC error information
+ self.response_error()
+
+ def response_error(self):
+ """Set error information when found"""
+
+ # Handle possible APIC error information
+ if self.totalCount != "0":
+ try:
+ self.error = self.imdata[0].get("error").get("attributes")
+ except (AttributeError, IndexError, KeyError):
+ pass
+
+ def request(self, path, payload=None):
+ """Perform a REST request"""
+
+ # Ensure method is set (only do this once)
+ self.define_method()
+ self.path = path
+
+ if self.params.get("port") is not None:
+ self.url = "%(protocol)s://%(host)s:%(port)s/" % self.params + path.lstrip("/")
+ else:
+ self.url = "%(protocol)s://%(host)s/" % self.params + path.lstrip("/")
+
+ # Sign and encode request as to APIC's wishes
+ if self.params.get("private_key"):
+ self.cert_auth(path=path, payload=payload)
+
+ # Perform request
+ resp, info = fetch_url(
+ self.module,
+ self.url,
+ data=payload,
+ headers=self.headers,
+ method=self.params.get("method").upper(),
+ timeout=self.params.get("timeout"),
+ use_proxy=self.params.get("use_proxy"),
+ )
+
+ self.response = info.get("msg")
+ self.status = info.get("status")
+
+ # Handle APIC response
+ if info.get("status") != 200:
+ try:
+ # APIC error
+ self.response_json(info["body"])
+ self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error)
+ except KeyError:
+ # Connection error
+ self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info)
+
+ self.response_json(resp.read())
+
+ def query(self, path):
+ """Perform a query with no payload"""
+
+ self.path = path
+
+ if self.params.get("port") is not None:
+ self.url = "%(protocol)s://%(host)s:%(port)s/" % self.params + path.lstrip("/")
+ else:
+ self.url = "%(protocol)s://%(host)s/" % self.params + path.lstrip("/")
+
+ # Sign and encode request as to APIC's wishes
+ if self.params.get("private_key"):
+ self.cert_auth(path=path, method="GET")
+
+ # Perform request
+ resp, query = fetch_url(
+ self.module,
+ self.url,
+ data=None,
+ headers=self.headers,
+ method="GET",
+ timeout=self.params.get("timeout"),
+ use_proxy=self.params.get("use_proxy"),
+ )
+
+ # Handle APIC response
+ if query.get("status") != 200:
+ self.response = query.get("msg")
+ self.status = query.get("status")
+ try:
+ # APIC error
+ self.response_json(query["body"])
+ self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error)
+ except KeyError:
+ # Connection error
+ self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % query)
+
+ query = json.loads(resp.read())
+
+ return json.dumps(query.get("imdata"), sort_keys=True, indent=2) + "\n"
+
+ def request_diff(self, path, payload=None):
+ """Perform a request, including a proper diff output"""
+ self.result["diff"] = dict()
+ self.result["diff"]["before"] = self.query(path)
+ self.request(path, payload=payload)
+ # TODO: Check if we can use the request output for the 'after' diff
+ self.result["diff"]["after"] = self.query(path)
+
+ if self.result.get("diff", {}).get("before") != self.result.get("diff", {}).get("after"):
+ self.result["changed"] = True
+
+ # TODO: This could be designed to update existing keys
+ def update_qs(self, params):
+ """Append key-value pairs to self.filter_string"""
+ accepted_params = dict((k, v) for (k, v) in params.items() if v is not None)
+ if accepted_params:
+ if self.filter_string:
+ self.filter_string += "&"
+ else:
+ self.filter_string = "?"
+ self.filter_string += "&".join(["%s=%s" % (k, v) for (k, v) in accepted_params.items()])
+
+ # TODO: This could be designed to accept multiple obj_classes and keys
+ def build_filter(self, obj_class, params):
+ """Build an APIC filter based on obj_class and key-value pairs"""
+ accepted_params = dict((k, v) for (k, v) in params.items() if v is not None)
+ if len(accepted_params) == 1:
+ return ",".join('eq({0}.{1},"{2}")'.format(obj_class, k, v) for (k, v) in accepted_params.items())
+ elif len(accepted_params) > 1:
+ return "and(" + ",".join(['eq({0}.{1},"{2}")'.format(obj_class, k, v) for (k, v) in accepted_params.items()]) + ")"
+
+ def _deep_url_path_builder(self, obj):
+ target_class = obj.get("target_class")
+ target_filter = obj.get("target_filter")
+ subtree_class = obj.get("subtree_class")
+ subtree_filter = obj.get("subtree_filter")
+ object_rn = obj.get("object_rn")
+ mo = obj.get("module_object")
+ add_subtree_filter = obj.get("add_subtree_filter")
+ add_target_filter = obj.get("add_target_filter")
+
+ if self.module.params.get("state") in ("absent", "present") and mo is not None:
+ self.path = "api/mo/uni/{0}.json".format(object_rn)
+ self.update_qs({"rsp-prop-include": "config-only"})
+
+ else:
+ # State is 'query'
+ if object_rn is not None:
+ # Query for a specific object in the module's class
+ self.path = "api/mo/uni/{0}.json".format(object_rn)
+ else:
+ self.path = "api/class/{0}.json".format(target_class)
+
+ if add_target_filter:
+ self.update_qs({"query-target-filter": self.build_filter(target_class, target_filter)})
+
+ if add_subtree_filter:
+ self.update_qs({"rsp-subtree-filter": self.build_filter(subtree_class, subtree_filter)})
+
+ if self.params.get("port") is not None:
+ self.url = "{protocol}://{host}:{port}/{path}".format(path=self.path, **self.module.params)
+
+ else:
+ self.url = "{protocol}://{host}/{path}".format(path=self.path, **self.module.params)
+
+ if self.child_classes:
+ self.update_qs(
+ {
+ "rsp-subtree": "full",
+ "rsp-subtree-class": ",".join(sorted(self.child_classes)),
+ }
+ )
+
+ def _deep_url_parent_object(self, parent_objects, parent_class):
+
+ for parent_object in parent_objects:
+ if parent_object.get("aci_class") is parent_class:
+ return parent_object
+
+ return None
+
+ def construct_deep_url(self, target_object, parent_objects=None, child_classes=None):
+ """
+ This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC.
+
+ :param target_object: The target class dictionary containing parent_class, aci_class, aci_rn, target_filter, and module_object keys.
+ :param parent_objects: The parent class list of dictionaries containing parent_class, aci_class, aci_rn, target_filter, and module_object keys.
+ :param child_classes: The list of child classes that the module supports along with the object.
+ :type target_object: dict
+ :type parent_objects: list[dict]
+ :type child_classes: list[string]
+ :return: The path and filter_string needed to build the full URL.
+ """
+
+ self.filter_string = ""
+ rn_builder = None
+ subtree_classes = None
+ add_subtree_filter = False
+ add_target_filter = False
+ has_target_query = False
+ has_target_query_compare = False
+ has_target_query_difference = False
+ has_target_query_called = False
+
+ if child_classes is None:
+ self.child_classes = set()
+ else:
+ self.child_classes = set(child_classes)
+
+ target_parent_class = target_object.get("parent_class")
+ target_class = target_object.get("aci_class")
+ target_rn = target_object.get("aci_rn")
+ target_filter = target_object.get("target_filter")
+ target_module_object = target_object.get("module_object")
+
+ url_path_object = dict(
+ target_class=target_class,
+ target_filter=target_filter,
+ subtree_class=target_class,
+ subtree_filter=target_filter,
+ module_object=target_module_object,
+ )
+
+ if target_module_object is not None:
+ rn_builder = target_rn
+ else:
+ has_target_query = True
+ has_target_query_compare = True
+
+ if parent_objects is not None:
+ current_parent_class = target_parent_class
+ has_parent_query_compare = False
+ has_parent_query_difference = False
+ is_first_parent = True
+ is_single_parent = None
+ search_classes = set()
+
+ while current_parent_class != "uni":
+ parent_object = self._deep_url_parent_object(parent_objects=parent_objects, parent_class=current_parent_class)
+
+ if parent_object is not None:
+ parent_parent_class = parent_object.get("parent_class")
+ parent_class = parent_object.get("aci_class")
+ parent_rn = parent_object.get("aci_rn")
+ parent_filter = parent_object.get("target_filter")
+ parent_module_object = parent_object.get("module_object")
+
+ if is_first_parent:
+ is_single_parent = True
+ else:
+ is_single_parent = False
+ is_first_parent = False
+
+ if parent_parent_class != "uni":
+ search_classes.add(parent_class)
+
+ if parent_module_object is not None:
+ if rn_builder is not None:
+ rn_builder = "{0}/{1}".format(parent_rn, rn_builder)
+ else:
+ rn_builder = parent_rn
+
+ url_path_object["target_class"] = parent_class
+ url_path_object["target_filter"] = parent_filter
+
+ has_target_query = False
+ else:
+ rn_builder = None
+ subtree_classes = search_classes
+
+ has_target_query = True
+ if is_single_parent:
+ has_parent_query_compare = True
+
+ current_parent_class = parent_parent_class
+ else:
+ raise ValueError("Reference error for parent_class '{0}'. Each parent_class must reference a valid object".format(current_parent_class))
+
+ if not has_target_query_difference and not has_target_query_called:
+ if has_target_query is not has_target_query_compare:
+ has_target_query_difference = True
+ else:
+ if not has_parent_query_difference and has_target_query is not has_parent_query_compare:
+ has_parent_query_difference = True
+ has_target_query_called = True
+
+ if not has_parent_query_difference and has_parent_query_compare and target_module_object is not None:
+ add_target_filter = True
+
+ elif has_parent_query_difference and target_module_object is not None:
+ add_subtree_filter = True
+ self.child_classes.add(target_class)
+
+ if has_target_query:
+ add_target_filter = True
+
+ elif has_parent_query_difference and not has_target_query and target_module_object is None:
+ self.child_classes.add(target_class)
+ self.child_classes.update(subtree_classes)
+
+ elif not has_parent_query_difference and not has_target_query and target_module_object is None:
+ self.child_classes.add(target_class)
+
+ elif not has_target_query and is_single_parent and target_module_object is None:
+ self.child_classes.add(target_class)
+
+ url_path_object["object_rn"] = rn_builder
+ url_path_object["add_subtree_filter"] = add_subtree_filter
+ url_path_object["add_target_filter"] = add_target_filter
+
+ self._deep_url_path_builder(url_path_object)
+
+ def construct_url(
+ self,
+ root_class,
+ subclass_1=None,
+ subclass_2=None,
+ subclass_3=None,
+ subclass_4=None,
+ subclass_5=None,
+ child_classes=None,
+ config_only=True,
+ ):
+
+ """
+ This method is used to retrieve the appropriate URL path and filter_string to make the request to the APIC.
+
+ :param root_class: The top-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys.
+ :param sublass_1: The second-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys.
+ :param sublass_2: The third-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys.
+ :param sublass_3: The fourth-level class dictionary containing aci_class, aci_rn, target_filter, and module_object keys.
+ :param child_classes: The list of child classes that the module supports along with the object.
+ :type root_class: dict
+ :type subclass_1: dict
+ :type subclass_2: dict
+ :type subclass_3: dict
+ :type subclass_4: dict
+ :type subclass_5: dict
+ :type child_classes: list
+ :return: The path and filter_string needed to build the full URL.
+ """
+ self.filter_string = ""
+
+ if child_classes is None:
+ self.child_classes = set()
+ else:
+ self.child_classes = set(child_classes)
+
+ if subclass_5 is not None:
+ self._construct_url_6(
+ root_class,
+ subclass_1,
+ subclass_2,
+ subclass_3,
+ subclass_4,
+ subclass_5,
+ config_only,
+ )
+ elif subclass_4 is not None:
+ self._construct_url_5(root_class, subclass_1, subclass_2, subclass_3, subclass_4, config_only)
+ elif subclass_3 is not None:
+ self._construct_url_4(root_class, subclass_1, subclass_2, subclass_3, config_only)
+ elif subclass_2 is not None:
+ self._construct_url_3(root_class, subclass_1, subclass_2, config_only)
+ elif subclass_1 is not None:
+ self._construct_url_2(root_class, subclass_1, config_only)
+ else:
+ self._construct_url_1(root_class, config_only)
+
+ if self.params.get("port") is not None:
+ self.url = "{protocol}://{host}:{port}/{path}".format(path=self.path, **self.module.params)
+ else:
+ self.url = "{protocol}://{host}/{path}".format(path=self.path, **self.module.params)
+
+ if self.child_classes:
+ # Append child_classes to filter_string if filter string is empty
+ self.update_qs(
+ {
+ "rsp-subtree": "full",
+ "rsp-subtree-class": ",".join(sorted(self.child_classes)),
+ }
+ )
+
+ def _construct_url_1(self, obj, config_only=True):
+ """
+ This method is used by construct_url when the object is the top-level class.
+ """
+ obj_class = obj.get("aci_class")
+ obj_rn = obj.get("aci_rn")
+ obj_filter = obj.get("target_filter")
+ mo = obj.get("module_object")
+
+ if self.module.params.get("state") in ("absent", "present"):
+ # State is absent or present
+ self.path = "api/mo/uni/{0}.json".format(obj_rn)
+ if config_only:
+ self.update_qs({"rsp-prop-include": "config-only"})
+ self.obj_filter = obj_filter
+ elif mo is None:
+ # Query for all objects of the module's class (filter by properties)
+ self.path = "api/class/{0}.json".format(obj_class)
+ if obj_filter is not None:
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ else:
+ # Query for a specific object in the module's class
+ self.path = "api/mo/uni/{0}.json".format(obj_rn)
+
+ def _construct_url_2(self, parent, obj, config_only=True):
+ """
+ This method is used by construct_url when the object is the second-level class.
+ """
+ parent_rn = parent.get("aci_rn")
+ parent_obj = parent.get("module_object")
+ obj_class = obj.get("aci_class")
+ obj_rn = obj.get("aci_rn")
+ obj_filter = obj.get("target_filter")
+ mo = obj.get("module_object")
+
+ if self.module.params.get("state") in ("absent", "present"):
+ # State is absent or present
+ self.path = "api/mo/uni/{0}/{1}.json".format(parent_rn, obj_rn)
+ if config_only:
+ self.update_qs({"rsp-prop-include": "config-only"})
+ self.obj_filter = obj_filter
+ elif parent_obj is None and mo is None:
+ # Query for all objects of the module's class
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif parent_obj is None: # mo is known
+ # Query for all objects of the module's class that match the provided ID value
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif mo is None: # parent_obj is known
+ # Query for all object's of the module's class that belong to a specific parent object
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}.json".format(parent_rn)
+ else:
+ # Query for specific object in the module's class
+ self.path = "api/mo/uni/{0}/{1}.json".format(parent_rn, obj_rn)
+
+ def _construct_url_3(self, root, parent, obj, config_only=True):
+ """
+ This method is used by construct_url when the object is the third-level class.
+ """
+ root_rn = root.get("aci_rn")
+ root_obj = root.get("module_object")
+ parent_class = parent.get("aci_class")
+ parent_rn = parent.get("aci_rn")
+ parent_filter = parent.get("target_filter")
+ parent_obj = parent.get("module_object")
+ obj_class = obj.get("aci_class")
+ obj_rn = obj.get("aci_rn")
+ obj_filter = obj.get("target_filter")
+ mo = obj.get("module_object")
+
+ if self.module.params.get("state") in ("absent", "present"):
+ # State is absent or present
+ self.path = "api/mo/uni/{0}/{1}/{2}.json".format(root_rn, parent_rn, obj_rn)
+ if config_only:
+ self.update_qs({"rsp-prop-include": "config-only"})
+ self.obj_filter = obj_filter
+ elif root_obj is None and parent_obj is None and mo is None:
+ # Query for all objects of the module's class
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif root_obj is None and parent_obj is None: # mo is known
+ # Query for all objects of the module's class matching the provided ID value of the object
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif root_obj is None and mo is None: # parent_obj is known
+ # Query for all objects of the module's class that belong to any parent class
+ # matching the provided ID value for the parent object
+ self.child_classes.add(obj_class)
+ self.path = "api/class/{0}.json".format(parent_class)
+ self.update_qs({"query-target-filter": self.build_filter(parent_class, parent_filter)})
+ elif parent_obj is None and mo is None: # root_obj is known
+ # Query for all objects of the module's class that belong to a specific root object
+ self.child_classes.update([parent_class, obj_class])
+ self.path = "api/mo/uni/{0}.json".format(root_rn)
+ # NOTE: No need to select by root_filter
+ # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)})
+ elif root_obj is None: # mo and parent_obj are known
+ # Query for all objects of the module's class that belong to any parent class
+ # matching the provided ID values for both object and parent object
+ self.child_classes.add(obj_class)
+ self.path = "api/class/{0}.json".format(parent_class)
+ self.update_qs({"query-target-filter": self.build_filter(parent_class, parent_filter)})
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif parent_obj is None: # mo and root_obj are known
+ # Query for all objects of the module's class that match the provided ID value and belong to a specific root object
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}.json".format(root_rn)
+ # NOTE: No need to select by root_filter
+ # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)})
+ # TODO: Filter by parent_filter and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif mo is None: # root_obj and parent_obj are known
+ # Query for all objects of the module's class that belong to a specific parent object
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}.json".format(root_rn, parent_rn)
+ # NOTE: No need to select by parent_filter
+ # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)})
+ else:
+ # Query for a specific object of the module's class
+ self.path = "api/mo/uni/{0}/{1}/{2}.json".format(root_rn, parent_rn, obj_rn)
+
+ def _construct_url_4(self, root, sec, parent, obj, config_only=True):
+ """
+ This method is used by construct_url when the object is the fourth-level class.
+ """
+ root_rn = root.get("aci_rn")
+ root_obj = root.get("module_object")
+ sec_rn = sec.get("aci_rn")
+ sec_obj = sec.get("module_object")
+ parent_rn = parent.get("aci_rn")
+ parent_obj = parent.get("module_object")
+ obj_class = obj.get("aci_class")
+ obj_rn = obj.get("aci_rn")
+ obj_filter = obj.get("target_filter")
+ mo = obj.get("module_object")
+
+ if self.child_classes is None:
+ self.child_classes = [obj_class]
+
+ if self.module.params.get("state") in ("absent", "present"):
+ # State is absent or present
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}.json".format(root_rn, sec_rn, parent_rn, obj_rn)
+ if config_only:
+ self.update_qs({"rsp-prop-include": "config-only"})
+ self.obj_filter = obj_filter
+ # TODO: Add all missing cases
+ elif root_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif sec_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}.json".format(root_rn)
+ # NOTE: No need to select by root_filter
+ # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)})
+ # TODO: Filter by sec_filter, parent and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif parent_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}.json".format(root_rn, sec_rn)
+ # NOTE: No need to select by sec_filter
+ # self.update_qs({'query-target-filter': self.build_filter(sec_class, sec_filter)})
+ # TODO: Filter by parent_filter and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif mo is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}/{2}.json".format(root_rn, sec_rn, parent_rn)
+ # NOTE: No need to select by parent_filter
+ # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)})
+ else:
+ # Query for a specific object of the module's class
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}.json".format(root_rn, sec_rn, parent_rn, obj_rn)
+
+ def _construct_url_5(self, root, ter, sec, parent, obj, config_only=True):
+ """
+ This method is used by construct_url when the object is the fourth-level class.
+ """
+
+ root_rn = root.get("aci_rn")
+ root_obj = root.get("module_object")
+ ter_rn = ter.get("aci_rn")
+ ter_obj = ter.get("module_object")
+ sec_rn = sec.get("aci_rn")
+ sec_obj = sec.get("module_object")
+ parent_rn = parent.get("aci_rn")
+ parent_obj = parent.get("module_object")
+ obj_class = obj.get("aci_class")
+ obj_rn = obj.get("aci_rn")
+ obj_filter = obj.get("target_filter")
+ mo = obj.get("module_object")
+
+ if self.child_classes is None:
+ self.child_classes = [obj_class]
+
+ if self.module.params.get("state") in ("absent", "present"):
+ # State is absent or present
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}/{4}.json".format(root_rn, ter_rn, sec_rn, parent_rn, obj_rn)
+ if config_only:
+ self.update_qs({"rsp-prop-include": "config-only"})
+ self.obj_filter = obj_filter
+ # TODO: Add all missing cases
+ elif root_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif ter_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}.json".format(root_rn)
+ # NOTE: No need to select by root_filter
+ # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)})
+ # TODO: Filter by ter_filter, parent and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif sec_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}.json".format(root_rn, ter_rn)
+ # NOTE: No need to select by ter_filter
+ # self.update_qs({'query-target-filter': self.build_filter(ter_class, ter_filter)})
+ # TODO: Filter by sec_filter, parent and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif parent_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}/{2}.json".format(root_rn, ter_rn, sec_rn)
+ # NOTE: No need to select by sec_filter
+ # self.update_qs({'query-target-filter': self.build_filter(sec_class, sec_filter)})
+ # TODO: Filter by parent_filter and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif mo is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}.json".format(root_rn, ter_rn, sec_rn, parent_rn)
+ # NOTE: No need to select by parent_filter
+ # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)})
+ else:
+ # Query for a specific object of the module's class
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}/{4}.json".format(root_rn, ter_rn, sec_rn, parent_rn, obj_rn)
+
+ def _construct_url_6(self, root, quad, ter, sec, parent, obj, config_only=True):
+ """
+ This method is used by construct_url when the object is the fourth-level class.
+ """
+ root_rn = root.get("aci_rn")
+ root_obj = root.get("module_object")
+ quad_rn = quad.get("aci_rn")
+ quad_obj = quad.get("module_object")
+ ter_rn = ter.get("aci_rn")
+ ter_obj = ter.get("module_object")
+ sec_rn = sec.get("aci_rn")
+ sec_obj = sec.get("module_object")
+ parent_rn = parent.get("aci_rn")
+ parent_obj = parent.get("module_object")
+ obj_class = obj.get("aci_class")
+ obj_rn = obj.get("aci_rn")
+ obj_filter = obj.get("target_filter")
+ mo = obj.get("module_object")
+
+ if self.child_classes is None:
+ self.child_classes = [obj_class]
+
+ if self.module.params.get("state") in ("absent", "present"):
+ # State is absent or present
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}/{4}/{5}.json".format(root_rn, quad_rn, ter_rn, sec_rn, parent_rn, obj_rn)
+ if config_only:
+ self.update_qs({"rsp-prop-include": "config-only"})
+ self.obj_filter = obj_filter
+ # TODO: Add all missing cases
+ elif root_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/class/{0}.json".format(obj_class)
+ self.update_qs({"query-target-filter": self.build_filter(obj_class, obj_filter)})
+ elif quad_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}.json".format(root_rn)
+ # NOTE: No need to select by root_filter
+ # self.update_qs({'query-target-filter': self.build_filter(root_class, root_filter)})
+ # TODO: Filter by quad_filter, parent and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif ter_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}.json".format(root_rn, quad_rn)
+ # NOTE: No need to select by quad_filter
+ # self.update_qs({'query-target-filter': self.build_filter(quad_class, quad_filter)})
+ # TODO: Filter by ter_filter, parent and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif sec_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}/{2}.json".format(root_rn, quad_rn, ter_rn)
+ # NOTE: No need to select by ter_filter
+ # self.update_qs({'query-target-filter': self.build_filter(ter_class, ter_filter)})
+ # TODO: Filter by sec_filter, parent and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif parent_obj is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}.json".format(root_rn, quad_rn, ter_rn, sec_rn)
+ # NOTE: No need to select by sec_filter
+ # self.update_qs({'query-target-filter': self.build_filter(sec_class, sec_filter)})
+ # TODO: Filter by parent_filter and obj_filter
+ self.update_qs({"rsp-subtree-filter": self.build_filter(obj_class, obj_filter)})
+ elif mo is None:
+ self.child_classes.add(obj_class)
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}/{4}.json".format(root_rn, quad_rn, ter_rn, sec_rn, parent_rn)
+ # NOTE: No need to select by parent_filter
+ # self.update_qs({'query-target-filter': self.build_filter(parent_class, parent_filter)})
+ else:
+ # Query for a specific object of the module's class
+ self.path = "api/mo/uni/{0}/{1}/{2}/{3}/{4}/{5}.json".format(root_rn, quad_rn, ter_rn, sec_rn, parent_rn, obj_rn)
+
+ def delete_config(self):
+ """
+ This method is used to handle the logic when the modules state is equal to absent. The method only pushes a change if
+ the object exists, and if check_mode is False. A successful change will mark the module as changed.
+ """
+ self.proposed = dict()
+
+ if not self.existing:
+ return
+
+ elif not self.module.check_mode:
+ # Sign and encode request as to APIC's wishes
+ if self.params["private_key"]:
+ self.cert_auth(method="DELETE")
+
+ resp, info = fetch_url(
+ self.module,
+ self.url,
+ headers=self.headers,
+ method="DELETE",
+ timeout=self.params.get("timeout"),
+ use_proxy=self.params.get("use_proxy"),
+ )
+
+ self.response = info.get("msg")
+ self.status = info.get("status")
+ self.method = "DELETE"
+
+ # Handle APIC response
+ if info.get("status") == 200:
+ self.result["changed"] = True
+ self.response_json(resp.read())
+ else:
+ try:
+ # APIC error
+ self.response_json(info["body"])
+ self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error)
+ except KeyError:
+ # Connection error
+ self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info)
+ else:
+ self.result["changed"] = True
+ self.method = "DELETE"
+
+ def get_diff(self, aci_class):
+ """
+ This method is used to get the difference between the proposed and existing configurations. Each module
+ should call the get_existing method before this method, and add the proposed config to the module results
+ using the module's config parameters. The new config will added to the self.result dictionary.
+
+ :param aci_class: Type str.
+ This is the root dictionary key for the MO's configuration body, or the ACI class of the MO.
+ """
+ proposed_config = self.proposed[aci_class]["attributes"]
+ if self.existing:
+ existing_config = self.existing[0][aci_class]["attributes"]
+ config = {}
+
+ # values are strings, so any diff between proposed and existing can be a straight replace
+ for key, value in proposed_config.items():
+ existing_field = existing_config.get(key)
+ if value != existing_field:
+ config[key] = value
+
+ # add name back to config only if the configs do not match
+ if config:
+ # TODO: If URLs are built with the object's name, then we should be able to leave off adding the name back
+ config = {aci_class: {"attributes": config}}
+
+ # check for updates to child configs and update new config dictionary
+ children = self.get_diff_children(aci_class)
+
+ if children and config:
+ config[aci_class].update({"children": children})
+ elif children:
+ config = {aci_class: {"attributes": {}, "children": children}}
+
+ else:
+ config = self.proposed
+ self.config = config
+
+ @staticmethod
+ def get_diff_child(child_class, proposed_child, existing_child):
+ """
+ This method is used to get the difference between a proposed and existing child configs. The get_nested_config()
+ method should be used to return the proposed and existing config portions of child.
+
+ :param child_class: Type str.
+ The root class (dict key) for the child dictionary.
+ :param proposed_child: Type dict.
+ The config portion of the proposed child dictionary.
+ :param existing_child: Type dict.
+ The config portion of the existing child dictionary.
+ :return: The child config with only values that are updated. If the proposed dictionary has no updates to make
+ to what exists on the APIC, then None is returned.
+ """
+ update_config = {child_class: {"attributes": {}}}
+ for key, value in proposed_child.items():
+ existing_field = existing_child.get(key)
+ if value != existing_field:
+ update_config[child_class]["attributes"][key] = value
+
+ if not update_config[child_class]["attributes"]:
+ return None
+
+ return update_config
+
+ def get_diff_children(self, aci_class, proposed_obj=None, existing_obj=None):
+ """
+ This method is used to retrieve the updated child configs by comparing the proposed children configs
+ against the objects existing children configs.
+
+ :param aci_class: Type str.
+ This is the root dictionary key for the MO's configuration body, or the ACI class of the MO.
+ :return: The list of updated child config dictionaries. None is returned if there are no changes to the child
+ configurations.
+ """
+ if proposed_obj is None:
+ proposed_children = self.proposed[aci_class].get("children")
+ else:
+ proposed_children = proposed_obj
+
+ if proposed_children:
+ child_updates = []
+ if existing_obj is None:
+ existing_children = self.existing[0][aci_class].get("children", [])
+ else:
+ existing_children = existing_obj
+
+ # Loop through proposed child configs and compare against existing child configuration
+ for child in proposed_children:
+ child_class, proposed_child, existing_child = self.get_nested_config(child, existing_children)
+ (
+ proposed_child_children,
+ existing_child_children,
+ ) = self.get_nested_children(child, existing_children)
+
+ if existing_child is None:
+ child_update = child
+ else:
+ child_update = self.get_diff_child(child_class, proposed_child, existing_child)
+ if proposed_child_children:
+ child_update_children = self.get_diff_children(aci_class, proposed_child_children, existing_child_children)
+
+ if child_update_children:
+ child_update = child
+
+ # Update list of updated child configs only if the child config is different than what exists
+ if child_update:
+ child_updates.append(child_update)
+ else:
+ return None
+
+ return child_updates
+
+ def get_existing(self):
+ """
+ This method is used to get the existing object(s) based on the path specified in the module. Each module should
+ build the URL so that if the object's name is supplied, then it will retrieve the configuration for that particular
+ object, but if no name is supplied, then it will retrieve all MOs for the class. Following this method will ensure
+ that this method can be used to supply the existing configuration when using the get_diff method. The response, status,
+ and existing configuration will be added to the self.result dictionary.
+ """
+ uri = self.url + self.filter_string
+
+ # Sign and encode request as to APIC's wishes
+ if self.params.get("private_key"):
+ self.cert_auth(path=self.path + self.filter_string, method="GET")
+
+ resp, info = fetch_url(
+ self.module,
+ uri,
+ headers=self.headers,
+ method="GET",
+ timeout=self.params.get("timeout"),
+ use_proxy=self.params.get("use_proxy"),
+ )
+ self.response = info.get("msg")
+ self.status = info.get("status")
+ self.method = "GET"
+
+ # Handle APIC response
+ if info.get("status") == 200:
+ self.existing = json.loads(resp.read())["imdata"]
+ else:
+ try:
+ # APIC error
+ self.response_json(info["body"])
+ self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error)
+ except KeyError:
+ # Connection error
+ self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info)
+
+ @staticmethod
+ def get_nested_config(proposed_child, existing_children):
+ """
+ This method is used for stiping off the outer layers of the child dictionaries so only the configuration
+ key, value pairs are returned.
+
+ :param proposed_child: Type dict.
+ The dictionary that represents the child config.
+ :param existing_children: Type list.
+ The list of existing child config dictionaries.
+ :return: The child's class as str (root config dict key), the child's proposed config dict, and the child's
+ existing configuration dict.
+ """
+ for key in proposed_child.keys():
+ child_class = key
+ proposed_config = proposed_child[key]["attributes"]
+ existing_config = None
+
+ # FIXME: Design causes issues for repeated child_classes
+ # get existing dictionary from the list of existing to use for comparison
+ for child in existing_children:
+ if child.get(child_class):
+ existing_config = child[key]["attributes"]
+ # NOTE: This is an ugly fix
+ # Return the one that is a subset match
+ if set(proposed_config.items()).issubset(set(existing_config.items())):
+ break
+ existing_config = None
+
+ return child_class, proposed_config, existing_config
+
+ @staticmethod
+ def get_nested_children(proposed_child, existing_children):
+ """
+ This method is used for stiping off the outer layers of the child dictionaries so only the children are returned.
+
+ :param proposed_child: Type dict.
+ The dictionary that represents the child config.
+ :param existing_children: Type list.
+ The list of existing child config dictionaries.
+ :return: The child's class as str (root config dict key), the child's proposed children as a list and the child's
+ existing children as a list.
+ """
+ for key in proposed_child.keys():
+ child_class = key
+ proposed_config = proposed_child[key]["attributes"]
+ existing_config = None
+ proposed_children = proposed_child[key].get("children")
+ existing_child_children = None
+
+ # FIXME: Design causes issues for repeated child_classes
+ # get existing dictionary from the list of existing to use for comparison
+ for child in existing_children:
+ if child.get(child_class):
+ existing_config = child[key]["attributes"]
+ existing_child_children = child[key].get("children")
+ # NOTE: This is an ugly fix
+ # Return the one that is a subset match
+ if set(proposed_config.items()).issubset(set(existing_config.items())):
+ break
+ existing_child_children = None
+ existing_config = None
+
+ return proposed_children, existing_child_children
+
+ def payload(self, aci_class, class_config, child_configs=None):
+ """
+ This method is used to dynamically build the proposed configuration dictionary from the config related parameters
+ passed into the module. All values that were not passed values from the playbook task will be removed so as to not
+ inadvertently change configurations.
+
+ :param aci_class: Type str
+ This is the root dictionary key for the MO's configuration body, or the ACI class of the MO.
+ :param class_config: Type dict
+ This is the configuration of the MO using the dictionary keys expected by the API
+ :param child_configs: Type list
+ This is a list of child dictionaries associated with the MOs config. The list should only
+ include child objects that are used to associate two MOs together. Children that represent
+ MOs should have their own module.
+ """
+ proposed = dict((k, str(v)) for k, v in class_config.items() if v is not None)
+ if self.params.get("annotation") is not None:
+ proposed["annotation"] = self.params.get("annotation")
+ if self.params.get("owner_key") is not None:
+ proposed["ownerKey"] = self.params.get("owner_key")
+ if self.params.get("owner_tag") is not None:
+ proposed["ownerTag"] = self.params.get("owner_tag")
+ self.proposed = {aci_class: {"attributes": proposed}}
+
+ # add child objects to proposed
+ if child_configs:
+ children = []
+ for child in child_configs:
+ child_copy = deepcopy(child)
+ has_value = False
+ for root_key in child_copy.keys():
+ for final_keys, values in child_copy[root_key]["attributes"].items():
+ if values is None:
+ child[root_key]["attributes"].pop(final_keys)
+ else:
+ child[root_key]["attributes"][final_keys] = str(values)
+ has_value = True
+ if has_value:
+ children.append(child)
+
+ if children:
+ self.proposed[aci_class].update(dict(children=children))
+
+ def post_config(self):
+ """
+ This method is used to handle the logic when the modules state is equal to present. The method only pushes a change if
+ the object has differences than what exists on the APIC, and if check_mode is False. A successful change will mark the
+ module as changed.
+ """
+ if not self.config:
+ return
+ elif not self.module.check_mode:
+ # Sign and encode request as to APIC's wishes
+ if self.params.get("private_key"):
+ self.cert_auth(method="POST", payload=json.dumps(self.config))
+
+ resp, info = fetch_url(
+ self.module,
+ self.url,
+ data=json.dumps(self.config),
+ headers=self.headers,
+ method="POST",
+ timeout=self.params.get("timeout"),
+ use_proxy=self.params.get("use_proxy"),
+ )
+
+ self.response = info.get("msg")
+ self.status = info.get("status")
+ self.method = "POST"
+
+ # Handle APIC response
+ if info.get("status") == 200:
+ self.result["changed"] = True
+ self.response_json(resp.read())
+ else:
+ try:
+ # APIC error
+ self.response_json(info["body"])
+ self.fail_json(msg="APIC Error %(code)s: %(text)s" % self.error)
+ except KeyError:
+ # Connection error
+ self.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info)
+ else:
+ self.result["changed"] = True
+ self.method = "POST"
+
+ def exit_json(self, filter_existing=None, **kwargs):
+ """
+ :param filter_existing: tuple consisting of the function at (index 0) and the args at (index 1)
+ CAUTION: the function should always take in self.existing in its first parameter
+ :param kwargs: kwargs to be passed to ansible module exit_json()
+ filter_existing is not passed via kwargs since it cant handle function type and should not be exposed to user
+ """
+
+ if "state" in self.params:
+ if self.params.get("state") in ("absent", "present"):
+ if self.params.get("output_level") in ("debug", "info"):
+ self.result["previous"] = self.existing if not filter_existing else filter_existing[0](self.existing, filter_existing[1])
+
+ # Return the gory details when we need it
+ if self.params.get("output_level") == "debug":
+ 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 self.stdout:
+ self.result["stdout"] = self.stdout
+
+ if "state" in self.params:
+ self.original = self.existing
+ if self.params.get("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 if not filter_existing else filter_existing[0](self.existing, filter_existing[1])
+
+ if self.params.get("output_level") in ("debug", "info"):
+ self.result["sent"] = self.config
+ self.result["proposed"] = self.proposed
+
+ self.dump_json()
+ self.result.update(**kwargs)
+ self.module.exit_json(**self.result)
+
+ def fail_json(self, msg, **kwargs):
+
+ # Return error information, if we have it
+ if self.error.get("code") is not None and self.error.get("text") is not None:
+ self.result["error"] = self.error
+
+ if "state" in self.params:
+ if self.params.get("state") in ("absent", "present"):
+ if self.params.get("output_level") in ("debug", "info"):
+ self.result["previous"] = self.existing
+ if self.stdout:
+ self.result["stdout"] = self.stdout
+
+ # Return the gory details when we need it
+ if self.params.get("output_level") == "debug":
+ if self.imdata is not None:
+ self.result["imdata"] = self.imdata
+ self.result["totalCount"] = self.totalCount
+
+ if self.params.get("output_level") == "debug":
+ if self.url is not None:
+ 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.get("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)
+
+ def dump_json(self):
+ if self.params.get("state") in ("absent", "present"):
+ dn_path = (self.url).split("/mo/")[-1]
+ if dn_path[-5:] == ".json":
+ dn_path = dn_path[:-5]
+ mo = {}
+ if self.proposed:
+ mo = self.proposed
+ for aci_class in mo:
+ mo[aci_class]["attributes"]["dn"] = dn_path
+ if self.obj_filter is not None:
+ if "tDn" in self.obj_filter:
+ mo[aci_class]["attributes"]["tDn"] = self.obj_filter["tDn"]
+
+ elif self.params.get("state") == "absent" and self.existing:
+ for aci_class in self.existing[0]:
+ mo[aci_class] = dict(attributes=dict(dn=dn_path, status="deleted"))
+
+ self.result["mo"] = mo
+ output_path = self.params.get("output_path")
+ if output_path is not None:
+ with open(output_path, "a") as output_file:
+ if self.result.get("changed") is True:
+ json.dump([mo], output_file)
diff --git a/ansible_collections/cisco/aci/plugins/modules/__init__.py b/ansible_collections/cisco/aci/plugins/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/__init__.py
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_custom_privilege.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_custom_privilege.py
new file mode 100644
index 00000000..a7735e24
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_custom_privilege.py
@@ -0,0 +1,366 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_custom_privilege
+short_description: Manage AAA RBAC Custom Privileges (aaa:RbacClassPriv)
+description:
+- Manage AAA Custom Privileges with RBAC Rules on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - Name of the object class for which you are configuring access.
+ type: str
+ aliases: [ custom_privilege_name ]
+ description:
+ description:
+ - Description of the AAA custom privilege.
+ type: str
+ aliases: [ descr ]
+ write_privilege:
+ description:
+ - Name of the custom privilege that will include write access to objects of the class.
+ type: str
+ aliases: [ write_priv, w_priv ]
+ choices: [
+ custom-privilege-1,
+ custom-privilege-2,
+ custom-privilege-3,
+ custom-privilege-4,
+ custom-privilege-5,
+ custom-privilege-6,
+ custom-privilege-7,
+ custom-privilege-8,
+ custom-privilege-9,
+ custom-privilege-10,
+ custom-privilege-11,
+ custom-privilege-12,
+ custom-privilege-13,
+ custom-privilege-14,
+ custom-privilege-15,
+ custom-privilege-16,
+ custom-privilege-17,
+ custom-privilege-18,
+ custom-privilege-19,
+ custom-privilege-20,
+ custom-privilege-21,
+ custom-privilege-22
+ ]
+ read_privilege:
+ description:
+ - Name of the custom privilege that will include read access to objects of the class.
+ type: str
+ aliases: [ read_priv, r_priv ]
+ choices: [
+ custom-privilege-1,
+ custom-privilege-2,
+ custom-privilege-3,
+ custom-privilege-4,
+ custom-privilege-5,
+ custom-privilege-6,
+ custom-privilege-7,
+ custom-privilege-8,
+ custom-privilege-9,
+ custom-privilege-10,
+ custom-privilege-11,
+ custom-privilege-12,
+ custom-privilege-13,
+ custom-privilege-14,
+ custom-privilege-15,
+ custom-privilege-16,
+ custom-privilege-17,
+ custom-privilege-18,
+ custom-privilege-19,
+ custom-privilege-20,
+ custom-privilege-21,
+ custom-privilege-22
+ ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+
+seealso:
+- module: cisco.aci.aci_aaa_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(aaa:Domain).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add a custom privilege
+ cisco.aci.aci_aaa_custom_privilege:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fabricPod
+ write_privilege: custom-privilege-1
+ read_privilege: custom-privilege-1
+ state: present
+ delegate_to: localhost
+
+- name: Add list of custom privileges
+ cisco.aci.aci_aaa_custom_privilege:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: "{{ item.name }}"
+ write_privilege: "{{ item.write_privilege }}"
+ read_privilege: "{{ item.read_privilege | default('') }}"
+ state: present
+ with_items:
+ - name: fvTenant
+ write_privilege: custom-privilege-2
+ read_privilege: custom-privilege-2
+ - name: aaaUser
+ write_privilege: custom-privilege-3
+ delegate_to: localhost
+
+- name: Query a custom privilege with name
+ cisco.aci.aci_aaa_custom_privilege:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fabricPod
+ state: query
+ delegate_to: localhost
+
+- name: Query all custom privileges
+ cisco.aci.aci_aaa_custom_privilege:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove a custom privilege
+ cisco.aci.aci_aaa_custom_privilege:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fabricPod
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: '?rsp-prop-include=config-only'
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
+
+CUSTOM_PRIVILEGES = [
+ "custom-privilege-1",
+ "custom-privilege-2",
+ "custom-privilege-3",
+ "custom-privilege-4",
+ "custom-privilege-5",
+ "custom-privilege-6",
+ "custom-privilege-7",
+ "custom-privilege-8",
+ "custom-privilege-9",
+ "custom-privilege-10",
+ "custom-privilege-11",
+ "custom-privilege-12",
+ "custom-privilege-13",
+ "custom-privilege-14",
+ "custom-privilege-15",
+ "custom-privilege-16",
+ "custom-privilege-17",
+ "custom-privilege-18",
+ "custom-privilege-19",
+ "custom-privilege-20",
+ "custom-privilege-21",
+ "custom-privilege-22",
+]
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ name=dict(type="str", aliases=["custom_privilege_name"]),
+ description=dict(type="str", aliases=["descr"]),
+ write_privilege=dict(
+ type="str",
+ aliases=["write_priv", "w_priv"],
+ choices=CUSTOM_PRIVILEGES,
+ ),
+ read_privilege=dict(
+ type="str",
+ aliases=["read_priv", "r_priv"],
+ choices=CUSTOM_PRIVILEGES,
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ w_priv = module.params.get("write_privilege")
+ r_priv = module.params.get("read_privilege")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(aci_class="aaaRbacClassPriv", aci_rn="rbacdb/rbacclpriv-{0}".format(name), module_object=name, target_filter=dict(name=name)),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="aaaRbacClassPriv",
+ class_config=dict(
+ name=name,
+ descr=description,
+ wPriv=w_priv,
+ rPriv=r_priv,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaRbacClassPriv")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_domain.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_domain.py
new file mode 100644
index 00000000..99104b01
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_domain.py
@@ -0,0 +1,280 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_domain
+short_description: Manage AAA domains (aaa:Domain)
+description:
+- Manage AAA domains on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the aaa domain.
+ type: str
+ aliases: [ aaa_domain ]
+ description:
+ description:
+ - Description of the aaa domain.
+ type: str
+ aliases: [ descr ]
+ restricted_rbac_domain:
+ description:
+ - C(True) to enable Restricted RBAC Domain on the aaa security domain.
+ type: bool
+ choices: [ false, true ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+
+seealso:
+- module: cisco.aci.aci_aaa_role
+- name: Manage AAA roles (aaa:Role)
+ description: More information about the AAA roles class B(aaa:Role).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add an aaa security domain
+ cisco.aci.aci_aaa_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: anstest_security_domain
+ description: "Anstest Sec Domain Descr"
+ state: present
+ delegate_to: localhost
+
+- name: Add list of aaa security domain
+ cisco.aci.aci_aaa_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: "{{ item.name }}"
+ description: "{{ item.description }}"
+ state: present
+ with_items:
+ - name: anstest1
+ description: "Anstest Sec Domain Descr 1"
+ - name: anstest2
+ description: "Anstest Sec Domain Descr 2"
+ delegate_to: localhost
+
+- name: Query an aaa security domain with name
+ cisco.aci.aci_aaa_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: anstest_security_domain
+ state: query
+ delegate_to: localhost
+
+- name: Query all aaa security domains
+ cisco.aci.aci_aaa_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove an aaa security domain
+ cisco.aci.aci_aaa_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: anstest_security_domain
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ name=dict(type="str", aliases=["aaa_domain"]),
+ description=dict(type="str", aliases=["descr"]),
+ restricted_rbac_domain=dict(type="bool", choices=[False, True]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ restricted_rbac_domain = module.params.get("restricted_rbac_domain")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="aaaDomain",
+ aci_rn="userext/domain-{0}".format(name),
+ module_object=name,
+ target_filter=dict(name=name),
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ restricted_rbac_domain_mapping = {False: "no", True: "yes"}
+ restricted_rbac_domain_state = restricted_rbac_domain_mapping.get(restricted_rbac_domain)
+ aci.payload(
+ aci_class="aaaDomain",
+ class_config=dict(
+ name=name,
+ descr=description,
+ restrictedRbacDomain=restricted_rbac_domain_state,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaDomain")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_role.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_role.py
new file mode 100644
index 00000000..147c6512
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_role.py
@@ -0,0 +1,380 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_role
+short_description: Manage AAA roles (aaa:Role)
+description:
+- Manage AAA roles on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the aaa role.
+ type: str
+ aliases: [ aaa_role ]
+ privileges:
+ description:
+ - The privilege(s) assigned to a role.
+ type: list
+ aliases: [ priv ]
+ elements: str
+ choices: [
+ admin,
+ aaa,
+ tenant-connectivity,
+ tenant-protocol,
+ vmm-policy,
+ tenant-ext-connectivity,
+ tenant-ext-protocol,
+ tenant-qos,
+ tenant-security,
+ tenant-network-profile,
+ tenant-epg,
+ fabric-connectivity,
+ fabric-protocol,
+ fabric-equipment,
+ access-connectivity,
+ access-protocol,
+ access-equipment,
+ access-qos,
+ nw-svc-params,
+ ops,
+ nw-svc-policy,
+ site-admin,
+ site-policy,
+ config-manager,
+ custom-privilege-1,
+ custom-privilege-2,
+ custom-privilege-3,
+ custom-privilege-4,
+ custom-privilege-5,
+ custom-privilege-6,
+ custom-privilege-7,
+ custom-privilege-8,
+ custom-privilege-9,
+ custom-privilege-10,
+ custom-privilege-11,
+ custom-privilege-12,
+ custom-privilege-13,
+ custom-privilege-14,
+ custom-privilege-15,
+ custom-privilege-16,
+ custom-privilege-17,
+ custom-privilege-18,
+ custom-privilege-19,
+ custom-privilege-20,
+ custom-privilege-21,
+ custom-privilege-22,
+ custom-port-privilege
+ ]
+ description:
+ description:
+ - Description of the aaa role.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+
+seealso:
+- module: cisco.aci.aci_aaa_domain
+- name: Manage AAA domains (aaa:Domain)
+ description: More information about the AAA domains class B(aaa:Domain).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+
+EXAMPLES = r"""
+- name: Add a aaa role
+ cisco.aci.aci_aaa_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: anstest
+ privileges: aaa
+ state: present
+ delegate_to: localhost
+
+- name: Add list of aaa roles
+ cisco.aci.aci_aaa_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: "{{ item.name }}"
+ privileges: "{{ item.privilege }}"
+ state: present
+ delegate_to: localhost
+ with_items:
+ - name: anstest1
+ privilege: site-admin
+ - name: anstest2
+ privilege: site-policy
+
+- name: Query a aaa role with name
+ cisco.aci.aci_aaa_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: anstest
+ state: query
+ delegate_to: localhost
+
+- name: Query all aaa roles
+ cisco.aci.aci_aaa_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove a aaa role with name
+ cisco.aci.aci_aaa_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: anstest
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
+
+PRIVILEGES = [
+ "admin",
+ "aaa",
+ "tenant-connectivity",
+ "tenant-protocol",
+ "vmm-policy",
+ "tenant-ext-connectivity",
+ "tenant-ext-protocol",
+ "tenant-qos",
+ "tenant-security",
+ "tenant-network-profile",
+ "tenant-epg",
+ "fabric-connectivity",
+ "fabric-protocol",
+ "fabric-equipment",
+ "access-connectivity",
+ "access-protocol",
+ "access-equipment",
+ "access-qos",
+ "nw-svc-params",
+ "ops",
+ "nw-svc-policy",
+ "site-admin",
+ "site-policy",
+ "config-manager",
+ "custom-privilege-1",
+ "custom-privilege-2",
+ "custom-privilege-3",
+ "custom-privilege-4",
+ "custom-privilege-5",
+ "custom-privilege-6",
+ "custom-privilege-7",
+ "custom-privilege-8",
+ "custom-privilege-9",
+ "custom-privilege-10",
+ "custom-privilege-11",
+ "custom-privilege-12",
+ "custom-privilege-13",
+ "custom-privilege-14",
+ "custom-privilege-15",
+ "custom-privilege-16",
+ "custom-privilege-17",
+ "custom-privilege-18",
+ "custom-privilege-19",
+ "custom-privilege-20",
+ "custom-privilege-21",
+ "custom-privilege-22",
+ "custom-port-privilege",
+]
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ name=dict(type="str", aliases=["aaa_role"]),
+ privileges=dict(type="list", aliases=["priv"], elements="str", choices=PRIVILEGES),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name", "privileges"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ privileges = module.params.get("privileges")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="aaaRole",
+ aci_rn="userext/role-{0}".format(name),
+ module_object=name,
+ target_filter=dict(name=name),
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ formatted_privileges = ",".join(privileges)
+ aci.payload(
+ aci_class="aaaRole",
+ class_config=dict(
+ name=name,
+ priv=formatted_privileges,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaRole")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_ssh_auth.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_ssh_auth.py
new file mode 100644
index 00000000..ec86540d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_ssh_auth.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_aaa_ssh_auth
+short_description: Manage AAA SSH auth (aaaSshAuth) objects.
+description:
+- Manage AAA SSH Auth key configuration on Cisco ACI fabrics.
+options:
+ aaa_user:
+ description:
+ - Name of an existing AAA user
+ type: str
+ required: yes
+ auth_name:
+ description:
+ - Name of the AAA SSH Auth key
+ type: str
+ data:
+ description:
+ - SSH key data
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(aaa_user) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_aaa_user) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(aaaSshAuth).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new SSH key
+ cisco.aci.aci_aaa_ssh_auth:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ auth_name: my_key
+ data: "{{ ssh_key_data_var }}"
+ state: present
+ delegate_to: localhost
+
+- name: Remove an SSH key
+ cisco.aci.aci_aaa_ssh_auth:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ auth_name: my_key
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SSH key
+ cisco.aci.aci_aaa_ssh_auth:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ auth_name: my_key
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SSH auth keys under a user
+ cisco.aci.aci_aaa_ssh_auth:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ aaa_user=dict(type='str', required=True),
+ auth_name=dict(type='str'),
+ data=dict(type='str'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['auth_name']],
+ ['state', 'present', ['auth_name', 'data']],
+ ],
+ )
+
+ aaa_user = module.params.get('aaa_user')
+ auth_name = module.params.get('auth_name')
+ data = module.params.get('data')
+ state = module.params.get('state')
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class='aaaUser',
+ aci_rn='userext/user-{0}'.format(aaa_user),
+ module_object=aaa_user,
+ target_filter={'name': aaa_user},
+ ),
+ subclass_1=dict(
+ aci_class='aaaSshAuth',
+ aci_rn='sshauth-{0}'.format(auth_name),
+ module_object=auth_name,
+ target_filter={'name': auth_name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='aaaSshAuth',
+ class_config=dict(
+ name=auth_name,
+ data=data
+ ),
+ )
+
+ aci.get_diff(aci_class='aaaSshAuth')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py
new file mode 100644
index 00000000..915b6b97
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user.py
@@ -0,0 +1,371 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_user
+short_description: Manage AAA users (aaa:User)
+description:
+- Manage AAA users on Cisco ACI fabrics.
+requirements:
+- python-dateutil
+options:
+ aaa_password:
+ description:
+ - The password of the locally-authenticated user.
+ type: str
+ aaa_password_lifetime:
+ description:
+ - The lifetime of the locally-authenticated user password.
+ type: int
+ aaa_password_update_required:
+ description:
+ - Whether this account needs password update.
+ type: bool
+ aaa_user:
+ description:
+ - The name of the locally-authenticated user user to add.
+ type: str
+ aliases: [ name, user ]
+ clear_password_history:
+ description:
+ - Whether to clear the password history of a locally-authenticated user.
+ type: bool
+ description:
+ description:
+ - Description for the AAA user.
+ type: str
+ aliases: [ descr ]
+ email:
+ description:
+ - The email address of the locally-authenticated user.
+ type: str
+ enabled:
+ description:
+ - The status of the locally-authenticated user account.
+ type: bool
+ expiration:
+ description:
+ - The expiration date of the locally-authenticated user account.
+ type: str
+ expires:
+ description:
+ - Whether to enable an expiration date for the locally-authenticated user account.
+ type: bool
+ first_name:
+ description:
+ - The first name of the locally-authenticated user.
+ type: str
+ last_name:
+ description:
+ - The last name of the locally-authenticated user.
+ type: str
+ phone:
+ description:
+ - The phone number of the locally-authenticated user.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+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. The vendor has been informed.
+ More information in :ref:`the ACI documentation <aci_guide_known_issues>`.
+seealso:
+- module: cisco.aci.aci_aaa_user_certificate
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(aaa:User).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a user
+ cisco.aci.aci_aaa_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: dag
+ aaa_password: AnotherSecretPassword
+ expiration: never
+ expires: no
+ email: dag@wieers.com
+ phone: 1-234-555-678
+ first_name: Dag
+ last_name: Wieers
+ state: present
+ delegate_to: localhost
+
+- name: Remove a user
+ cisco.aci.aci_aaa_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: dag
+ state: absent
+ delegate_to: localhost
+
+- name: Query a user
+ cisco.aci.aci_aaa_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: dag
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all users
+ cisco.aci.aci_aaa_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: '?rsp-prop-include=config-only'
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+try:
+ from dateutil.tz import tzutc
+ import dateutil.parser
+
+ HAS_DATEUTIL = True
+except ImportError:
+ HAS_DATEUTIL = False
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ aaa_password=dict(type="str", no_log=True),
+ aaa_password_lifetime=dict(type="int", no_log=False),
+ aaa_password_update_required=dict(type="bool", no_log=False),
+ aaa_user=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ clear_password_history=dict(type="bool", no_log=False),
+ description=dict(type="str", aliases=["descr"]),
+ email=dict(type="str"),
+ enabled=dict(type="bool"),
+ expiration=dict(type="str"),
+ expires=dict(type="bool"),
+ first_name=dict(type="str"),
+ last_name=dict(type="str"),
+ phone=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["aaa_user"]],
+ ["state", "present", ["aaa_user"]],
+ ["expires", True, ["expiration"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ if not HAS_DATEUTIL:
+ module.fail_json(msg="dateutil required for this module")
+
+ aaa_password = module.params.get("aaa_password")
+ aaa_password_lifetime = module.params.get("aaa_password_lifetime")
+ aaa_password_update_required = aci.boolean(module.params.get("aaa_password_update_required"))
+ aaa_user = module.params.get("aaa_user")
+ clear_password_history = aci.boolean(module.params.get("clear_password_history"), "yes", "no")
+ description = module.params.get("description")
+ email = module.params.get("email")
+ enabled = aci.boolean(module.params.get("enabled"), "active", "inactive")
+ expires = aci.boolean(module.params.get("expires"))
+ first_name = module.params.get("first_name")
+ last_name = module.params.get("last_name")
+ phone = module.params.get("phone")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ expiration = module.params.get("expiration")
+ if expiration is not None and expiration != "never":
+ try:
+ expiration = aci.iso8601_format(dateutil.parser.parse(expiration).replace(tzinfo=tzutc()))
+ except Exception as e:
+ module.fail_json(msg="Failed to parse date format '%s', %s" % (module.params.get("expiration"), e))
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="aaaUser",
+ aci_rn="userext/user-{0}".format(aaa_user),
+ module_object=aaa_user,
+ target_filter={"name": aaa_user},
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="aaaUser",
+ class_config=dict(
+ accountStatus=enabled,
+ clearPwdHistory=clear_password_history,
+ descr=description,
+ email=email,
+ expiration=expiration,
+ expires=expires,
+ firstName=first_name,
+ lastName=last_name,
+ name=aaa_user,
+ phone=phone,
+ pwd=aaa_password,
+ pwdLifeTime=aaa_password_lifetime,
+ pwdUpdateRequired=aaa_password_update_required,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaUser")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py
new file mode 100644
index 00000000..311ba4cc
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_certificate.py
@@ -0,0 +1,300 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_user_certificate
+short_description: Manage AAA user certificates (aaa:UserCert)
+description:
+- Manage AAA user certificates on Cisco ACI fabrics.
+options:
+ aaa_user:
+ description:
+ - The name of the user to add a certificate to.
+ type: str
+ required: yes
+ aaa_user_type:
+ description:
+ - Whether this is a normal user or an appuser.
+ type: str
+ choices: [ appuser, user ]
+ default: user
+ certificate:
+ description:
+ - The PEM format public key extracted from the X.509 certificate.
+ type: str
+ aliases: [ cert_data, certificate_data ]
+ name:
+ description:
+ - The name of the user certificate entry in ACI.
+ type: str
+ aliases: [ cert_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(aaa_user) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_aaa_user) module can be used for this.
+seealso:
+- module: cisco.aci.aci_aaa_user
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(aaa:UserCert).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a certificate to user
+ cisco.aci.aci_aaa_user_certificate:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: admin
+ name: admin
+ certificate_data: '{{ lookup("file", "pki/admin.crt") }}'
+ state: present
+ delegate_to: localhost
+
+- name: Remove a certificate of a user
+ cisco.aci.aci_aaa_user_certificate:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: admin
+ name: admin
+ state: absent
+ delegate_to: localhost
+
+- name: Query a certificate of a user
+ cisco.aci.aci_aaa_user_certificate:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: admin
+ name: admin
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all certificates of a user
+ cisco.aci.aci_aaa_user_certificate:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: admin
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+ACI_MAPPING = dict(
+ appuser=dict(
+ aci_class="aaaAppUser",
+ aci_mo="userext/appuser-",
+ ),
+ user=dict(
+ aci_class="aaaUser",
+ aci_mo="userext/user-",
+ ),
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ aaa_user=dict(type="str", required=True),
+ aaa_user_type=dict(type="str", default="user", choices=["appuser", "user"]),
+ certificate=dict(type="str", aliases=["cert_data", "certificate_data"]),
+ name=dict(type="str"), # Not required for querying all objects
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["aaa_user", "name"]],
+ ["state", "present", ["aaa_user", "certificate", "name"]],
+ ],
+ )
+
+ aaa_user = module.params.get("aaa_user")
+ aaa_user_type = module.params.get("aaa_user_type")
+ certificate = module.params.get("certificate")
+ name = module.params.get("name")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=ACI_MAPPING.get(aaa_user_type).get("aci_class"),
+ aci_rn=ACI_MAPPING.get(aaa_user_type).get("aci_mo") + aaa_user,
+ module_object=aaa_user,
+ target_filter={"name": aaa_user},
+ ),
+ subclass_1=dict(
+ aci_class="aaaUserCert",
+ aci_rn="usercert-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="aaaUserCert",
+ class_config=dict(
+ data=certificate,
+ name=name,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaUserCert")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_domain.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_domain.py
new file mode 100644
index 00000000..e864c8f9
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_domain.py
@@ -0,0 +1,317 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_user_domain
+short_description: Manage AAA user domains (aaa:UserDomain)
+description:
+- Manage AAA user domain configuration on Cisco ACI fabrics.
+options:
+ aaa_user:
+ description:
+ - The name of an existing AAA user
+ type: str
+ aliases: [ user_name ]
+ aaa_user_type:
+ description:
+ - Whether this is a normal user or an appuser.
+ type: str
+ choices: [ appuser, user ]
+ default: user
+ name:
+ description:
+ - The name of the user domain
+ type: str
+ aliases: [ domain_name, user_domain ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(aaa_user) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_aaa_user) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(aaaUserDomain).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add a security domain to a aaa_user
+ cisco.aci.aci_aaa_user_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ name: my_domain
+ state: present
+ delegate_to: localhost
+
+- name: Remove a security domain from a aaa_user
+ cisco.aci.aci_aaa_user_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ name: my_domain
+ state: absent
+ delegate_to: localhost
+
+- name: Add list of security domains to a aaa_user
+ cisco.aci.aci_aaa_user_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ name: "{{ item.name }}"
+ state: present
+ with_items:
+ - name: common
+ - name: all
+ - name: mgmt
+ delegate_to: localhost
+
+- name: Query a security domain from a aaa_user
+ cisco.aci.aci_aaa_user_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ name: my_domain
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all security domains of a aaa_user
+ cisco.aci.aci_aaa_user_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ state: query
+ delegate_to: localhost
+ register: query_results
+
+- name: Query all security domains to user associations
+ cisco.aci.aci_aaa_user_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_all_domains
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+ACI_MAPPING = dict(
+ appuser=dict(
+ aci_class="aaaAppUser",
+ aci_mo="userext/appuser-",
+ ),
+ user=dict(
+ aci_class="aaaUser",
+ aci_mo="userext/user-",
+ ),
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ aaa_user=dict(type="str", aliases=["user_name"]),
+ name=dict(type="str", aliases=["domain_name", "user_domain"]),
+ aaa_user_type=dict(type="str", default="user", choices=["appuser", "user"]),
+ name_alias=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["aaa_user", "name"]],
+ ["state", "present", ["aaa_user", "name"]],
+ ],
+ )
+
+ aaa_user = module.params.get("aaa_user")
+ name = module.params.get("name")
+ aaa_user_type = module.params.get("aaa_user_type")
+ name_alias = module.params.get("name_alias")
+ state = module.params.get("state")
+
+ child_classes = ["aaaUserRole"]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=ACI_MAPPING.get(aaa_user_type).get("aci_class"),
+ aci_rn="{0}{1}".format(ACI_MAPPING.get(aaa_user_type).get("aci_mo"), aaa_user),
+ module_object=aaa_user,
+ target_filter={"name": aaa_user},
+ ),
+ subclass_1=dict(
+ aci_class="aaaUserDomain",
+ aci_rn="userdomain-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=child_classes,
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="aaaUserDomain",
+ class_config=dict(
+ name=name,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaUserDomain")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_role.py b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_role.py
new file mode 100644
index 00000000..3870db37
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aaa_user_role.py
@@ -0,0 +1,365 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aaa_user_role
+short_description: Manage AAA user roles (aaa:UserRole)
+description:
+- Manage AAA User Role configuration on Cisco ACI fabrics.
+options:
+ aaa_user:
+ description:
+ - The name of the existing user to add roles and privileges
+ type: str
+ aliases: [ user_name ]
+ aaa_user_type:
+ description:
+ - Whether this is a normal user or an appuser.
+ type: str
+ choices: [ appuser, user ]
+ default: user
+ domain_name:
+ description:
+ - The name of the security domain
+ type: str
+ aliases: [ user_domain ]
+ name:
+ description:
+ - Name of the AAA role
+ type: str
+ aliases: [ role_name, user_role ]
+ privilege_type:
+ description:
+ - Privilege for the role
+ type: str
+ aliases: [ priv_type ]
+ choices: [ read, write ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(aaa_user) and C(domain_name) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_aaa_user) and M(cisco.aci.aci_aaa_user_domain) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(aaaUserRole).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add a user role to a user security domain
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ domain_name: my_domain
+ name: my_role
+ privilege_type: read
+ state: present
+ delegate_to: localhost
+
+- name: Add list of user roles to a user domain
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ domain_name: my_domain
+ name: "{{ item.name }}"
+ privilege_type: "{{ item.privilege_type }}"
+ state: present
+ with_items:
+ - name: aaa
+ privilege_type: write
+ - name: access-admin
+ privilege_type: write
+ - name: ops
+ privilege_type: write
+ delegate_to: localhost
+
+- name: Query a user role from a user security domain
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ domain_name: my_domain
+ name: my_role
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all user roles from a user security domain
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ domain_name: my_domain
+ state: query
+ delegate_to: localhost
+ register: query_results
+
+- name: Query all user roles from a user
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ state: query
+ delegate_to: localhost
+ register: query_all_roles_of_user
+
+- name: Query all user roles
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_all_user_roles
+
+- name: Remove user role from a user domain
+ cisco.aci.aci_aaa_user_role:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aaa_user: my_user
+ domain_name: my_domain
+ name: my_role
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+ACI_MAPPING = dict(
+ appuser=dict(
+ aci_class="aaaAppUser",
+ aci_mo="userext/appuser-",
+ ),
+ user=dict(
+ aci_class="aaaUser",
+ aci_mo="userext/user-",
+ ),
+)
+
+PRIV_TYPE_MAPPING = {
+ "read": "readPriv",
+ "write": "writePriv",
+}
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ aaa_user=dict(type="str", aliases=["user_name"]),
+ aaa_user_type=dict(type="str", default="user", choices=["appuser", "user"]),
+ domain_name=dict(type="str", aliases=["user_domain"]),
+ name=dict(type="str", aliases=["role_name", "user_role"]),
+ privilege_type=dict(type="str", aliases=["priv_type"], choices=["read", "write"]),
+ name_alias=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["aaa_user", "domain_name", "name"]],
+ ["state", "present", ["aaa_user", "domain_name", "name"]],
+ ],
+ )
+
+ aaa_user = module.params.get("aaa_user")
+ aaa_user_type = module.params.get("aaa_user_type")
+ domain_name = module.params.get("domain_name")
+ name = module.params.get("name")
+ privilege_type = module.params.get("privilege_type")
+ name_alias = module.params.get("name_alias")
+ state = module.params.get("state")
+
+ if privilege_type is not None:
+ privilege_type = PRIV_TYPE_MAPPING[privilege_type]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=ACI_MAPPING.get(aaa_user_type).get("aci_class"),
+ aci_rn="{0}{1}".format(ACI_MAPPING.get(aaa_user_type).get("aci_mo"), aaa_user),
+ module_object=aaa_user,
+ target_filter={"name": aaa_user},
+ ),
+ subclass_1=dict(
+ aci_class="aaaUserDomain",
+ aci_rn="userdomain-{0}".format(domain_name),
+ module_object=domain_name,
+ target_filter={"name": domain_name},
+ ),
+ subclass_2=dict(
+ aci_class="aaaUserRole",
+ aci_rn="role-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="aaaUserRole",
+ class_config=dict(
+ name=name,
+ privType=privilege_type,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="aaaUserRole")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py b/ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py
new file mode 100644
index 00000000..64642329
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_access_port_block_to_access_port.py
@@ -0,0 +1,438 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, Simon Metzger <smnmtzgr@gmail.com>
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# Copyright: (c) 2020, Zak Lantz (@manofcolombia) <zakodewald@gmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_access_port_block_to_access_port
+short_description: Manage port blocks of Fabric interface policy leaf profile interface selectors (infra:HPortS, infra:PortBlk)
+description:
+- Manage port blocks of Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics.
+options:
+ interface_profile:
+ description:
+ - The name of the Fabric access policy leaf interface profile.
+ type: str
+ aliases: [ leaf_interface_profile_name, leaf_interface_profile, interface_profile_name ]
+ access_port_selector:
+ description:
+ - The name of the Fabric access policy leaf interface profile access port selector.
+ type: str
+ aliases: [ name, access_port_selector_name ]
+ port_blk:
+ description:
+ - The name of the Fabric access policy leaf interface profile access port block.
+ type: str
+ aliases: [ leaf_port_blk_name, leaf_port_blk ]
+ port_blk_description:
+ description:
+ - The description to assign to the C(leaf_port_blk).
+ type: str
+ aliases: [ leaf_port_blk_description ]
+ from_port:
+ description:
+ - The beginning (from-range) of the port range block for the leaf access port block.
+ type: str
+ aliases: [ from, fromPort, from_port_range ]
+ to_port:
+ description:
+ - The end (to-range) of the port range block for the leaf access port block.
+ type: str
+ aliases: [ to, toPort, to_port_range ]
+ from_card:
+ description:
+ - The beginning (from-range) of the card range block for the leaf access port block.
+ type: str
+ aliases: [ from_card_range ]
+ to_card:
+ description:
+ - The end (to-range) of the card range block for the leaf access port block.
+ type: str
+ aliases: [ to_card_range ]
+ type:
+ description:
+ - The type of access port block to be created under respective access port.
+ type: str
+ choices: [ fex, leaf ]
+ default: leaf
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(interface_profile) and C(access_port_selector) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_interface_policy_leaf_profile) and M(cisco.aci.aci_access_port_to_interface_policy_leaf_profile) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:HPortS) and B(infra:PortBlk).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Simon Metzger (@smnmtzgr)
+"""
+
+EXAMPLES = r"""
+- name: Associate an access port block (single port) to an interface selector
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ port_blk: leafportblkname
+ from_port: 13
+ to_port: 13
+ state: present
+ delegate_to: localhost
+
+- name: Associate an access port block (port range) to an interface selector
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ port_blk: leafportblkname
+ from_port: 13
+ to_port: 16
+ state: present
+ delegate_to: localhost
+
+- name: Associate an access port block (single port) to an interface selector of type fex
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ interface_profile: leafintprfname_fex
+ access_port_selector: accessportselectorname_fex
+ port_blk: leafportblkname_fex
+ from_port: 13
+ to_port: 13
+ state: present
+ delegate_to: localhost
+
+- name: Associate an access port block (port range) to an interface selector of type fex
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ interface_profile: leafintprfname_fex
+ access_port_selector: accessportselectorname_fex
+ port_blk: leafportblkname_fex
+ from_port: 13
+ to_port: 16
+ state: present
+ delegate_to: localhost
+
+- name: Remove an access port block from an interface selector
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ port_blk: leafportblkname
+ from_port: 13
+ to_port: 13
+ state: absent
+ delegate_to: localhost
+
+- name: Remove an access port block from an interface selector of type fex
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ interface_profile: leafintprfname_fex
+ access_port_selector: accessportselectorname_fex
+ port_blk: leafportblkname_fex
+ from_port: 13
+ to_port: 13
+ state: absent
+ delegate_to: localhost
+
+- name: Query Specific access port block under given access port selector
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ port_blk: leafportblkname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query Specific access port block under given access port selector of type fex
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ interface_profile: leafintprfname_fex
+ access_port_selector: accessportselectorname_fex
+ port_blk: leafportblkname_fex
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all access port blocks under given leaf interface profile
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all access port blocks under given leaf interface profile of type fex
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ interface_profile: leafintprfname_fex
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all access port blocks in the fabric
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all access port blocks in the fabric of type fex
+ cisco.aci.aci_access_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ interface_profile=dict(type="str", aliases=["leaf_interface_profile_name", "leaf_interface_profile", "interface_profile_name"]),
+ access_port_selector=dict(type="str", aliases=["name", "access_port_selector_name"]), # Not required for querying all objects
+ port_blk=dict(type="str", aliases=["leaf_port_blk_name", "leaf_port_blk"]), # Not required for querying all objects
+ port_blk_description=dict(type="str", aliases=["leaf_port_blk_description"]),
+ from_port=dict(type="str", aliases=["from", "fromPort", "from_port_range"]),
+ to_port=dict(type="str", aliases=["to", "toPort", "to_port_range"]),
+ from_card=dict(type="str", aliases=["from_card_range"]),
+ to_card=dict(type="str", aliases=["to_card_range"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ type=dict(type="str", default="leaf", choices=["fex", "leaf"]), # This parameter is not required for querying all objects
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["access_port_selector", "port_blk", "interface_profile"]],
+ ["state", "present", ["access_port_selector", "port_blk", "from_port", "to_port", "interface_profile"]],
+ ],
+ )
+
+ interface_profile = module.params.get("interface_profile")
+ access_port_selector = module.params.get("access_port_selector")
+ port_blk = module.params.get("port_blk")
+ port_blk_description = module.params.get("port_blk_description")
+ from_port = module.params.get("from_port")
+ to_port = module.params.get("to_port")
+ from_card = module.params.get("from_card")
+ to_card = module.params.get("to_card")
+ state = module.params.get("state")
+ type_port = module.params.get("type")
+
+ aci = ACIModule(module)
+ aci_class = "infraAccPortP"
+ aci_rn = "accportprof"
+ if type_port == "fex":
+ aci_class = "infraFexP"
+ aci_rn = "fexprof"
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_class,
+ aci_rn="infra/" + aci_rn + "-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_1=dict(
+ aci_class="infraHPortS",
+ # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module
+ aci_rn="hports-{0}-typ-range".format(access_port_selector),
+ module_object=access_port_selector,
+ target_filter={"name": access_port_selector},
+ ),
+ subclass_2=dict(
+ aci_class="infraPortBlk",
+ aci_rn="portblk-{0}".format(port_blk),
+ module_object=port_blk,
+ target_filter={"name": port_blk},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraPortBlk",
+ class_config=dict(
+ descr=port_blk_description,
+ name=port_blk,
+ fromPort=from_port,
+ toPort=to_port,
+ fromCard=from_card,
+ toCard=to_card,
+ # type='range',
+ ),
+ )
+
+ aci.get_diff(aci_class="infraPortBlk")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py
new file mode 100644
index 00000000..b9407f34
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_access_port_to_interface_policy_leaf_profile.py
@@ -0,0 +1,493 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_access_port_to_interface_policy_leaf_profile
+short_description: Manage Fabric interface policy leaf profile interface selectors (infra:HPortS, infra:RsAccBaseGrp, infra:PortBlk)
+description:
+- Manage Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics.
+options:
+ interface_profile:
+ description:
+ - The name of the Fabric access policy leaf interface profile.
+ type: str
+ aliases: [ leaf_interface_profile_name, leaf_interface_profile, interface_profile_name ]
+ access_port_selector:
+ description:
+ - The name of the Fabric access policy leaf interface profile access port selector.
+ type: str
+ aliases: [ name, access_port_selector_name ]
+ description:
+ description:
+ - The description to assign to the C(access_port_selector)
+ type: str
+ port_blk:
+ description:
+ - B(Deprecated)
+ - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead.
+ - The parameter will be removed in Ansible 2.12.
+ - HORIZONTALLINE
+ - The name of the Fabric access policy leaf interface profile access port block.
+ type: str
+ aliases: [ leaf_port_blk_name, leaf_port_blk, port_blk_name ]
+ leaf_port_blk_description:
+ description:
+ - B(Deprecated)
+ - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead.
+ - The parameter will be removed in Ansible 2.12.
+ - HORIZONTALLINE
+ - The description to assign to the C(leaf_port_blk)
+ type: str
+ from_port:
+ description:
+ - B(Deprecated)
+ - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead.
+ - The parameter will be removed in Ansible 2.12.
+ - HORIZONTALLINE
+ - The beginning (from-range) of the port range block for the leaf access port block.
+ type: str
+ aliases: [ from, fromPort, from_port_range ]
+ to_port:
+ description:
+ - B(Deprecated)
+ - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead.
+ - The parameter will be removed in Ansible 2.12.
+ - HORIZONTALLINE
+ - The end (to-range) of the port range block for the leaf access port block.
+ type: str
+ aliases: [ to, toPort, to_port_range ]
+ from_card:
+ description:
+ - B(Deprecated)
+ - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead.
+ - The parameter will be removed in Ansible 2.12.
+ - HORIZONTALLINE
+ - The beginning (from-range) of the card range block for the leaf access port block.
+ type: str
+ aliases: [ from_card_range ]
+ to_card:
+ description:
+ - B(Deprecated)
+ - Starting with Ansible 2.8 we recommend using M(cisco.aci.aci_access_port_block_to_access_port) instead.
+ - The parameter will be removed in Ansible 2.12.
+ - HORIZONTALLINE
+ - The end (to-range) of the card range block for the leaf access port block.
+ type: str
+ aliases: [ to_card_range ]
+ policy_group:
+ description:
+ - The name of the fabric access policy group to be associated with the leaf interface profile interface selector.
+ type: str
+ aliases: [ policy_group_name ]
+ interface_type:
+ description:
+ - The type of interface for the static EPG deployment.
+ - The interface_type fex_profile can not be configured with a profile of type fex.
+ type: str
+ choices: [ breakout, fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc , fex_profile]
+ default: switch_port
+ fex_id:
+ description:
+ - Id of the fex profile, a valid FEX ID is between 101 to 199.
+ type: int
+ fex_profile:
+ description:
+ - The name of the Fex Profile. Value of the fex_profile is overridden by the policy_group.
+ type: str
+ aliases: [ fex_profile_name ]
+ type:
+ description:
+ - The type of access port to be created under respective profile.
+ type: str
+ aliases: [ profile_type ]
+ choices: [ fex, leaf ]
+ default: leaf
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(interface_profile) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_interface_policy_leaf_profile) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_access_port_block_to_access_port
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:HPortS), B(infra:RsAccBaseGrp) and B(infra:PortBlk).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Associate an Interface Access Port Selector to an Interface Policy Leaf Profile with a Policy Group
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ port_blk: leafportblkname
+ from_port: 13
+ to_port: 16
+ policy_group: policygroupname
+ state: present
+ delegate_to: localhost
+
+- name: Associate an interface access port selector to an Interface Policy Leaf Profile (w/o policy group) (check if this works)
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ port_blk: leafportblkname
+ from_port: 13
+ to_port: 16
+ state: present
+ delegate_to: localhost
+
+- name: Remove an interface access port selector associated with an Interface Policy Leaf Profile
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ state: absent
+ delegate_to: localhost
+
+- name: Remove an interface access port selector associated with an Interface Policy Fex Profile
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: fexintprfname
+ access_port_selector: accessportselectorname
+ state: absent
+ delegate_to: localhost
+
+- name: Query Specific access_port_selector under given leaf_interface_profile
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query Specific access_port_selector under given Fex leaf_interface_profile
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: fexintprfname
+ access_port_selector: accessportselectorname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Create and Bind Access Port Selector with Fex Profile Policy Group
+ cisco.aci.aci_access_port_to_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_interface_profile: leafintprftest
+ fex_profile: fexintprftest
+ policy_group: fexintprftest
+ access_port_selector_name: anstest_fex_accessportselector
+ interface_type: fex_profile
+ from_port: 13
+ to_port: 13
+ port_blk: block2
+ fex_id: 105
+ state: present
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+port_channels_dn = "uni/infra/funcprof/accbundle-{0}"
+
+INTERFACE_TYPE_MAPPING = dict(
+ breakout="uni/infra/funcprof/brkoutportgrp-{0}",
+ fex="uni/infra/funcprof/accportgrp-{0}",
+ fex_profile="uni/infra/fexprof-{0}/fexbundle-{1}",
+ port_channel=port_channels_dn,
+ switch_port="uni/infra/funcprof/accportgrp-{0}",
+ vpc=port_channels_dn,
+ fex_port_channel=port_channels_dn,
+ fex_vpc=port_channels_dn,
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ interface_profile=dict(type="str", aliases=["leaf_interface_profile_name", "leaf_interface_profile", "interface_profile_name"]),
+ access_port_selector=dict(type="str", aliases=["name", "access_port_selector_name"]), # Not required for querying all objects
+ description=dict(type="str"),
+ port_blk=dict(type="str", aliases=["leaf_port_blk_name", "leaf_port_blk", "port_blk_name"]),
+ leaf_port_blk_description=dict(type="str"),
+ from_port=dict(type="str", aliases=["from", "fromPort", "from_port_range"]),
+ to_port=dict(type="str", aliases=["to", "toPort", "to_port_range"]),
+ from_card=dict(type="str", aliases=["from_card_range"]),
+ to_card=dict(type="str", aliases=["to_card_range"]),
+ policy_group=dict(type="str", aliases=["policy_group_name"]),
+ interface_type=dict(
+ type="str", default="switch_port", choices=["breakout", "fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc", "fex_profile"]
+ ),
+ fex_id=dict(type="int"),
+ fex_profile=dict(type="str", aliases=["fex_profile_name"]),
+ type=dict(type="str", default="leaf", choices=["fex", "leaf"], aliases=["profile_type"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["interface_profile", "access_port_selector"]],
+ ["state", "present", ["interface_profile", "access_port_selector"]],
+ ],
+ )
+
+ interface_profile = module.params.get("interface_profile")
+ access_port_selector = module.params.get("access_port_selector")
+ description = module.params.get("description")
+ port_blk = module.params.get("port_blk")
+ leaf_port_blk_description = module.params.get("leaf_port_blk_description")
+ from_port = module.params.get("from_port")
+ to_port = module.params.get("to_port")
+ from_card = module.params.get("from_card")
+ to_card = module.params.get("to_card")
+ policy_group = module.params.get("policy_group")
+ interface_type = module.params.get("interface_type")
+ fex_id = module.params.get("fex_id")
+ fex_profile = module.params.get("fex_profile")
+ state = module.params.get("state")
+ type_profile = module.params.get("type")
+
+ # Build child_configs dynamically
+ child_configs = [
+ dict(
+ infraPortBlk=dict(
+ attributes=dict(
+ descr=leaf_port_blk_description,
+ name=port_blk,
+ fromPort=from_port,
+ toPort=to_port,
+ fromCard=from_card,
+ toCard=to_card,
+ ),
+ ),
+ )
+ ]
+
+ # Add infraRsAccBaseGrp only when policy_group was defined
+ if policy_group is not None:
+
+ infra_rs_acc_base_grp = dict(
+ infraRsAccBaseGrp=dict(
+ attributes=dict(),
+ ),
+ )
+
+ if interface_type == "fex_profile":
+ if type_profile == "fex":
+ module.fail_json(msg="Invalid Configuration - interface_type fex_profile can not be configured with a profile of type fex")
+ elif fex_profile is not None:
+ infra_rs_acc_base_grp["infraRsAccBaseGrp"]["attributes"]["tDn"] = INTERFACE_TYPE_MAPPING[interface_type].format(fex_profile, policy_group)
+ elif fex_profile is None:
+ infra_rs_acc_base_grp["infraRsAccBaseGrp"]["attributes"]["tDn"] = INTERFACE_TYPE_MAPPING[interface_type].format(policy_group, policy_group)
+
+ if fex_id is not None:
+ if fex_id in range(101, 200):
+ infra_rs_acc_base_grp["infraRsAccBaseGrp"]["attributes"]["fexId"] = fex_id
+ else:
+ module.fail_json(msg="A valid FEX ID is between 101 to 199")
+ else:
+ module.fail_json(msg="The fex_id must not be None, when interface_type is fex_profile")
+
+ else:
+ infra_rs_acc_base_grp["infraRsAccBaseGrp"]["attributes"]["tDn"] = INTERFACE_TYPE_MAPPING[interface_type].format(policy_group)
+
+ child_configs.append(infra_rs_acc_base_grp)
+
+ aci = ACIModule(module)
+ aci_class = "infraAccPortP"
+ aci_rn = "accportprof"
+ if type_profile == "fex":
+ aci_class = "infraFexP"
+ aci_rn = "fexprof"
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_class,
+ aci_rn="infra/" + aci_rn + "-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_1=dict(
+ aci_class="infraHPortS",
+ # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module
+ aci_rn="hports-{0}-typ-range".format(access_port_selector),
+ module_object=access_port_selector,
+ target_filter={"name": access_port_selector},
+ ),
+ child_classes=["infraPortBlk", "infraRsAccBaseGrp"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraHPortS",
+ class_config=dict(
+ descr=description,
+ name=access_port_selector,
+ # type='range',
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="infraHPortS")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py b/ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py
new file mode 100644
index 00000000..8073c6ca
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_access_sub_port_block_to_access_port.py
@@ -0,0 +1,365 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Simon Metzger <smnmtzgr@gmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_access_sub_port_block_to_access_port
+short_description: Manage sub port blocks of Fabric interface policy leaf profile interface selectors (infra:HPortS, infra:SubPortBlk)
+description:
+- Manage sub port blocks of Fabric interface policy leaf profile interface selectors on Cisco ACI fabrics.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:HPortS) and B(infra:SubPortBlk).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Simon Metzger (@smnmtzgr)
+options:
+ leaf_interface_profile:
+ description:
+ - The name of the Fabric access policy leaf interface profile.
+ type: str
+ aliases: [ leaf_interface_profile_name ]
+ access_port_selector:
+ description:
+ - The name of the Fabric access policy leaf interface profile access port selector.
+ type: str
+ aliases: [ name, access_port_selector_name ]
+ leaf_port_blk:
+ description:
+ - The name of the Fabric access policy leaf interface profile access port block.
+ type: str
+ aliases: [ leaf_port_blk_name ]
+ leaf_port_blk_description:
+ description:
+ - The description to assign to the C(leaf_port_blk).
+ type: str
+ from_port:
+ description:
+ - The beginning (from-range) of the port range block for the leaf access port block.
+ type: str
+ aliases: [ from, fromPort, from_port_range ]
+ to_port:
+ description:
+ - The end (to-range) of the port range block for the leaf access port block.
+ type: str
+ aliases: [ to, toPort, to_port_range ]
+ from_sub_port:
+ description:
+ - The beginning (from-range) of the sub port range block for the leaf access port block.
+ type: str
+ aliases: [ fromSubPort, from_sub_port_range ]
+ to_sub_port:
+ description:
+ - The end (to-range) of the sub port range block for the leaf access port block.
+ type: str
+ aliases: [ toSubPort, to_sub_port_range ]
+ from_card:
+ description:
+ - The beginning (from-range) of the card range block for the leaf access port block.
+ type: str
+ aliases: [ from_card_range ]
+ to_card:
+ description:
+ - The end (to-range) of the card range block for the leaf access port block.
+ type: str
+ aliases: [ to_card_range ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+"""
+
+EXAMPLES = r"""
+- name: Associate an access sub port block (single port) to an interface selector
+ cisco.aci.aci_access_sub_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ leaf_port_blk: leafportblkname
+ from_port: 13
+ to_port: 13
+ from_sub_port: 1
+ to_sub_port: 1
+ state: present
+ delegate_to: localhost
+
+- name: Associate an access sub port block (port range) to an interface selector
+ cisco.aci.aci_access_sub_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ leaf_port_blk: leafportblkname
+ from_port: 13
+ to_port: 13
+ from_sub_port: 1
+ to_sub_port: 3
+ state: present
+ delegate_to: localhost
+
+- name: Remove an access sub port block from an interface selector
+ cisco.aci.aci_access_sub_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ leaf_port_blk: leafportblkname
+ from_port: 13
+ to_port: 13
+ from_sub_port: 1
+ to_sub_port: 1
+ state: absent
+ delegate_to: localhost
+
+- name: Query Specific access sub port block under given access port selector
+ cisco.aci.aci_access_sub_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_interface_profile: leafintprfname
+ access_port_selector: accessportselectorname
+ leaf_port_blk: leafportblkname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all access sub port blocks under given leaf interface profile
+ cisco.aci.aci_access_sub_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_interface_profile: leafintprfname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all access sub port blocks in the fabric
+ cisco.aci.aci_access_sub_port_block_to_access_port:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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
+ leaf_port_blk=dict(type="str", aliases=["leaf_port_blk_name"]), # Not required for querying all objects
+ leaf_port_blk_description=dict(type="str"),
+ from_port=dict(type="str", aliases=["from", "fromPort", "from_port_range"]), # Not required for querying all objects and deleting sub port blocks
+ to_port=dict(type="str", aliases=["to", "toPort", "to_port_range"]), # Not required for querying all objects and deleting sub port blocks
+ from_sub_port=dict(type="str", aliases=["fromSubPort", "from_sub_port_range"]), # Not required for querying all objects and deleting sub port blocks
+ to_sub_port=dict(type="str", aliases=["toSubPort", "to_sub_port_range"]), # Not required for querying all objects and deleting sub port blocks
+ from_card=dict(type="str", aliases=["from_card_range"]),
+ to_card=dict(type="str", aliases=["to_card_range"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["access_port_selector", "leaf_port_blk", "leaf_interface_profile"]],
+ ["state", "present", ["access_port_selector", "leaf_port_blk", "from_port", "to_port", "from_sub_port", "to_sub_port", "leaf_interface_profile"]],
+ ],
+ )
+
+ leaf_interface_profile = module.params.get("leaf_interface_profile")
+ access_port_selector = module.params.get("access_port_selector")
+ leaf_port_blk = module.params.get("leaf_port_blk")
+ leaf_port_blk_description = module.params.get("leaf_port_blk_description")
+ from_port = module.params.get("from_port")
+ to_port = module.params.get("to_port")
+ from_sub_port = module.params.get("from_sub_port")
+ to_sub_port = module.params.get("to_sub_port")
+ from_card = module.params.get("from_card")
+ to_card = module.params.get("to_card")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraAccPortP",
+ aci_rn="infra/accportprof-{0}".format(leaf_interface_profile),
+ module_object=leaf_interface_profile,
+ target_filter={"name": leaf_interface_profile},
+ ),
+ subclass_1=dict(
+ aci_class="infraHPortS",
+ # NOTE: normal rn: hports-{name}-typ-{type}, hence here hardcoded to range for purposes of module
+ aci_rn="hports-{0}-typ-range".format(access_port_selector),
+ module_object=access_port_selector,
+ target_filter={"name": access_port_selector},
+ ),
+ subclass_2=dict(
+ aci_class="infraSubPortBlk",
+ aci_rn="subportblk-{0}".format(leaf_port_blk),
+ module_object=leaf_port_blk,
+ target_filter={"name": leaf_port_blk},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraSubPortBlk",
+ class_config=dict(
+ descr=leaf_port_blk_description,
+ name=leaf_port_blk,
+ fromPort=from_port,
+ toPort=to_port,
+ fromSubPort=from_sub_port,
+ toSubPort=to_sub_port,
+ fromCard=from_card,
+ toCard=to_card,
+ # type='range',
+ ),
+ )
+
+ aci.get_diff(aci_class="infraSubPortBlk")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aep.py b/ansible_collections/cisco/aci/plugins/modules/aci_aep.py
new file mode 100644
index 00000000..ad9a40d8
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aep.py
@@ -0,0 +1,289 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aep
+short_description: Manage attachable Access Entity Profile (AEP) objects (infra:AttEntityP, infra:ProvAcc)
+description:
+- Connect to external virtual and physical domains by using
+ attachable Access Entity Profiles (AEP) on Cisco ACI fabrics.
+options:
+ aep:
+ description:
+ - The name of the Attachable Access Entity Profile.
+ type: str
+ aliases: [ aep_name, name ]
+ description:
+ description:
+ - Description for the AEP.
+ type: str
+ aliases: [ descr ]
+ infra_vlan:
+ description:
+ - Enable infrastructure VLAN.
+ - The hypervisor functions of the AEP.
+ - C(no) will disable the infrastructure vlan if it is enabled.
+ type: bool
+ aliases: [ infrastructure_vlan ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ default: present
+ choices: [ absent, present, query ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_aep_to_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:AttEntityP) and B(infra:ProvAcc).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Swetha Chunduri (@schunduri)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new AEP
+ cisco.aci.aci_aep:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: ACI-AEP
+ description: default
+ infra_vlan: true
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing AEP
+ cisco.aci.aci_aep:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: ACI-AEP
+ state: absent
+ delegate_to: localhost
+
+- name: Query all AEPs
+ cisco.aci.aci_aep:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific AEP
+ cisco.aci.aci_aep:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: ACI-AEP
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ 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"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["aep"]],
+ ["state", "present", ["aep"]],
+ ],
+ )
+
+ aep = module.params.get("aep")
+ description = module.params.get("description")
+ infra_vlan = module.params.get("infra_vlan")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+ if infra_vlan:
+ child_configs = [dict(infraProvAcc=dict(attributes=dict(name="provacc")))]
+ elif infra_vlan is False:
+ child_configs = [dict(infraProvAcc=dict(attributes=dict(name="provacc", status="deleted")))]
+ else:
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraAttEntityP",
+ aci_rn="infra/attentp-{0}".format(aep),
+ module_object=aep,
+ target_filter={"name": aep},
+ ),
+ child_classes=["infraProvAcc"],
+ )
+
+ aci.get_existing()
+
+ try:
+ if len(aci.existing[0]["infraAttEntityP"]) == 1 and infra_vlan is False:
+ child_configs = []
+ except Exception:
+ pass
+
+ if state == "present":
+
+ aci.payload(
+ aci_class="infraAttEntityP",
+ class_config=dict(
+ name=aep,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="infraAttEntityP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py b/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py
new file mode 100644
index 00000000..d3a8e9e1
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_domain.py
@@ -0,0 +1,314 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aep_to_domain
+short_description: Bind AEPs to Physical or Virtual Domains (infra:RsDomP)
+description:
+- Bind AEPs to Physical or Virtual Domains on Cisco ACI fabrics.
+options:
+ aep:
+ description:
+ - The name of the Attachable Access Entity Profile.
+ type: str
+ aliases: [ aep_name ]
+ domain:
+ description:
+ - Name of the physical or virtual domain being associated with the AEP.
+ type: str
+ aliases: [ domain_name, domain_profile ]
+ domain_type:
+ description:
+ - Determines if the Domain is physical (phys) or virtual (vmm).
+ type: str
+ choices: [ fc, l2dom, l3dom, phys, vmm ]
+ aliases: [ type ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(aep) and C(domain) parameters should exist before using this module.
+ The M(cisco.aci.aci_aep) and M(cisco.aci.aci_domain) can be used for these.
+seealso:
+- module: cisco.aci.aci_aep
+- module: cisco.aci.aci_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:RsDomP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add AEP to domain binding
+ cisco.aci.aci_aep_to_domain: &binding_present
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: test_aep
+ domain: phys_dom
+ domain_type: phys
+ state: present
+ delegate_to: localhost
+
+- name: Remove AEP to domain binding
+ cisco.aci.aci_aep_to_domain: &binding_absent
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: test_aep
+ domain: phys_dom
+ domain_type: phys
+ state: absent
+ delegate_to: localhost
+
+- name: Query our AEP to domain binding
+ cisco.aci.aci_aep_to_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: test_aep
+ domain: phys_dom
+ domain_type: phys
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all AEP to domain bindings
+ cisco.aci.aci_aep_to_domain: &binding_query
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["domain_type", "vmm", ["vm_provider"]],
+ ["state", "absent", ["aep", "domain", "domain_type"]],
+ ["state", "present", ["aep", "domain", "domain_type"]],
+ ],
+ required_together=[
+ ["domain", "domain_type"],
+ ],
+ )
+
+ aep = module.params.get("aep")
+ domain = module.params.get("domain")
+ domain_type = module.params.get("domain_type")
+ vm_provider = module.params.get("vm_provider")
+ state = module.params.get("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))
+
+ # Compile the full domain for URL building
+ if domain_type == "fc":
+ domain_mo = "uni/fc-{0}".format(domain)
+ elif domain_type == "l2dom":
+ domain_mo = "uni/l2dom-{0}".format(domain)
+ elif domain_type == "l3dom":
+ domain_mo = "uni/l3dom-{0}".format(domain)
+ elif domain_type == "phys":
+ domain_mo = "uni/phys-{0}".format(domain)
+ elif domain_type == "vmm":
+ domain_mo = "uni/vmmp-{0}/dom-{1}".format(VM_PROVIDER_MAPPING[vm_provider], domain)
+ else:
+ domain_mo = None
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraAttEntityP",
+ aci_rn="infra/attentp-{0}".format(aep),
+ module_object=aep,
+ target_filter={"name": aep},
+ ),
+ subclass_1=dict(
+ aci_class="infraRsDomP",
+ aci_rn="rsdomP-[{0}]".format(domain_mo),
+ module_object=domain_mo,
+ target_filter={"tDn": domain_mo},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraRsDomP",
+ class_config=dict(tDn=domain_mo),
+ )
+
+ aci.get_diff(aci_class="infraRsDomP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_epg.py
new file mode 100644
index 00000000..c7741707
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_aep_to_epg.py
@@ -0,0 +1,333 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_aep_to_epg
+short_description: Bind EPG to AEP (infra:RsFuncToEpg).
+description:
+- Bind EPG to AEP.
+options:
+ aep:
+ description:
+ - The name of the Attachable Access Entity Profile.
+ type: str
+ aliases: [ aep_name ]
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - Name of an existing application network profile, that will contain the EPGs.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ epg:
+ description:
+ - The name of the end point group.
+ type: str
+ aliases: [ epg_name ]
+ encap:
+ description:
+ - The VLAN associated with this application EPG.
+ type: int
+ aliases: [ vlan, vlan_id, encap_id ]
+ primary_encap:
+ description:
+ - The primary VLAN associated with this EPG
+ type: int
+ aliases: [ primary_vlan, primary_vlan_id, primary_encap_id ]
+ 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 to C(trunk) when unset during creation.
+ type: str
+ choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ]
+ aliases: [ mode, mode_name, interface_mode_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Associate EPG with AEP
+ cisco.aci.aci_aep_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: aep1
+ tenant: tenant1
+ ap: ap1
+ epg: epg1
+ encap_id: 222
+ interface_mode: access
+ state: present
+ delegate_to: localhost
+
+- name: Associate EPG with AEP
+ cisco.aci.aci_aep_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: aep1
+ tenant: tenant1
+ ap: ap1
+ epg: epg1
+ encap_id: 222
+ interface_mode: access
+ state: absent
+ delegate_to: localhost
+
+- name: Get specific EPG with AEP association
+ cisco.aci.aci_aep_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ aep: aep1
+ tenant: tenant1
+ ap: ap1
+ epg: epg1
+ encap_id: 222
+ interface_mode: access
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Get all EPG with AEP association
+ cisco.aci.aci_aep_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+INTERFACE_MODE_MAPPING = {
+ "802.1p": "native",
+ "access": "untagged",
+ "native": "native",
+ "regular": "regular",
+ "tagged": "regular",
+ "trunk": "regular",
+ "untagged": "untagged",
+}
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ aep=dict(type="str", aliases=["aep_name"]),
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ epg=dict(type="str", aliases=["epg_name"]),
+ encap=dict(type="int", aliases=["vlan", "vlan_id", "encap_id"]),
+ primary_encap=dict(type="int", aliases=["primary_vlan", "primary_vlan_id", "primary_encap_id"]),
+ interface_mode=dict(
+ type="str", choices=["802.1p", "access", "native", "regular", "tagged", "trunk", "untagged"], aliases=["mode_name", "mode", "interface_mode_name"]
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["aep", "epg", "ap", "tenant"]],
+ ["state", "present", ["interface_mode", "encap", "aep", "epg", "ap", "tenant"]],
+ ],
+ )
+
+ aep = module.params.get("aep")
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ epg = module.params.get("epg")
+ encap = module.params.get("encap")
+ primary_encap = module.params.get("primary_encap")
+ interface_mode = module.params.get("interface_mode")
+ state = module.params.get("state")
+
+ if interface_mode is not None:
+ interface_mode = INTERFACE_MODE_MAPPING[interface_mode]
+
+ if encap is not None:
+ encap = "vlan-{0}".format(encap)
+
+ if primary_encap is not None:
+ primary_encap = "vlan-{0}".format(primary_encap)
+
+ epg_mo = None
+ if tenant is not None and ap is not None and epg is not None:
+ epg_mo = "uni/tn-{0}/ap-{1}/epg-{2}".format(tenant, ap, epg)
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(aci_class="infraAttEntityP", aci_rn="infra/attentp-{0}".format(aep), module_object=aep, target_filter={"name": aep}),
+ subclass_1=dict(aci_class="infraGeneric", aci_rn="gen-default", module_object="default", target_filter={"name": "default"}),
+ subclass_2=dict(aci_class="infraRsFuncToEpg", aci_rn="rsfuncToEpg-[{0}]".format(epg_mo), module_object=epg_mo, target_filter={"tDn": epg_mo}),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ # Post configuration on infraGeneric (subclass_1) level instead of on
+ # infraRsFuncToEpg (subclass_2) level.
+ # The reason being that the MO "gen-default" (of class infraGeneric) does not
+ # exist until the first EPG to AEP association is created.
+ aci.construct_url(
+ root_class=dict(aci_class="infraAttEntityP", aci_rn="infra/attentp-{0}".format(aep), module_object=aep, target_filter={"name": aep}),
+ subclass_1=dict(aci_class="infraGeneric", aci_rn="gen-default", module_object="default", target_filter={"name": "default"}),
+ child_classes=["infraRsFuncToEpg"],
+ )
+
+ aci.get_existing()
+
+ child_configs = [dict(infraRsFuncToEpg=dict(attributes=dict(encap=encap, primaryEncap=primary_encap, mode=interface_mode, tDn=epg_mo)))]
+
+ aci.payload(aci_class="infraGeneric", class_config=dict(name="default"), child_configs=child_configs)
+
+ aci.get_diff(aci_class="infraGeneric")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_ap.py b/ansible_collections/cisco/aci/plugins/modules/aci_ap.py
new file mode 100644
index 00000000..d5655efe
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_ap.py
@@ -0,0 +1,293 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_ap
+short_description: Manage top level Application Profile (AP) objects (fv:Ap)
+description:
+- Manage top level Application Profile (AP) objects on Cisco ACI fabrics
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application network profile.
+ type: str
+ aliases: [ app_profile, app_profile_name, name ]
+ description:
+ description:
+ - Description for the AP.
+ type: str
+ aliases: [ descr ]
+ monitoring_policy:
+ description:
+ - The name of the monitoring policy.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- This module does not manage EPGs, see M(cisco.aci.aci_epg) to do this.
+- The used C(tenant) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:Ap).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Swetha Chunduri (@schunduri)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new AP
+ cisco.aci.aci_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: default
+ description: default ap
+ monitoring_policy: default
+ state: present
+ delegate_to: localhost
+
+- name: Remove an AP
+ cisco.aci.aci_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: default
+ state: absent
+ delegate_to: localhost
+
+- name: Query an AP
+ cisco.aci.aci_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: default
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all APs
+ cisco.aci.aci_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ 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"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ monitoring_policy=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap"]],
+ ["state", "present", ["tenant", "ap"]],
+ ],
+ )
+
+ ap = module.params.get("ap")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+ monitoring_policy = module.params.get("monitoring_policy")
+
+ child_configs = [dict(fvRsApMonPol=dict(attributes=dict(tnMonEPGPolName=monitoring_policy)))]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ child_classes=["fvRsApMonPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvAp",
+ class_config=dict(
+ name=ap,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="fvAp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bd.py b/ansible_collections/cisco/aci/plugins/modules/aci_bd.py
new file mode 100644
index 00000000..fbf8ea95
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bd.py
@@ -0,0 +1,489 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_bd
+short_description: Manage Bridge Domains (BD) objects (fv:BD)
+description:
+- Manages Bridge Domains (BD) on Cisco ACI fabrics.
+options:
+ arp_flooding:
+ description:
+ - Determines if the Bridge Domain should flood ARP traffic.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ bd:
+ description:
+ - The name of the Bridge Domain.
+ type: str
+ aliases: [ bd_name, name ]
+ bd_type:
+ description:
+ - The type of traffic on the Bridge Domain.
+ - The APIC defaults to C(ethernet) when unset during creation.
+ type: str
+ choices: [ ethernet, fc ]
+ description:
+ description:
+ - Description for the Bridge Domain.
+ type: str
+ enable_multicast:
+ description:
+ - Determines if PIM is enabled.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ enable_routing:
+ description:
+ - Determines if IP forwarding should be allowed.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ endpoint_clear:
+ description:
+ - Clears all End Points in all Leaves when C(yes).
+ - The value is not reset to disabled once End Points have been cleared; that requires a second task.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ endpoint_move_detect:
+ description:
+ - Determines if GARP should be enabled to detect when End Points move.
+ type: str
+ choices: [ default, garp ]
+ endpoint_retention_action:
+ description:
+ - Determines if the Bridge Domain should inherit or resolve the End Point Retention Policy.
+ - The APIC defaults to C(resolve) when unset during creation.
+ type: str
+ choices: [ inherit, resolve ]
+ endpoint_retention_policy:
+ description:
+ - The name of the End Point Retention Policy the Bridge Domain should use when
+ overriding the default End Point Retention Policy.
+ type: str
+ igmp_snoop_policy:
+ description:
+ - The name of the IGMP Snooping Policy the Bridge Domain should use when
+ overriding the default IGMP Snooping Policy.
+ type: str
+ ip_learning:
+ description:
+ - Determines if the Bridge Domain should learn End Point IPs.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ ipv6_nd_policy:
+ description:
+ - The name of the IPv6 Neighbor Discovery Policy the Bridge Domain should use when
+ overridding the default IPV6 ND Policy.
+ type: str
+ l2_unknown_unicast:
+ description:
+ - Determines what forwarding method to use for unknown l2 destinations.
+ - The APIC defaults to C(proxy) when unset during creation.
+ type: str
+ choices: [ proxy, flood ]
+ l3_unknown_multicast:
+ description:
+ - Determines the forwarding method to use for unknown multicast destinations.
+ - The APIC defaults to C(flood) when unset during creation.
+ type: str
+ choices: [ flood, opt-flood ]
+ ipv6_l3_unknown_multicast:
+ description:
+ - Determines the forwarding method to use for IPv6 unknown multicast destinations.
+ - The APIC defaults to C(flood) when unset during creation.
+ type: str
+ choices: [ flood, opt-flood ]
+ limit_ip_learn:
+ description:
+ - Determines if the BD should limit IP learning to only subnets owned by the Bridge Domain.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ mac_address:
+ description:
+ - The MAC Address to assign to the C(bd) instead of using the default.
+ - The APIC defaults to C(00:22:BD:F8:19:FF) when unset during creation.
+ type: str
+ aliases: [ mac ]
+ multi_dest:
+ description:
+ - Determines the forwarding method for L2 multicast, broadcast, and link layer traffic.
+ - The APIC defaults to C(bd-flood) when unset during creation.
+ type: str
+ choices: [ bd-flood, drop, encap-flood ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+ vrf:
+ description:
+ - The name of the VRF.
+ type: str
+ aliases: [ vrf_name ]
+ route_profile:
+ description:
+ - The Route Profile to associate with the Bridge Domain.
+ type: str
+ route_profile_l3out:
+ description:
+ - The L3 Out that contains the associated Route Profile.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:BD).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add Bridge Domain
+ cisco.aci.aci_bd:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ tenant: prod
+ bd: web_servers
+ mac_address: 00:22:BD:F8:19:FE
+ vrf: prod_vrf
+ state: present
+ delegate_to: localhost
+
+- name: Add an FC Bridge Domain
+ cisco.aci.aci_bd:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ tenant: prod
+ bd: storage
+ bd_type: fc
+ vrf: fc_vrf
+ enable_routing: no
+ state: present
+ delegate_to: localhost
+
+- name: Modify a Bridge Domain
+ cisco.aci.aci_bd:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: yes
+ tenant: prod
+ bd: web_servers
+ arp_flooding: yes
+ l2_unknown_unicast: flood
+ state: present
+ delegate_to: localhost
+
+- name: Query All Bridge Domains
+ cisco.aci.aci_bd:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: yes
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a Bridge Domain
+ cisco.aci.aci_bd:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: yes
+ tenant: prod
+ bd: web_servers
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a Bridge Domain
+ cisco.aci.aci_bd:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: yes
+ tenant: prod
+ bd: web_servers
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ arp_flooding=dict(type="bool"),
+ 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"),
+ enable_routing=dict(type="bool"),
+ endpoint_clear=dict(type="bool"),
+ endpoint_move_detect=dict(type="str", choices=["default", "garp"]),
+ endpoint_retention_action=dict(type="str", choices=["inherit", "resolve"]),
+ endpoint_retention_policy=dict(type="str"),
+ igmp_snoop_policy=dict(type="str"),
+ ip_learning=dict(type="bool"),
+ ipv6_nd_policy=dict(type="str"),
+ l2_unknown_unicast=dict(type="str", choices=["proxy", "flood"]),
+ l3_unknown_multicast=dict(type="str", choices=["flood", "opt-flood"]),
+ ipv6_l3_unknown_multicast=dict(type="str", choices=["flood", "opt-flood"]),
+ limit_ip_learn=dict(type="bool"),
+ mac_address=dict(type="str", aliases=["mac"]),
+ multi_dest=dict(type="str", choices=["bd-flood", "drop", "encap-flood"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ vrf=dict(type="str", aliases=["vrf_name"]),
+ route_profile=dict(type="str"),
+ route_profile_l3out=dict(type="str"),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["bd", "tenant"]],
+ ["state", "present", ["bd", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ arp_flooding = aci.boolean(module.params.get("arp_flooding"))
+ bd = module.params.get("bd")
+ bd_type = module.params.get("bd_type")
+ if bd_type == "ethernet":
+ # ethernet type is represented as regular, but that is not clear to the users
+ bd_type = "regular"
+ description = module.params.get("description")
+ enable_multicast = aci.boolean(module.params.get("enable_multicast"))
+ enable_routing = aci.boolean(module.params.get("enable_routing"))
+ endpoint_clear = aci.boolean(module.params.get("endpoint_clear"))
+ endpoint_move_detect = module.params.get("endpoint_move_detect")
+ if endpoint_move_detect == "default":
+ # the ACI default setting is an empty string, but that is not a good input value
+ endpoint_move_detect = ""
+ endpoint_retention_action = module.params.get("endpoint_retention_action")
+ endpoint_retention_policy = module.params.get("endpoint_retention_policy")
+ igmp_snoop_policy = module.params.get("igmp_snoop_policy")
+ ip_learning = aci.boolean(module.params.get("ip_learning"))
+ ipv6_nd_policy = module.params.get("ipv6_nd_policy")
+ l2_unknown_unicast = module.params.get("l2_unknown_unicast")
+ l3_unknown_multicast = module.params.get("l3_unknown_multicast")
+ ipv6_l3_unknown_multicast = module.params.get("ipv6_l3_unknown_multicast")
+ limit_ip_learn = aci.boolean(module.params.get("limit_ip_learn"))
+ mac_address = module.params.get("mac_address")
+ multi_dest = module.params.get("multi_dest")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ vrf = module.params.get("vrf")
+ route_profile = module.params.get("route_profile")
+ route_profile_l3out = module.params.get("route_profile_l3out")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvBD",
+ aci_rn="BD-{0}".format(bd),
+ module_object=bd,
+ target_filter={"name": bd},
+ ),
+ child_classes=["fvRsCtx", "fvRsIgmpsn", "fvRsBDToNdP", "fvRsBdToEpRet", "fvRsBDToProfile"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ class_config = dict(
+ arpFlood=arp_flooding,
+ descr=description,
+ epClear=endpoint_clear,
+ epMoveDetectMode=endpoint_move_detect,
+ ipLearning=ip_learning,
+ limitIpLearnToSubnets=limit_ip_learn,
+ mac=mac_address,
+ mcastAllow=enable_multicast,
+ multiDstPktAct=multi_dest,
+ name=bd,
+ type=bd_type,
+ unicastRoute=enable_routing,
+ unkMacUcastAct=l2_unknown_unicast,
+ unkMcastAct=l3_unknown_multicast,
+ nameAlias=name_alias,
+ )
+
+ if ipv6_l3_unknown_multicast is not None:
+ class_config["v6unkMcastAct"] = ipv6_l3_unknown_multicast
+
+ aci.payload(
+ aci_class="fvBD",
+ class_config=class_config,
+ child_configs=[
+ {"fvRsCtx": {"attributes": {"tnFvCtxName": vrf}}},
+ {"fvRsIgmpsn": {"attributes": {"tnIgmpSnoopPolName": igmp_snoop_policy}}},
+ {"fvRsBDToNdP": {"attributes": {"tnNdIfPolName": ipv6_nd_policy}}},
+ {"fvRsBdToEpRet": {"attributes": {"resolveAct": endpoint_retention_action, "tnFvEpRetPolName": endpoint_retention_policy}}},
+ {"fvRsBDToProfile": {"attributes": {"tnL3extOutName": route_profile_l3out, "tnRtctrlProfileName": route_profile}}},
+ ],
+ )
+
+ aci.get_diff(aci_class="fvBD")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py b/ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py
new file mode 100644
index 00000000..484bdb2f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bd_dhcp_label.py
@@ -0,0 +1,317 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Dag Wieers (@dagwieers)
+# Copyright: (c) 2020, sig9org (@sig9org)
+# 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_bd_dhcp_label
+short_description: Manage DHCP Labels (dhcp:Lbl)
+description:
+- Manage DHCP Labels on Cisco ACI fabrics.
+options:
+ bd:
+ description:
+ - The name of the Bridge Domain.
+ type: str
+ aliases: [ bd_name ]
+ description:
+ description:
+ - The description for the DHCP Label.
+ type: str
+ aliases: [ descr ]
+ dhcp_label:
+ description:
+ - The name of the DHCP Relay Label.
+ type: str
+ aliases: [ name ]
+ dhcp_option:
+ description:
+ - Name of the DHCP Option Policy to be associated with the DCHP Relay Policy.
+ This policy need to be present in the same tenant as the bridge domain.
+ - The DHCP option is used to supply DHCP clients with configuration parameters
+ such as a domain, name server, subnet, and network address.
+ type: str
+ scope:
+ description:
+ - Represents the target relay servers ownership.
+ type: str
+ choices: [ infra, tenant ]
+ default: infra
+ aliases: [ owner ]
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- A DHCP relay label contains a C(name) for the label, the C(scope), and a DHCP option policy.
+ The scope is the C(owner) of the relay server and the DHCP option policy supplies DHCP clients
+ with configuration parameters such as domain, nameserver, and subnet router addresses.
+- The C(tenant) and C(bd) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module and M(cisco.aci.aci_bd) can be used for these.
+seealso:
+- module: cisco.aci.aci_bd
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(dhcp:Lbl).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- sig9 (@sig9org)
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a new DHCP Relay Label to a Bridge Domain
+ cisco.aci.aci_bd_dhcp_label:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ dhcp_label: label1
+ scope: infra
+ state: present
+
+- name: Query a DHCP Relay Label of a Bridge Domain
+ cisco.aci.aci_bd_dhcp_label:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ dhcp_label: label1
+ scope: infra
+ state: query
+
+- name: Query all DHCP Relay Labels of a Bridge Domain
+ cisco.aci.aci_bd_dhcp_label:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ state: query
+
+- name: Remove a DHCP Relay Label for a Bridge Domain
+ cisco.aci.aci_bd_dhcp_label:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ dhcp_label: label1
+ scope: infra
+ 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: str
+ 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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ bd=dict(type="str", aliases=["bd_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ dhcp_label=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ dhcp_option=dict(type="str"),
+ scope=dict(type="str", default="infra", choices=["infra", "tenant"], aliases=["owner"]), # Not required for querying all objects
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["bd", "tenant", "dhcp_label", "scope"]],
+ ["state", "present", ["bd", "tenant", "dhcp_label", "scope"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ bd = module.params.get("bd")
+ description = module.params.get("description")
+ dhcp_label = module.params.get("dhcp_label")
+ dhcp_option = module.params.get("dhcp_option")
+ scope = module.params.get("scope")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvBD",
+ aci_rn="BD-{0}".format(bd),
+ module_object=bd,
+ target_filter={"name": bd},
+ ),
+ subclass_2=dict(
+ aci_class="dhcpLbl",
+ aci_rn="dhcplbl-{0}".format(dhcp_label),
+ module_object=dhcp_label,
+ target_filter={"name": dhcp_label},
+ ),
+ child_classes=["dhcpRsDhcpOptionPol"],
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="dhcpLbl",
+ class_config=dict(
+ descr=description,
+ name=dhcp_label,
+ owner=scope,
+ ),
+ child_configs=[
+ {"dhcpRsDhcpOptionPol": {"attributes": {"tnDhcpOptionPolName": dhcp_option}}},
+ ],
+ )
+
+ aci.get_diff(aci_class="dhcpLbl")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py b/ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py
new file mode 100644
index 00000000..617bbbf1
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bd_subnet.py
@@ -0,0 +1,468 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_bd_subnet
+short_description: Manage Subnets (fv:Subnet)
+description:
+- Manage Subnets on Cisco ACI fabrics.
+options:
+ bd:
+ description:
+ - The name of the Bridge Domain.
+ type: str
+ aliases: [ bd_name ]
+ description:
+ description:
+ - The description for the Subnet.
+ type: str
+ aliases: [ descr ]
+ enable_vip:
+ description:
+ - Determines if the Subnet should be treated as a VIP; used when the BD is extended to multiple sites.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ gateway:
+ description:
+ - The IPv4 or IPv6 gateway address for the Subnet.
+ type: str
+ aliases: [ gateway_ip ]
+ mask:
+ description:
+ - The subnet mask for the Subnet.
+ - This is the number associated with CIDR notation.
+ - For IPv4 addresses, accepted values range between C(0) and C(32).
+ - For IPv6 addresses, accepted Values range between C(0) and C(128).
+ type: int
+ aliases: [ subnet_mask ]
+ nd_prefix_policy:
+ description:
+ - The IPv6 Neighbor Discovery Prefix Policy to associate with the Subnet.
+ type: str
+ preferred:
+ description:
+ - Determines if the Subnet is preferred over all available Subnets. Only one Subnet per Address Family (IPv4/IPv6).
+ can be preferred in the Bridge Domain.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ route_profile:
+ description:
+ - The Route Profile to the associate with the Subnet.
+ type: str
+ route_profile_l3_out:
+ description:
+ - The L3 Out that contains the associated Route Profile.
+ type: str
+ scope:
+ description:
+ - Determines the scope of the Subnet.
+ - The C(private) option only allows communication with hosts in the same VRF.
+ - The C(public) option allows the Subnet to be advertised outside of the ACI Fabric, and allows communication with
+ hosts in other VRFs.
+ - The shared option limits communication to hosts in either the same VRF or the shared VRF.
+ - The value is a list of options, C(private) and C(public) are mutually exclusive, but both can be used with C(shared).
+ - The APIC defaults to C(private) when unset during creation.
+ type: list
+ elements: str
+ choices:
+ - private
+ - public
+ - shared
+ subnet_control:
+ description:
+ - Determines the Subnet's Control State.
+ - The C(querier_ip) option is used to treat the gateway_ip as an IGMP querier source IP.
+ - The C(nd_ra) option is used to treat the gateway_ip address as a Neighbor Discovery Router Advertisement Prefix.
+ - The C(no_gw) option is used to remove default gateway functionality from the gateway address.
+ - The APIC defaults to C(nd_ra) when unset during creation.
+ type: str
+ choices: [ nd_ra, no_gw, querier_ip, unspecified ]
+ subnet_name:
+ description:
+ - The name of the Subnet.
+ type: str
+ aliases: [ name ]
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+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(cisco.aci.aci_tenant) module and M(cisco.aci.aci_bd) can be used for these.
+seealso:
+- module: cisco.aci.aci_bd
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:Subnet).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Create a tenant
+ cisco.aci.aci_tenant:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: present
+ delegate_to: localhost
+
+- name: Create a bridge domain
+ cisco.aci.aci_bd:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ state: present
+ delegate_to: localhost
+
+- name: Create a subnet
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ gateway: 10.1.1.1
+ mask: 24
+ state: present
+ delegate_to: localhost
+
+- name: Create a subnet with options
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ subnet_name: sql
+ gateway: 10.1.2.1
+ mask: 23
+ description: SQL Servers
+ scope: public
+ route_profile_l3_out: corp
+ route_profile: corp_route_profile
+ state: present
+ delegate_to: localhost
+
+- name: Update a subnets scope to private and shared
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ gateway: 10.1.1.1
+ mask: 24
+ scope: [private, shared]
+ state: present
+ delegate_to: localhost
+
+- name: Get all subnets
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Get all subnets of specific gateway in specified tenant
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ gateway: 10.1.1.1
+ mask: 24
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Get specific subnet
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ gateway: 10.1.1.1
+ mask: 24
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a subnet
+ cisco.aci.aci_bd_subnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ bd: database
+ gateway: 10.1.1.1
+ mask: 24
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+SUBNET_CONTROL_MAPPING = dict(
+ nd_ra="nd",
+ no_gw="no-default-gateway",
+ querier_ip="querier",
+ unspecified="",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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"]), # 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"),
+ route_profile=dict(type="str"),
+ route_profile_l3_out=dict(type="str"),
+ scope=dict(type="list", elements="str", 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"]), # Not required for querying all objects
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_together=[["gateway", "mask"]],
+ required_if=[
+ ["state", "present", ["bd", "gateway", "mask", "tenant"]],
+ ["state", "absent", ["bd", "gateway", "mask", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ description = module.params.get("description")
+ enable_vip = aci.boolean(module.params.get("enable_vip"))
+ tenant = module.params.get("tenant")
+ bd = module.params.get("bd")
+ gateway = module.params.get("gateway")
+ mask = module.params.get("mask")
+ if mask is not None and mask not in range(0, 129):
+ # TODO: split checks between IPv4 and IPv6 Addresses
+ module.fail_json(msg="Valid Subnet Masks are 0 to 32 for IPv4 Addresses and 0 to 128 for IPv6 addresses")
+ if gateway is not None:
+ gateway = "{0}/{1}".format(gateway, str(mask))
+ subnet_name = module.params.get("subnet_name")
+ nd_prefix_policy = module.params.get("nd_prefix_policy")
+ preferred = aci.boolean(module.params.get("preferred"))
+ route_profile = module.params.get("route_profile")
+ route_profile_l3_out = module.params.get("route_profile_l3_out")
+ scope = module.params.get("scope")
+ if scope is not None:
+ if "private" in scope and "public" in scope:
+ module.fail_json(msg="Parameter 'scope' cannot be both 'private' and 'public', got: %s" % scope)
+ else:
+ scope = ",".join(sorted(scope))
+ state = module.params.get("state")
+ subnet_control = module.params.get("subnet_control")
+ if subnet_control:
+ subnet_control = SUBNET_CONTROL_MAPPING[subnet_control]
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvBD",
+ aci_rn="BD-{0}".format(bd),
+ module_object=bd,
+ target_filter={"name": bd},
+ ),
+ subclass_2=dict(
+ aci_class="fvSubnet",
+ aci_rn="subnet-[{0}]".format(gateway),
+ module_object=gateway,
+ target_filter={"ip": gateway},
+ ),
+ child_classes=["fvRsBDSubnetToProfile", "fvRsNdPfxPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvSubnet",
+ class_config=dict(
+ ctrl=subnet_control,
+ descr=description,
+ ip=gateway,
+ name=subnet_name,
+ preferred=preferred,
+ scope=scope,
+ virtual=enable_vip,
+ nameAlias=name_alias,
+ ),
+ child_configs=[
+ {"fvRsBDSubnetToProfile": {"attributes": {"tnL3extOutName": route_profile_l3_out, "tnRtctrlProfileName": route_profile}}},
+ {"fvRsNdPfxPol": {"attributes": {"tnNdPfxPolName": nd_prefix_policy}}},
+ ],
+ )
+
+ aci.get_diff(aci_class="fvSubnet")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py b/ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py
new file mode 100644
index 00000000..fffe4cab
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bd_to_l3out.py
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_bd_to_l3out
+short_description: Bind Bridge Domain to L3 Out (fv:RsBDToOut)
+description:
+- Bind Bridge Domain to L3 Out on Cisco ACI fabrics.
+options:
+ bd:
+ description:
+ - The name of the Bridge Domain.
+ type: str
+ aliases: [ bd_name, bridge_domain ]
+ l3out:
+ description:
+ - The name of the l3out to associate with th Bridge Domain.
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(bd) and C(l3out) parameters should exist before using this module.
+ The M(cisco.aci.aci_bd) and C(aci_l3out) can be used for these.
+seealso:
+- module: cisco.aci.aci_bd
+- module: cisco.aci.aci_l3out
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:RsBDToOut).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Bind Bridge Domain to L3 Out
+ cisco.aci.aci_bd_to_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ bd: web_servers
+ l3out: prod_l3out
+ tenant: prod
+ state: present
+ delegate_to: localhost
+
+- name: Unbind Bridge Domain from L3 Out
+ cisco.aci.aci_bd_to_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ bd: web_servers
+ l3out: prod_l3out
+ tenant: prod
+ state: absent
+ delegate_to: localhost
+
+- name: Query all Bridge Domains bound to L3 Outs
+ cisco.aci.aci_bd_to_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query specific Bridge Domain(s) bound to an L3 Out
+ cisco.aci.aci_bd_to_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ bd: web_servers
+ l3out: prod_l3out
+ tenant: prod
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+SUBNET_CONTROL_MAPPING = dict(
+ nd_ra="nd",
+ no_gw="no-default-gateway",
+ querier_ip="querier",
+ unspecified="",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["bd", "l3out", "tenant"]],
+ ["state", "absent", ["bd", "l3out", "tenant"]],
+ ],
+ )
+
+ bd = module.params.get("bd")
+ l3out = module.params.get("l3out")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvBD",
+ aci_rn="BD-{0}".format(bd),
+ module_object=bd,
+ target_filter={"name": bd},
+ ),
+ subclass_2=dict(
+ aci_class="fvRsBDToOut",
+ aci_rn="rsBDToOut-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"tnL3extOutName": l3out},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvRsBDToOut",
+ class_config=dict(tnL3extOutName=l3out),
+ )
+
+ aci.get_diff(aci_class="fvRsBDToOut")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_asn.py b/ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_asn.py
new file mode 100644
index 00000000..69c56b18
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_asn.py
@@ -0,0 +1,239 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_bgp_rr_asn
+short_description: Manage BGP Route Reflector ASN.
+description:
+- Manage the BGP Autonomous System Number of the fabric (bgpAsP).
+- This module is specifically for fabric BGP, for L3Out BGP use the aci_l3out_bgp_peer module
+options:
+ asn:
+ description:
+ - BGP Autonomous System Number
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(bgpAsP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Set fabric BGP AS number
+ cisco.aci.aci_bgp_rr_asn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ asn: 65001
+ state: present
+ delegate_to: localhost
+
+- name: Remove fabric BGP AS number
+ cisco.aci.aci_bgp_rr_asn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: absent
+ delegate_to: localhost
+
+- name: Query fabric BGP AS number
+ cisco.aci.aci_bgp_rr_asn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ asn=dict(type='int'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'present', ['asn']],
+ ],
+ )
+
+ asn = module.params.get('asn')
+ state = module.params.get('state')
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class='bgpInstPol',
+ aci_rn='fabric/bgpInstP-default',
+ module_object='default',
+ target_filter={'name': 'default'},
+ ),
+ subclass_1=dict(
+ aci_class='bgpAsP',
+ aci_rn='as',
+ module_object='name',
+ target_filter={'name': ''},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+
+ aci.payload(
+ aci_class='bgpAsP',
+ class_config=dict(
+ asn=asn,
+ ),
+ )
+
+ aci.get_diff(aci_class='bgpAsP')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_node.py b/ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_node.py
new file mode 100644
index 00000000..a173210c
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bgp_rr_node.py
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_bgp_rr_node
+short_description: Manage BGP Route Reflector objects.
+description:
+- Manage ACI BGP Route Reflector Nodes (bgpRRNodePEp).
+options:
+ node_id:
+ description:
+ - ID of the Route Reflector Node
+ type: int
+ pod_id:
+ description:
+ - Pod the node belongs to
+ type: int
+ description:
+ description:
+ - Description of the Route Reflector Node
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(bgpRRNodePEp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new BGP Route Reflector
+ cisco.aci.aci_bgp_rr_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ node_id: 101
+ pod_id: 1
+ state: present
+ delegate_to: localhost
+
+- name: Remove a BGP Route Reflector
+ cisco.aci.aci_bgp_rr_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ node_id: 101
+ state: absent
+ delegate_to: localhost
+
+- name: Query a BGP Route Reflector
+ cisco.aci.aci_bgp_rr_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ node_id: 101
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all BGP Route Reflectors
+ cisco.aci.aci_bgp_rr_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ node_id=dict(type='int'),
+ pod_id=dict(type='int'),
+ description=dict(type='str'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['node_id']],
+ ['state', 'present', ['node_id', 'pod_id']],
+ ],
+ )
+
+ node_id = module.params.get('node_id')
+ pod_id = module.params.get('pod_id')
+ description = module.params.get('description')
+ state = module.params.get('state')
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class='bgpInstPol',
+ aci_rn='fabric/bgpInstP-default',
+ module_object='default',
+ target_filter={'name': 'default'},
+ ),
+ subclass_1=dict(
+ aci_class='bgpRRP',
+ aci_rn='rr',
+ module_object='name',
+ target_filter={'name': ''},
+ ),
+ subclass_2=dict(
+ aci_class='bgpRRNodePEp',
+ aci_rn='node-{0}'.format(node_id),
+ module_object=node_id,
+ target_filter={'id': node_id},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+
+ aci.payload(
+ aci_class='bgpRRNodePEp',
+ class_config=dict(
+ descr=description,
+ id=node_id,
+ podId=pod_id,
+ ),
+ )
+
+ aci.get_diff(aci_class='bgpRRNodePEp')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_bulk_static_binding_to_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_bulk_static_binding_to_epg.py
new file mode 100644
index 00000000..2f8e8af4
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_bulk_static_binding_to_epg.py
@@ -0,0 +1,608 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_bulk_static_binding_to_epg
+short_description: Bind static paths to EPGs (fv:RsPathAtt)
+description:
+- Bind static paths to EPGs on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application profile.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ epg:
+ description:
+ - The name of the end point group.
+ type: str
+ aliases: [ epg_name ]
+ description:
+ description:
+ - Description for the static path to EPG binding.
+ type: str
+ aliases: [ descr ]
+ encap_id:
+ description:
+ - The encapsulation ID associating the C(epg) with the interface path.
+ - This acts as the secondary C(encap_id) when using micro-segmentation.
+ - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096).
+ type: int
+ aliases: [ vlan, vlan_id ]
+ primary_encap_id:
+ description:
+ - Determines the primary encapsulation ID associating the C(epg)
+ with the interface path when using micro-segmentation.
+ - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown).
+ - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation.
+ type: str
+ aliases: [ primary_vlan, primary_vlan_id ]
+ deploy_immediacy:
+ description:
+ - The Deployment Immediacy of Static EPG on PC, VPC or Interface.
+ - The APIC defaults to C(lazy) when unset during creation.
+ type: str
+ choices: [ immediate, lazy ]
+ 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 to C(trunk) when unset during creation.
+ type: str
+ choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ]
+ aliases: [ interface_mode_name, mode ]
+ interface_type:
+ description:
+ - The type of interface for the static EPG deployment.
+ type: str
+ choices: [ fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc ]
+ default: switch_port
+ interface_configs:
+ description:
+ - List of interface configurations, elements in the form of a dictionary.
+ - Module level attributes will be overridden by the path level attributes.
+ type: list
+ elements: dict
+ suboptions:
+ description:
+ description:
+ - Description for the static path to EPG binding.
+ type: str
+ aliases: [ descr ]
+ encap_id:
+ description:
+ - The encapsulation ID associating the C(epg) with the interface path.
+ - This acts as the secondary C(encap_id) when using micro-segmentation.
+ - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096).
+ type: int
+ aliases: [ vlan, vlan_id ]
+ primary_encap_id:
+ description:
+ - Determines the primary encapsulation ID associating the C(epg)
+ with the interface path when using micro-segmentation.
+ - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown).
+ - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation.
+ type: str
+ aliases: [ primary_vlan, primary_vlan_id ]
+ deploy_immediacy:
+ description:
+ - The Deployment Immediacy of Static EPG on PC, VPC or Interface.
+ - The APIC defaults to C(lazy) when unset during creation.
+ type: str
+ choices: [ immediate, lazy ]
+ 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 to C(trunk) when unset during creation.
+ type: str
+ choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ]
+ aliases: [ interface_mode_name, mode ]
+ interface_type:
+ description:
+ - The type of interface for the static EPG deployment.
+ type: str
+ choices: [ fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc ]
+ pod_id:
+ description:
+ - The pod number part of the tDn.
+ - C(pod_id) is usually an integer below C(10).
+ type: int
+ required: yes
+ 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.
+ - The C(leafs) value is usually something like '101' or '101-102' depending on C(connection_type).
+ type: list
+ elements: str
+ required: yes
+ aliases: [ leaves, nodes, paths, switches ]
+ interface:
+ description:
+ - The C(interface) string value part of the tDn.
+ - Usually a policy group like C(test-IntPolGrp) or an interface of the following format C(1/7) depending on C(interface_type).
+ type: str
+ required: yes
+ extpaths:
+ description:
+ - The C(extpaths) integer value part of the tDn.
+ - C(extpaths) is only used if C(interface_type) is C(fex), C(fex_vpc) or C(fex_port_channel).
+ - When C(interface_type) is C(fex_vpc), then C(extpaths) is a list with both fex IDs.
+ - Usually something like C(1011).
+ type: list
+ elements: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(ap), C(epg) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_ap
+- module: cisco.aci.aci_epg
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:RsPathAtt).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+- Marcel Zehnder (@maercu)
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Create list of interfaces using module level attributes
+ cisco.aci.aci_bulk_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ encap_id: 221
+ interface_mode: trunk
+ deploy_immediacy: lazy
+ description: "Module level attributes used to create interfaces"
+ interface_configs:
+ - interface: 1/7
+ leafs: 101
+ pod: 1
+ - interface: 1/7
+ leafs: 107
+ pod: 7
+ - interface: 1/8
+ leafs: 108
+ pod: 8
+ encap_id: 108
+ state: present
+ delegate_to: localhost
+
+- name: Create/Update list of interfaces using path level attributes
+ cisco.aci.aci_bulk_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ interface_configs:
+ - interface: 1/7
+ leafs: 101
+ pod: 1
+ encap_id: 221
+ interface_mode: trunk
+ deploy_immediacy: lazy
+ description: "Path level attributes used to create/update interfaces"
+ - interface: 1/7
+ leafs: 107
+ pod: 7
+ encap_id: 221
+ interface_mode: trunk
+ deploy_immediacy: lazy
+ description: "Path level attributes used to create/update interfaces"
+ - interface: 1/8
+ leafs: 108
+ pod: 8
+ encap_id: 108
+ interface_mode: trunk
+ deploy_immediacy: lazy
+ description: "Path level attributes used to create/update interfaces"
+ state: present
+ delegate_to: localhost
+
+- name: Query all interfaces of an EPG
+ cisco.aci.aci_bulk_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ state: query
+ delegate_to: localhost
+
+- name: Query all interfaces
+ cisco.aci.aci_bulk_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove list of interfaces
+ cisco.aci.aci_bulk_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ encap_id: 221
+ interface_mode: trunk
+ deploy_immediacy: lazy
+ interface_configs:
+ - interface: 1/7
+ leafs: 101
+ pod: 1
+ - interface: 1/7
+ leafs: 107
+ pod: 7
+ - interface: 1/8
+ leafs: 108
+ pod: 8
+ encap_id: 108
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+INTERFACE_MODE_MAPPING = {
+ "802.1p": "native",
+ "access": "untagged",
+ "native": "native",
+ "regular": "regular",
+ "tagged": "regular",
+ "trunk": "regular",
+ "untagged": "untagged",
+}
+
+INTERFACE_TYPE_MAPPING = {
+ "fex": "topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[eth{interface}]",
+ "fex_port_channel": "topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[{interface}]",
+ "fex_vpc": "topology/pod-{pod_id}/protpaths-{leafs}/extprotpaths-{extpaths}/pathep-[{interface}]",
+ "port_channel": "topology/pod-{pod_id}/paths-{leafs}/pathep-[{interface}]",
+ "switch_port": "topology/pod-{pod_id}/paths-{leafs}/pathep-[eth{interface}]",
+ "vpc": "topology/pod-{pod_id}/protpaths-{leafs}/pathep-[{interface}]",
+}
+
+INTERFACE_STATUS_MAPPING = {"absent": "deleted"}
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_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"]),
+ description=dict(type="str", aliases=["descr"]),
+ encap_id=dict(type="int", aliases=["vlan", "vlan_id"]),
+ primary_encap_id=dict(type="str", aliases=["primary_vlan", "primary_vlan_id"]),
+ deploy_immediacy=dict(type="str", choices=["immediate", "lazy"]),
+ 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", "fex_port_channel", "fex_vpc"]),
+ interface_configs=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ description=dict(type="str", aliases=["descr"]),
+ encap_id=dict(type="int", aliases=["vlan", "vlan_id"]),
+ primary_encap_id=dict(type="str", aliases=["primary_vlan", "primary_vlan_id"]),
+ deploy_immediacy=dict(type="str", choices=["immediate", "lazy"]),
+ interface_mode=dict(
+ type="str", choices=["802.1p", "access", "native", "regular", "tagged", "trunk", "untagged"], aliases=["interface_mode_name", "mode"]
+ ),
+ interface_type=dict(type="str", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]),
+ pod_id=dict(type="int", required=True, aliases=["pod", "pod_number"]),
+ leafs=dict(type="list", elements="str", required=True, aliases=["leaves", "nodes", "paths", "switches"]),
+ interface=dict(type="str", required=True),
+ extpaths=dict(type="list", elements="str"),
+ ),
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["ap", "epg", "tenant"]],
+ ["state", "present", ["ap", "epg", "tenant"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ epg = module.params.get("epg")
+ module_description = module.params.get("description")
+ module_encap_id = module.params.get("encap_id")
+ module_primary_encap_id = module.params.get("primary_encap_id")
+ module_deploy_immediacy = module.params.get("deploy_immediacy")
+ module_interface_mode = module.params.get("interface_mode")
+ module_interface_type = module.params.get("interface_type")
+ interface_configs = module.params.get("interface_configs")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ children = []
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter=dict(name=tenant),
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter=dict(name=ap),
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter=dict(name=epg),
+ ),
+ child_classes=["fvRsPathAtt"],
+ )
+
+ aci.get_existing()
+
+ if state == "present" or state == "absent":
+ for interface_config in interface_configs:
+ pod_id = interface_config.get("pod_id")
+ leafs = interface_config.get("leafs")
+ interface = interface_config.get("interface")
+ extpaths = interface_config.get("extpaths")
+
+ description = interface_config.get("description") or module_description
+ deploy_immediacy = interface_config.get("deploy_immediacy") or module_deploy_immediacy
+ interface_type = interface_config.get("interface_type") or module_interface_type
+ encap_id = interface_config.get("encap_id") or module_encap_id
+ primary_encap_id = interface_config.get("primary_encap_id") or module_primary_encap_id
+ interface_mode = interface_config.get("interface_mode") or module_interface_mode
+
+ if interface_type in ["fex", "fex_vpc", "fex_port_channel"] and extpaths is None:
+ aci.fail_json(msg="extpaths is required when interface_type is: {0}".format(interface_type))
+
+ if leafs is not None:
+ # Process leafs, and support dash-delimited leafs
+ leafs = []
+ for leaf in interface_config.get("leafs"):
+ # Users are likely to use integers for leaf IDs, which would raise an exception when using the join method
+ leafs.extend(str(leaf).split("-"))
+ if len(leafs) == 1:
+ if interface_type in ["vpc", "fex_vpc"]:
+ aci.fail_json(msg='A interface_type of "vpc" requires 2 leafs')
+ leafs = leafs[0]
+ elif len(leafs) == 2:
+ if interface_type not in ["vpc", "fex_vpc"]:
+ aci.fail_json(
+ msg='The interface_types "switch_port", "port_channel", and "fex" do not support using multiple leafs for a single binding'
+ )
+ leafs = "-".join(leafs)
+ else:
+ aci.fail_json(msg='The "leafs" parameter must not have more than 2 entries')
+
+ if extpaths is not None:
+ # Process extpaths, and support dash-delimited extpaths
+ extpaths = []
+ for extpath in interface_config.get("extpaths"):
+ # Users are likely to use integers for extpaths IDs, which would raise an exception when using the join method
+ extpaths.extend(str(extpath).split("-"))
+ if len(extpaths) == 1:
+ if interface_type == "fex_vpc":
+ aci.fail_json(msg='A interface_type of "fex_vpc" requires 2 extpaths')
+ extpaths = extpaths[0]
+ elif len(extpaths) == 2:
+ if interface_type != "fex_vpc":
+ aci.fail_json(msg='The interface_types "fex" and "fex_port_channel" do not support using multiple extpaths for a single binding')
+ extpaths = "-".join(extpaths)
+ else:
+ aci.fail_json(msg='The "extpaths" parameter must not have more than 2 entries')
+
+ if encap_id is not None:
+ if encap_id not in range(1, 4097):
+ aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096")
+ encap_id = "vlan-{0}".format(encap_id)
+
+ if primary_encap_id is not None:
+ try:
+ primary_encap_id = int(primary_encap_id)
+ if isinstance(primary_encap_id, int) and primary_encap_id in range(1, 4097):
+ primary_encap_id = "vlan-{0}".format(primary_encap_id)
+ else:
+ aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096 or unknown.")
+ except Exception as e:
+ if isinstance(primary_encap_id, str) and primary_encap_id != "unknown":
+ aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096 or unknown. %s" % e)
+
+ static_path = INTERFACE_TYPE_MAPPING[interface_type].format(pod_id=pod_id, leafs=leafs, extpaths=extpaths, interface=interface)
+
+ interface_mode = INTERFACE_MODE_MAPPING.get(interface_mode)
+
+ interface_status = INTERFACE_STATUS_MAPPING.get(state)
+
+ children.append(
+ dict(
+ fvRsPathAtt=dict(
+ attributes=dict(
+ descr=description,
+ encap=encap_id,
+ primaryEncap=primary_encap_id,
+ instrImedcy=deploy_immediacy,
+ mode=interface_mode,
+ tDn=static_path,
+ status=interface_status,
+ )
+ )
+ )
+ )
+
+ aci.payload(
+ aci_class="fvAEPg",
+ class_config=dict(),
+ child_configs=children,
+ )
+
+ aci.get_diff(aci_class="fvAEPg")
+
+ aci.post_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ap.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ap.py
new file mode 100644
index 00000000..dd5593c2
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ap.py
@@ -0,0 +1,309 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, nkatarmal-crest (@nirav.katarmal)
+# Copyright: (c) 2021, Cindy Zhao (@cizhao)
+# 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
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_ap
+short_description: Manage Cloud Application Profile (AP) (cloud:App)
+description:
+- Manage Cloud Application Profile (AP) objects on Cisco Cloud ACI
+options:
+ description:
+ description:
+ - Description for the cloud application profile.
+ aliases: [ descr ]
+ type: str
+ name:
+ description:
+ - The name of the cloud application profile.
+ aliases: [ app_profile, app_profile_name, ap ]
+ type: str
+ tenant:
+ description:
+ - The name of an existing tenant.
+ aliases: [ tenant_name ]
+ type: str
+ 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
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- More information about the internal APIC class B(cloud:App) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Nirav (@nirav)
+- Cindy Zhao (@cizhao)
+"""
+
+EXAMPLES = r"""
+- name: Add a new cloud AP
+ cisco.aci.aci_cloud_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ name: intranet
+ description: Web Intranet EPG
+ state: present
+ delegate_to: localhost
+
+- name: Remove a cloud AP
+ cisco.aci.aci_cloud_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ tenant: production
+ name: intranet
+ state: absent
+ delegate_to: localhost
+
+- name: Query a cloud AP
+ cisco.aci.aci_cloud_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ name: ticketing
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all cloud APs
+ cisco.aci.aci_cloud_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all cloud APs with a Specific Name
+ cisco.aci.aci_cloud_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ name: ticketing
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all cloud APs of a tenant
+ cisco.aci.aci_cloud_ap:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ description=dict(type="str", aliases=["descr"]),
+ name=dict(type="str", aliases=["app_profile", "app_profile_name", "ap"]),
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ [
+ "state",
+ "absent",
+ [
+ "name",
+ "tenant",
+ ],
+ ],
+ [
+ "state",
+ "present",
+ [
+ "name",
+ "tenant",
+ ],
+ ],
+ ],
+ )
+
+ description = module.params["description"]
+ name = module.params["name"]
+ tenant = module.params["tenant"]
+ state = module.params["state"]
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ target_filter={"name": tenant},
+ module_object=tenant,
+ ),
+ subclass_1=dict(
+ aci_class="cloudApp",
+ aci_rn="cloudapp-{0}".format(name),
+ target_filter={"name": name},
+ module_object=name,
+ ),
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudApp",
+ class_config=dict(
+ descr=description,
+ name=name,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudApp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_aws_provider.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_aws_provider.py
new file mode 100644
index 00000000..d4ff55b0
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_aws_provider.py
@@ -0,0 +1,290 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_aws_provider
+short_description: Manage Cloud AWS Provider (cloud:AwsProvider)
+description:
+- Manage AWS provider on Cisco Cloud ACI.
+author:
+- Shreyas Srish (@shrsr)
+options:
+ access_key_id:
+ description:
+ - Cloud Access Key ID.
+ type: str
+ account_id:
+ description:
+ - AWS Account ID.
+ type: str
+ is_account_in_org:
+ description:
+ - Is Account in Organization.
+ type: bool
+ is_trusted:
+ description:
+ - Trusted Tenant
+ type: bool
+ secret_access_key:
+ description:
+ - Cloud Secret Access Key.
+ type: str
+ tenant:
+ description:
+ - Name of tenant.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+ - cisco.aci.aci
+ - cisco.aci.annotation
+ - cisco.aci.owner
+
+notes:
+ - More information about the internal APIC class B(cloud:AwsProvider) from
+ - L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+"""
+
+EXAMPLES = r"""
+- name: Create aws provider again after deletion as not trusted
+ cisco.aci.aci_cloud_aws_provider:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: ansible_test
+ account_id: 111111111111
+ is_trusted: yes
+ state: present
+ delegate_to: localhost
+
+- name: Delete aws provider
+ cisco.aci.aci_cloud_aws_provider:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: ansible_test
+ account_id: 111111111111
+ is_trusted: yes
+ state: absent
+ delegate_to: localhost
+
+- name: Query aws provider
+ cisco.aci.aci_cloud_aws_provider:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ state: query
+ delegate_to: localhost
+
+- name: Query all aws provider
+ cisco.aci.aci_cloud_aws_provider:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ {
+ "access_key_id": dict(type="str"),
+ "account_id": dict(type="str"),
+ "is_account_in_org": dict(type="bool"),
+ "is_trusted": dict(type="bool"),
+ "secret_access_key": dict(type="str", no_log=True),
+ "tenant": dict(type="str"),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant"]],
+ ["state", "present", ["tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ access_key_id = module.params.get("access_key_id")
+ account_id = module.params.get("account_id")
+ annotation = module.params.get("annotation")
+ is_account_in_org = aci.boolean(module.params.get("is_account_in_org"))
+ is_trusted = aci.boolean(module.params.get("is_trusted"))
+ secret_access_key = module.params.get("secret_access_key")
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ child_configs = []
+
+ aci.construct_url(
+ root_class={
+ "aci_class": "fvTenant",
+ "aci_rn": "tn-{0}".format(tenant),
+ "target_filter": 'eq(fvTenant.name, "{0}")'.format(tenant),
+ "module_object": tenant,
+ },
+ subclass_1={
+ "aci_class": "cloudAwsProvider",
+ "aci_rn": "awsprovider",
+ "target_filter": {"account_id": account_id},
+ "module_object": account_id,
+ },
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudAwsProvider",
+ class_config={
+ "accessKeyId": access_key_id,
+ "accountId": account_id,
+ "annotation": annotation,
+ "isAccountInOrg": is_account_in_org,
+ "isTrusted": is_trusted,
+ "secretAccessKey": secret_access_key,
+ },
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudAwsProvider")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_bgp_asn.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_bgp_asn.py
new file mode 100644
index 00000000..90d4dd77
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_bgp_asn.py
@@ -0,0 +1,250 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Anvitha Jain (@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_bgp_asn
+short_description: Manage Cloud APIC BGP Autonomous System Profile (cloud:BgpAsP)
+description:
+- Manage the BGP Autonomous System Profile for Cloud APIC
+author:
+- Anvitha Jain (@anvitha-jain)
+version_added: '2.1.0'
+options:
+ asn:
+ description:
+ - The BGP Autonomous System Number.
+ type: str
+ description:
+ description:
+ - Description of the BGP Autonomous System Profile.
+ aliases: [ descr ]
+ type: str
+ name:
+ description:
+ - object name
+ aliases: [ autonomous_system_profile, autonomous_system_profile_name ]
+ type: str
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ 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
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- More information about the internal APIC class B(cloud:BgpAsP) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+"""
+
+EXAMPLES = r"""
+- name: Add a new cloud BGP ASN
+ cisco.aci.aci_cloud_bgp_asn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ asn: 64601
+ description: ASN description
+ name: ASN_1
+ state: present
+ delegate_to: localhost
+- name: Remove a cloud BGP ASN
+ cisco.aci.aci_cloud_bgp_asn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ state: absent
+ delegate_to: localhost
+- name: Query a cloud BGP ASN
+ cisco.aci.aci_cloud_bgp_asn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ {
+ "asn": dict(type="str"),
+ "description": dict(type="str", aliases=["descr"]),
+ "name": dict(type="str", aliases=["autonomous_system_profile", "autonomous_system_profile_name"]),
+ "name_alias": dict(type="str"),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ annotation = module.params["annotation"]
+ asn = module.params["asn"]
+ description = module.params["description"]
+ name = module.params["name"]
+ name_alias = module.params["name_alias"]
+ state = module.params["state"]
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class={"aci_class": "cloudBgpAsP", "aci_rn": "clouddomp/as", "target_filter": {"name": name}, "module_object": None}, child_classes=[]
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudBgpAsP",
+ class_config={
+ "annotation": annotation,
+ "asn": asn,
+ "descr": description,
+ "name": name,
+ "nameAlias": name_alias,
+ },
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudBgpAsP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py
new file mode 100644
index 00000000..4261a054
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_cidr.py
@@ -0,0 +1,287 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com>
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_cidr
+short_description: Manage CIDR under Cloud Context Profile (cloud:Cidr)
+description:
+- Manage Cloud CIDR on Cisco Cloud ACI.
+author:
+- Nirav (@nirav)
+- Cindy Zhao (@cizhao)
+options:
+ address:
+ description:
+ - CIDR ip and its netmask.
+ type: str
+ aliases: [ cidr ]
+ description:
+ description:
+ - Description of the Cloud CIDR.
+ type: str
+ name_alias:
+ description:
+ - An alias for the name of the current object. This relates to the nameAlias field in ACI and is used to rename object without changing the DN.
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ required: yes
+ cloud_context_profile:
+ description:
+ - The name of the Cloud Context Profile.
+ type: str
+ required: yes
+ 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
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- This module is only used to manage non_primary Cloud CIDR, see M(cisco.aci.aci_cloud_ctx_profile) to create the primary CIDR.
+- More information about the internal APIC class B(cloud:Cidr) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+"""
+
+EXAMPLES = r"""
+- name: Create non_primary CIDR
+ cisco.aci.aci_cloud_cidr:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ address: 10.10.0.0/16
+ cloud_context_profile: ctxProfileName
+ state: present
+ delegate_to: localhost
+
+- name: Remove non_primary CIDR
+ cisco.aci.aci_cloud_cidr:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ address: 10.10.0.0/16
+ cloud_context_profile: ctxProfileName
+ state: absent
+ delegate_to: localhost
+
+- name: Query all CIDRs under given cloud context profile
+ cisco.aci.aci_cloud_cidr:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ cloud_context_profile: ctxProfileName
+ state: query
+ delegate_to: localhost
+
+- name: Query specific CIDR under given cloud context profile
+ cisco.aci.aci_cloud_cidr:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ cloud_context_profile: ctxProfileName
+ address: 10.10.0.0/16
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ address=dict(type="str", aliases=["cidr"]),
+ description=dict(type="str"),
+ name_alias=dict(type="str"),
+ tenant=dict(type="str", required=True),
+ cloud_context_profile=dict(type="str", required=True),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["address"]],
+ ["state", "present", ["address"]],
+ ],
+ )
+
+ address = module.params.get("address")
+ description = module.params.get("description")
+ name_alias = module.params.get("name_alias")
+ tenant = module.params.get("tenant")
+ cloud_context_profile = module.params.get("cloud_context_profile")
+ state = module.params.get("state")
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(aci_class="fvTenant", aci_rn="tn-{0}".format(tenant), target_filter='eq(fvTenant.name, "{0}")'.format(tenant), module_object=tenant),
+ subclass_1=dict(
+ aci_class="cloudCtxProfile",
+ aci_rn="ctxprofile-{0}".format(cloud_context_profile),
+ target_filter='eq(cloudCtxProfile.name, "{0}")'.format(cloud_context_profile),
+ module_object=cloud_context_profile,
+ ),
+ subclass_2=dict(
+ aci_class="cloudCidr", aci_rn="cidr-[{0}]".format(address), target_filter='eq(cloudCidr.addr, "{0}")'.format(address), module_object=address
+ ),
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudCidr",
+ class_config=dict(
+ addr=address,
+ descr=description,
+ nameAlias=name_alias,
+ primary="no",
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudCidr")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py
new file mode 100644
index 00000000..82c4ebfd
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_ctx_profile.py
@@ -0,0 +1,309 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com>
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_ctx_profile
+short_description: Manage Cloud Context Profile (cloud:CtxProfile)
+description:
+- Manage the Cloud Context Profile objects on Cisco Cloud ACI.
+notes:
+- More information about the internal APIC class B(cloud:CtxProfile) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Nirav (@crestdatasys)
+- Cindy Zhao (@cizhao)
+options:
+ name:
+ description:
+ - The name of the Cloud Context Profile
+ type: str
+ aliases: [ cloud_context_profile ]
+ description:
+ description:
+ - Description of the Cloud Context Profile
+ type: str
+ name_alias:
+ description:
+ - An alias for the name of the current object. This relates to the nameAlias field in ACI and is used to rename object without changing the DN
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ primary_cidr:
+ description:
+ - The subnet with netmask to use as primary CIDR block for the Cloud Context Profile.
+ type: str
+ vrf:
+ description:
+ - The name of the VRF.
+ type: str
+ region:
+ description:
+ - The name of the cloud region in which to deploy the network construct.
+ type: str
+ cloud:
+ description:
+ - The cloud vendor in which the controller runs.
+ choices: [ aws, azure ]
+ type: str
+ 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 ]
+ type: str
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+"""
+
+EXAMPLES = r"""
+- name: Add a new aci cloud ctx profile
+ cisco.aci.aci_cloud_ctx_profile:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant_1
+ name: cloud_ctx_profile
+ vrf: VRF1
+ region: us-west-1
+ cloud: aws
+ primary_cidr: '10.0.10.1/16'
+ state: present
+ delegate_to: localhost
+
+- name: Remove an aci cloud ctx profile
+ cisco.aci.aci_cloud_ctx_profile:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant_1
+ name: cloud_ctx_profile
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific aci cloud ctx profile
+ cisco.aci.aci_cloud_ctx_profile:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ name: ctx_profile_1
+ state: query
+ delegate_to: localhost
+
+- name: Query all aci cloud ctx profile
+ cisco.aci.aci_cloud_ctx_profile:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ description=dict(
+ type="str",
+ ),
+ name=dict(type="str", aliases=["cloud_context_profile"]),
+ name_alias=dict(
+ type="str",
+ ),
+ tenant=dict(
+ type="str",
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ primary_cidr=dict(
+ type="str",
+ ),
+ # FIXME: didn't find the flow_log in UI
+ # flow_log=dict(type='str'),
+ vrf=dict(type="str"),
+ region=dict(type="str"),
+ cloud=dict(type="str", choices=["aws", "azure"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name", "tenant"]],
+ ["state", "present", ["name", "tenant", "vrf", "region", "primary_cidr", "cloud"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ name = module.params.get("name")
+ name_alias = module.params.get("name_alias")
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ primary_cidr = module.params.get("primary_cidr")
+ child_configs = []
+
+ vrf = module.params.get("vrf")
+ region = module.params.get("region")
+ cloud = module.params.get("cloud")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(aci_class="fvTenant", aci_rn="tn-{0}".format(tenant), target_filter='eq(fvTenant.name, "{0}")'.format(tenant), module_object=tenant),
+ subclass_1=dict(
+ aci_class="cloudCtxProfile", aci_rn="ctxprofile-{0}".format(name), target_filter='eq(cloudCtxProfile.name, "{0}")'.format(name), module_object=name
+ ),
+ child_classes=["cloudRsToCtx", "cloudRsCtxProfileToRegion", "cloudRouterP", "cloudCidr"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs.append(dict(cloudRsToCtx=dict(attributes=dict(tnFvCtxName=vrf))))
+ child_configs.append(dict(cloudRsCtxProfileToRegion=dict(attributes=dict(tDn="uni/clouddomp/provp-{0}/region-{1}".format(cloud, region)))))
+ child_configs.append(dict(cloudCidr=dict(attributes=dict(addr=primary_cidr, primary="yes"))))
+ aci.payload(
+ aci_class="cloudCtxProfile",
+ class_config=dict(
+ descr=description,
+ name=name,
+ name_alias=name_alias,
+ type="regular",
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudCtxProfile")
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg.py
new file mode 100644
index 00000000..d705b9bc
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_epg
+short_description: Manage Cloud EPG (cloud:EPg)
+description:
+- Manage Cloud EPG on Cisco Cloud ACI
+options:
+ tenant:
+ description:
+ - The name of the existing tenant.
+ type: str
+ ap:
+ description:
+ - The name of the cloud application profile.
+ aliases: [ app_profile, app_profile_name ]
+ type: str
+ name:
+ description:
+ - The name of the cloud EPG.
+ aliases: [ cloud_epg, cloud_epg_name, epg, epg_name ]
+ type: str
+ description:
+ description:
+ - Description of the cloud EPG.
+ aliases: [ descr ]
+ type: str
+ vrf:
+ description:
+ - The name of the VRF.
+ type: str
+ aliases: [ context, vrf_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ choices: [ absent, present, query ]
+ default: present
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- More information about the internal APIC class B(cloud:EPg) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Nirav (@nirav)
+- Cindy Zhao (@cizhao)
+"""
+
+EXAMPLES = r"""
+- name: Create aci cloud epg (check_mode)
+ cisco.aci.aci_cloud_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ vrf: vrfName
+ description: Aci Cloud EPG
+ name: epgName
+ state: present
+ delegate_to: localhost
+
+- name: Remove cloud epg
+ cisco.aci.aci_cloud_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ name: cloudName
+ state: absent
+ delegate_to: localhost
+
+- name: query all
+ cisco.aci.aci_cloud_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ state: query
+ delegate_to: localhost
+
+- name: query a specific cloud epg
+ cisco.aci.aci_cloud_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ name: epgName
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ {
+ "description": dict(type="str", aliases=["descr"]),
+ "name": dict(type="str", aliases=["cloud_epg", "cloud_epg_name", "epg", "epg_name"]),
+ "tenant": dict(
+ type="str",
+ ),
+ "ap": dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ "vrf": dict(type="str", aliases=["context", "vrf_name"]),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name", "tenant", "ap"]],
+ ["state", "present", ["name", "tenant", "ap"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ name = module.params.get("name")
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ state = module.params.get("state")
+ child_configs = []
+ relation_vrf = module.params.get("vrf")
+
+ if relation_vrf:
+ child_configs.append({"cloudRsCloudEPgCtx": {"attributes": {"tnFvCtxName": relation_vrf}}})
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class={
+ "aci_class": "fvTenant",
+ "aci_rn": "tn-{0}".format(tenant),
+ "target_filter": 'eq(fvTenant.name, "{0}")'.format(tenant),
+ "module_object": tenant,
+ },
+ subclass_1={"aci_class": "cloudApp", "aci_rn": "cloudapp-{0}".format(ap), "target_filter": 'eq(cloudApp.name, "{0}")'.format(ap), "module_object": ap},
+ subclass_2={
+ "aci_class": "cloudEPg",
+ "aci_rn": "cloudepg-{0}".format(name),
+ "target_filter": 'eq(cloudEPg.name, "{0}")'.format(name),
+ "module_object": name,
+ },
+ child_classes=["cloudRsCloudEPgCtx"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudEPg",
+ class_config={
+ "descr": description,
+ "name": name,
+ },
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudEPg")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg_selector.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg_selector.py
new file mode 100644
index 00000000..d5d3369a
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_epg_selector.py
@@ -0,0 +1,371 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_epg_selector
+short_description: Manage Cloud Endpoint Selector (cloud:EPSelector)
+description:
+- Manage Cloud Endpoint Selector on Cisco Cloud ACI
+notes:
+- More information about the internal APIC class B(cloud:EPSelector) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Cindy Zhao (@cizhao)
+options:
+ description:
+ description:
+ - Description of the Cloud Endpoint Selector.
+ type: str
+ expressions:
+ description:
+ - Expressions associated to this selector.
+ type: list
+ elements: dict
+ suboptions:
+ key:
+ description:
+ - The key of the expression.
+ - The key is custom or is one of region, zone and ip
+ - The key can be zone only when the site is AWS.
+ required: true
+ type: str
+ operator:
+ description:
+ - The operator associated to the expression.
+ - Operator C(has_key) or C(does_not_have_key) is only available for key custom or zone
+ required: true
+ type: str
+ choices: [ not_in, in, equals, not_equals, has_key, does_not_have_key ]
+ value:
+ description:
+ - The value associated to the expression.
+ - If the operator is C(in) or C(not_in), the value should be a comma separated string.
+ type: str
+ name:
+ description:
+ - The name of the Cloud Endpoint selector.
+ aliases: [ selector, selector_name ]
+ type: str
+ tenant:
+ description:
+ - The name of the existing tenant.
+ required: yes
+ type: str
+ ap:
+ description:
+ - The name of the cloud application profile.
+ required: yes
+ type: str
+ epg:
+ description:
+ - The name of the cloud EPG.
+ required: yes
+ type: str
+ 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
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+"""
+
+EXAMPLES = r"""
+- name: Create aci cloud epg selector
+ cisco.aci.aci_cloud_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ epg: epgName
+ description: cloud epg selector
+ name: selectorName
+ expressions:
+ - key: ip
+ operator: in
+ value: 10.10.10.1
+ state: present
+ delegate_to: localhost
+
+- name: Remove cloud epg selector
+ cisco.aci.aci_cloud_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ epg: epgName
+ name: selectorName
+ state: absent
+ delegate_to: localhost
+
+- name: query all
+ cisco.aci.aci_cloud_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ epg: epgName
+ state: query
+ delegate_to: localhost
+
+- name: query a specific cloud epg selector
+ cisco.aci.aci_cloud_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ ap: apName
+ epg: epgName
+ name: selectorName
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, expression_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+EXPRESSION_KEYS = {
+ "ip": "IP",
+ "region": "Region",
+ "zone": "Zone",
+}
+
+EXPRESSION_OPERATORS = {
+ "not_in": "notin",
+ "not_equals": "!=",
+ "in": "in",
+ "equals": "==",
+}
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ {
+ "description": dict(type="str"),
+ "expressions": dict(type="list", elements="dict", options=expression_spec()),
+ "name": dict(type="str", aliases=["selector", "selector_name"]),
+ "tenant": dict(type="str", required=True),
+ "ap": dict(type="str", required=True),
+ "epg": dict(type="str", required=True),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ expressions = module.params.get("expressions")
+ name = module.params.get("name")
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ epg = module.params.get("epg")
+ state = module.params.get("state")
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class={
+ "aci_class": "fvTenant",
+ "aci_rn": "tn-{0}".format(tenant),
+ "target_filter": 'eq(fvTenant.name, "{0}")'.format(tenant),
+ "module_object": tenant,
+ },
+ subclass_1={"aci_class": "cloudApp", "aci_rn": "cloudapp-{0}".format(ap), "target_filter": 'eq(cloudApp.name, "{0}")'.format(ap), "module_object": ap},
+ subclass_2={
+ "aci_class": "cloudEPg",
+ "aci_rn": "cloudepg-{0}".format(epg),
+ "target_filter": 'eq(cloudEPg.name, "{0}")'.format(epg),
+ "module_object": epg,
+ },
+ subclass_3={
+ "aci_class": "cloudEPSelector",
+ "aci_rn": "epselector-{0}".format(name),
+ "target_filter": 'eq(cloudEPSelector.name, "{0}")'.format(name),
+ "module_object": name,
+ },
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ expressions_list = []
+ for expression in expressions:
+ key = expression.get("key")
+ operator = expression.get("operator")
+ if expression.get("value"):
+ value = "'" + "','".join(expression.get("value").split(",")) + "'"
+ else:
+ value = None
+ if operator in ["has_key", "does_not_have_key"]:
+ if value:
+ module.fail_json(msg="Attribute 'value' is not supported for operator '{0}' in expression '{1}'".format(operator, key))
+ if key in ["ip", "region"]:
+ module.fail_json(msg="Operator '{0}' is not supported when expression key is '{1}'".format(operator, key))
+ if operator in ["not_in", "in", "equals", "not_equals"] and not value:
+ module.fail_json(msg="Attribute 'value' needed for operator '{0}' in expression '{1}'".format(operator, key))
+ if key in ["ip", "region", "zone"]:
+ key = EXPRESSION_KEYS.get(key)
+ else:
+ key = "custom:" + key
+ if operator in ["not_in", "in"]:
+ expressions_list.append("{0} {1}({2})".format(key, EXPRESSION_OPERATORS.get(operator), value))
+ elif operator in ["equals", "not_equals"]:
+ expressions_list.append("{0}{1}{2}".format(key, EXPRESSION_OPERATORS.get(operator), value))
+ elif operator == "does_not_have_key":
+ expressions_list.append("!{0}".format(key))
+ else:
+ expressions_list.append(key)
+ matchExpression = ",".join(expressions_list)
+ aci.payload(
+ aci_class="cloudEPSelector",
+ class_config={
+ "descr": description,
+ "matchExpression": matchExpression,
+ "name": name,
+ },
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudEPSelector")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg.py
new file mode 100644
index 00000000..87840139
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg.py
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Anvitha Jain (@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_external_epg
+short_description: Manage Cloud External EPG (cloud:ExtEPg)
+description:
+- Configures WAN router connectivity to the cloud infrastructure.
+options:
+ description:
+ description:
+ - configuration item description.
+ aliases: [ descr ]
+ type: str
+ name:
+ description:
+ - Name of Object cloud_external_epg.
+ aliases: [ cloud_external_epg, cloud_external_epg_name, external_epg, external_epg_name, extepg, extepg_name ]
+ type: str
+ route_reachability:
+ description:
+ - Route reachability for this EPG.
+ choices: [ inter-site, internet, unspecified ]
+ type: str
+ tenant:
+ description:
+ - The name of tenant.
+ type: str
+ ap:
+ description:
+ - The name of the cloud application profile.
+ aliases: [ app_profile, app_profile_name ]
+ type: str
+ vrf:
+ description:
+ - The name of the VRF.
+ type: str
+ aliases: [ context, vrf_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ choices: [ absent, present, query ]
+ default: present
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- More information about the internal APIC class B(cloud:ExtEPg) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Anvitha Jain (@anvitha-jain)
+"""
+
+EXAMPLES = r"""
+- name: Add a new cloud external EPG
+ cisco.aci.aci_cloud_external_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant1
+ ap: ap1
+ vrf: vrf1
+ description: Cloud External EPG description
+ name: ext_epg
+ route_reachability: internet
+ state: present
+ delegate_to: localhost
+
+- name: Remove a cloud external EPG
+ cisco.aci.aci_cloud_external_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ tenant: tenant1
+ ap: ap1
+ name: ext_epg
+ state: absent
+ delegate_to: localhost
+
+- name: Query a cloud external EPG
+ cisco.aci.aci_cloud_external_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant1
+ ap: ap1
+ name: ext_epg
+ state: query
+ delegate_to: localhost
+
+- name: query all
+ cisco.aci.aci_cloud_external_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant1
+ ap: ap1
+ state: query
+ delegate_to: localhost
+"""
+
+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",
+ "name_alias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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",
+ "name_alias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ {
+ "description": dict(type="str", aliases=["descr"]),
+ "name": dict(type="str", aliases=["cloud_external_epg", "cloud_external_epg_name", "external_epg", "external_epg_name", "extepg", "extepg_name"]),
+ "route_reachability": dict(type="str", choices=["inter-site", "internet", "unspecified"]),
+ "tenant": dict(type="str"),
+ "ap": dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ "vrf": dict(type="str", aliases=["context", "vrf_name"]),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name", "tenant", "ap"]],
+ ["state", "present", ["name", "tenant", "ap"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ name = module.params.get("name")
+ route_reachability = module.params.get("route_reachability")
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ state = module.params.get("state")
+ child_configs = []
+ relation_vrf = module.params.get("vrf")
+
+ if relation_vrf:
+ child_configs.append({"cloudRsCloudEPgCtx": {"attributes": {"tnFvCtxName": relation_vrf}}})
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class={
+ "aci_class": "fvTenant",
+ "aci_rn": "tn-{0}".format(tenant),
+ "target_filter": 'eq(fvTenant.name, "{0}")'.format(tenant),
+ "module_object": tenant,
+ },
+ subclass_1={"aci_class": "cloudApp", "aci_rn": "cloudapp-{0}".format(ap), "target_filter": 'eq(cloudApp.name, "{0}")'.format(ap), "module_object": ap},
+ subclass_2={
+ "aci_class": "cloudExtEPg",
+ "aci_rn": "cloudextepg-{0}".format(name),
+ "target_filter": 'eq(cloudExtEPg.name, "{0}")'.format(name),
+ "module_object": name,
+ },
+ child_classes=["fvRsCustQosPol", "cloudRsCloudEPgCtx"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudExtEPg",
+ class_config={
+ "descr": description,
+ "name": name,
+ "routeReachability": route_reachability,
+ },
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudExtEPg")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg_selector.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg_selector.py
new file mode 100644
index 00000000..cf2fe5fd
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_external_epg_selector.py
@@ -0,0 +1,183 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Anvitha Jain (@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_external_epg_selector
+short_description: Manage Cloud Endpoint Selector for External EPGs (cloud:ExtEPSelector)
+description:
+- Decides which endpoints belong to the EPGs based on several parameters.
+options:
+ name:
+ description:
+ - The name of the Cloud Endpoint selector.
+ aliases: [ selector, cloud_external_epg_selector, external_epg_selector, extepg_selector, selector_name ]
+ type: str
+ subnet:
+ description:
+ - IP address of the Cloud Subnet.
+ aliases: [ ip ]
+ type: str
+ tenant:
+ description:
+ - The name of tenant.
+ type: str
+ ap:
+ description:
+ - The name of the cloud application profile.
+ aliases: [ app_profile, app_profile_name ]
+ type: str
+ cloud_external_epg:
+ description:
+ - Name of Object cloud_external_epg.
+ type: str
+ 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
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- More information about the internal APIC class B(cloud:ExtEPSelector) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Anvitha Jain (@anvitha-jain)
+"""
+
+EXAMPLES = r"""
+- name: Add a new cloud external EPG selector
+ cisco.aci.aci_cloud_external_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant1
+ ap: ap1
+ cloud_external_epg: ext_epg
+ name: subnet_name
+ subnet: 10.0.0.0/16
+ state: present
+ delegate_to: localhost
+
+- name: Remove a cloud external EPG selector
+ cisco.aci.aci_cloud_external_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ tenant: tenant1
+ ap: ap1
+ cloud_external_epg: ext_epg
+ name: subnet_name
+ subnet: 10.0.0.0/16
+ state: absent
+ delegate_to: localhost
+
+- name: Query all cloud external EPG selectors
+ cisco.aci.aci_cloud_external_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenant1
+ ap: ap1
+ cloud_external_epg: ext_epg
+ state: query
+ delegate_to: localhost
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ {
+ "name": dict(type="str", aliases=["selector", "cloud_external_epg_selector", "external_epg_selector", "extepg_selector", "selector_name"]),
+ "subnet": dict(type="str", aliases=["ip"]),
+ "tenant": dict(type="str"),
+ "cloud_external_epg": dict(type="str"),
+ "ap": dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["subnet", "tenant", "ap", "cloud_external_epg"]],
+ ["state", "present", ["subnet", "tenant", "ap", "cloud_external_epg"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ subnet = module.params.get("subnet")
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ cloud_external_epg = module.params.get("cloud_external_epg")
+ state = module.params.get("state")
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class={
+ "aci_class": "fvTenant",
+ "aci_rn": "tn-{0}".format(tenant),
+ "target_filter": 'eq(fvTenant.name, "{0}")'.format(tenant),
+ "module_object": tenant,
+ },
+ subclass_1={"aci_class": "cloudApp", "aci_rn": "cloudapp-{0}".format(ap), "target_filter": 'eq(cloudApp.name, "{0}")'.format(ap), "module_object": ap},
+ subclass_2={
+ "aci_class": "cloudExtEPg",
+ "aci_rn": "cloudextepg-{0}".format(cloud_external_epg),
+ "target_filter": 'eq(cloudExtEPg.name, "{0}")'.format(cloud_external_epg),
+ "module_object": cloud_external_epg,
+ },
+ subclass_3={
+ "aci_class": "cloudExtEPSelector",
+ "aci_rn": "extepselector-[{0}]".format(subnet),
+ "target_filter": 'eq(cloudExtEPSelector.name, "{0}")'.format(subnet),
+ "module_object": subnet,
+ },
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cloudExtEPSelector",
+ class_config={
+ "name": name,
+ "subnet": subnet,
+ },
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudExtEPSelector")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py
new file mode 100644
index 00000000..970ec442
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_provider.py
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_provider
+short_description: Query Cloud Provider information (cloud:ProvP)
+description:
+- Query Cloud Provider information (cloud:ProvP) on Cisco Cloud ACI.
+author:
+- Lionel Hercot (@lhercot)
+options:
+ state:
+ description:
+ - Use C(query) for listing an object or multiple objects.
+ choices: [ query ]
+ default: query
+ type: str
+
+notes:
+- More information about the internal APIC class B(cloud:ProvP) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+- This module is used to query Cloud Provider information.
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+"""
+
+EXAMPLES = r"""
+- name: Query cloud provider information
+ cisco.aci.aci_cloud_provider:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ state=dict(type="str", default="query", choices=["query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ aci = ACIModule(module)
+ aci.construct_url(root_class=dict(aci_class="cloudProvP"))
+
+ aci.get_existing()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py
new file mode 100644
index 00000000..0cba9d1b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_region.py
@@ -0,0 +1,216 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com>
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_region
+short_description: Manage Cloud Providers Region (cloud:Region)
+description:
+- Manage Cloud Providers Region on Cisco Cloud ACI.
+author:
+- Nirav (@nirav)
+- Cindy Zhao (@cizhao)
+options:
+ region:
+ description:
+ - The name of the cloud provider's region.
+ aliases: [ name ]
+ type: str
+ cloud:
+ description:
+ - The vendor of the controller
+ choices: [ aws, azure ]
+ type: str
+ required: yes
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ choices: [ query ]
+ default: query
+ type: str
+
+notes:
+- More information about the internal APIC class B(cloud:Region) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+- This module is used to query Cloud Providers Region.
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+"""
+
+EXAMPLES = r"""
+- name: Query all regions
+ cisco.aci.aci_cloud_region:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ cloud: 'aws'
+ state: query
+ delegate_to: localhost
+
+- name: Query a specific region
+ cisco.aci.aci_cloud_region:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ cloud: 'aws'
+ region: eu-west-2
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ region=dict(type="str", aliases=["name"]),
+ cloud=dict(type="str", choices=["aws", "azure"], required=True),
+ state=dict(type="str", default="query", choices=["query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ region = module.params.get("region")
+ cloud = module.params.get("cloud")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="cloudProvP", aci_rn="clouddomp/provp-{0}".format(cloud), target_filter='eq(cloudProvP.vendor, "{0}")'.format(cloud), module_object=cloud
+ ),
+ subclass_1=dict(
+ aci_class="cloudRegion", aci_rn="region-{0}".format(region), target_filter='eq(cloudRegion.name, "{0}")'.format(region), module_object=region
+ ),
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py
new file mode 100644
index 00000000..d759f448
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_subnet.py
@@ -0,0 +1,320 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com>
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_subnet
+short_description: Manage Cloud Subnet (cloud:Subnet)
+description:
+- Manage Cloud Subnet on Cisco Cloud ACI.
+notes:
+- More information about the internal APIC class B(cloud:Subnet) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Nirav (@nirav)
+- Cindy Zhao (@cizhao)
+options:
+ name:
+ description:
+ - The name of the Cloud Subnet.
+ type: str
+ description:
+ description:
+ - Description of the Cloud Subnet.
+ type: str
+ address:
+ description:
+ - Ip address of the Cloud Subnet.
+ type: str
+ aliases: [subnet]
+ name_alias:
+ description:
+ - An alias for the name of the current object. This relates to the nameAlias field in ACI and is used to rename object without changing the DN.
+ type: str
+ tenant:
+ description:
+ - The name of tenant.
+ type: str
+ required: yes
+ cloud_context_profile:
+ description:
+ - The name of cloud context profile.
+ type: str
+ required: yes
+ cidr:
+ description:
+ - Address of cloud cidr.
+ type: str
+ required: yes
+ availability_zone:
+ description:
+ - The cloud zone which is attached to the given cloud context profile.
+ - Only used when it is an aws cloud apic.
+ type: str
+ vnet_gateway:
+ description:
+ - Determine if a vNet Gateway Router will be deployed or not.
+ - Only used when it is an azure cloud apic.
+ type: bool
+ default: false
+ 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
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+"""
+
+EXAMPLES = r"""
+- name: Create aci cloud subnet
+ cisco.aci.aci_cloud_subnet:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ tenant: anstest
+ cloud_context_profile: aws_cloudCtxProfile
+ cidr: '10.10.0.0/16'
+ availability_zone: us-west-1a
+ address: 10.10.0.1
+ delegate_to: localhost
+
+- name: Query a specific subnet
+ cisco.aci.aci_cloud_subnet:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ tenant: anstest
+ cloud_context_profile: ctx_profile_1
+ cidr: '10.10.0.0/16'
+ address: 10.10.0.1
+ state: query
+ delegate_to: localhost
+
+- name: Query all subnets
+ cisco.aci.aci_cloud_subnet:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ tenant: anstest
+ cloud_context_profile: ctx_profile_1
+ cidr: '10.10.0.0/16'
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["subnet"]),
+ description=dict(type="str"),
+ address=dict(type="str"),
+ name_alias=dict(type="str"),
+ vnet_gateway=dict(type="bool", default=False),
+ tenant=dict(type="str", required=True),
+ cloud_context_profile=dict(type="str", required=True),
+ cidr=dict(type="str", required=True),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ availability_zone=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["address"]],
+ ["state", "present", ["address"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ address = module.params.get("address")
+ name_alias = module.params.get("name_alias")
+ vnet_gateway = module.params.get("vnet_gateway")
+ tenant = module.params.get("tenant")
+ cloud_context_profile = module.params.get("cloud_context_profile")
+ cidr = module.params.get("cidr")
+ state = module.params.get("state")
+ availability_zone = module.params.get("availability_zone")
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(aci_class="fvTenant", aci_rn="tn-{0}".format(tenant), target_filter='eq(fvTenant.name, "{0}")'.format(tenant), module_object=tenant),
+ subclass_1=dict(
+ aci_class="cloudCtxProfile",
+ aci_rn="ctxprofile-{0}".format(cloud_context_profile),
+ target_filter='eq(cloudCtxProfile.name, "{0}")'.format(cloud_context_profile),
+ module_object=cloud_context_profile,
+ ),
+ subclass_2=dict(aci_class="cloudCidr", aci_rn="cidr-[{0}]".format(cidr), target_filter='eq(cloudCidr.addr, "{0}")'.format(cidr), module_object=cidr),
+ subclass_3=dict(
+ aci_class="cloudSubnet", aci_rn="subnet-[{0}]".format(address), target_filter='eq(cloudSubnet.ip, "{0}")'.format(address), module_object=address
+ ),
+ child_classes=["cloudRsZoneAttach"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ # in aws cloud apic
+ if availability_zone:
+ region = availability_zone[:-1]
+ tDn = "uni/clouddomp/provp-aws/region-{0}/zone-{1}".format(region, availability_zone)
+ child_configs.append({"cloudRsZoneAttach": {"attributes": {"tDn": tDn}}})
+ # in azure cloud apic
+ if vnet_gateway:
+ usage = "gateway"
+ else:
+ usage = "user"
+
+ aci.payload(
+ aci_class="cloudSubnet",
+ class_config=dict(
+ name=name,
+ descr=description,
+ ip=address,
+ nameAlias=name_alias,
+ scope="private",
+ usage=usage,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="cloudSubnet")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py
new file mode 100644
index 00000000..6724e6e6
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_vpn_gateway.py
@@ -0,0 +1,235 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_vpn_gateway
+short_description: Manage cloudRouterP in Cloud Context Profile (cloud:cloudRouterP)
+description:
+- Manage cloudRouterP objects on Cisco Cloud ACI.
+notes:
+- More information about the internal APIC class B(cloud:cloudRouterP) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+author:
+- Cindy Zhao (@cizhao)
+options:
+ tenant:
+ description:
+ - The name of tenant.
+ type: str
+ required: yes
+ cloud_context_profile:
+ description:
+ - The name of cloud context profile.
+ type: str
+ required: yes
+ 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 ]
+ type: str
+ default: query
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+"""
+
+EXAMPLES = r"""
+- name: Enable VpnGateway
+ cisco.aci.aci_cloud_vpn_gateway:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_test
+ cloud_context_profile: ctx_profile_1
+ state: present
+ delegate_to: localhost
+
+- name: Disable VpnGateway
+ cisco.aci.aci_cloud_vpn_gateway:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_test
+ cloud_context_profile: ctx_profile_1
+ state: absent
+ delegate_to: localhost
+
+- name: Query VpnGateway
+ cisco.aci.aci_cloud_vpn_gateway:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_test
+ cloud_context_profile: ctx_profile_1
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", required=True),
+ cloud_context_profile=dict(type="str", required=True),
+ state=dict(type="str", default="query", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ tenant = module.params.get("tenant")
+ cloud_context_profile = module.params.get("cloud_context_profile")
+ state = module.params.get("state")
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(aci_class="fvTenant", aci_rn="tn-{0}".format(tenant), target_filter='eq(fvTenant.name, "{0}")'.format(tenant), module_object=tenant),
+ subclass_1=dict(
+ aci_class="cloudCtxProfile",
+ aci_rn="ctxprofile-{0}".format(cloud_context_profile),
+ target_filter='eq(cloudCtxProfile.name, "{0}")'.format(cloud_context_profile),
+ module_object=cloud_context_profile,
+ ),
+ subclass_2=dict(aci_class="cloudRouterP", aci_rn="routerp-default", target_filter='eq(cloudRouterP.name, "default")', module_object="default"),
+ child_classes=["cloudRsToVpnGwPol", "cloudRsToHostRouterPol", "cloudIntNetworkP"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs.append(dict(cloudIntNetworkP=dict(attributes=dict(name="default"))))
+ aci.payload(aci_class="cloudRouterP", class_config=dict(name="default"), child_configs=child_configs)
+
+ aci.get_diff(aci_class="cloudRouterP")
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py
new file mode 100644
index 00000000..f4f4dda5
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_cloud_zone.py
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, nkatarmal-crest <nirav.katarmal@crestdatasys.com>
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_cloud_zone
+short_description: Manage Cloud Availability Zone (cloud:Zone)
+description:
+- Manage Cloud Availability Zone on Cisco Cloud ACI.
+notes:
+- More information about the internal APIC class B(cloud:Zone) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+- This module is used to query Cloud Availability Zone.
+author:
+- Nirav (@nirav)
+- Cindy Zhao (@cizhao)
+options:
+ name:
+ description:
+ - object name
+ aliases: [ zone ]
+ type: str
+ cloud:
+ description:
+ - The cloud provider.
+ choices: [ aws, azure ]
+ type: str
+ required: yes
+ region:
+ description:
+ - The name of the cloud provider's region.
+ type: str
+ required: yes
+ state:
+ description:
+ - Use C(query) for listing an object or multiple objects.
+ choices: [ query ]
+ default: query
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+"""
+
+EXAMPLES = r"""
+- name: Query all zones in a region
+ cisco.aci.aci_cloud_zone:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ cloud: 'aws'
+ region: regionName
+ state: query
+ delegate_to: localhost
+
+- name: Query a specific zone
+ cisco.aci.aci_cloud_zone:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ cloud: 'aws'
+ region: regionName
+ zone: zoneName
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["zone"]),
+ cloud=dict(type="str", choices=["aws", "azure"], required=True),
+ region=dict(type="str", required=True),
+ state=dict(type="str", default="query", choices=["query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ name = module.params.get("name")
+ cloud = module.params.get("cloud")
+ region = module.params.get("region")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="cloudProvP", aci_rn="clouddomp/provp-{0}".format(cloud), target_filter='eq(cloudProvP.vendor, "{0}")'.format(cloud), module_object=cloud
+ ),
+ subclass_1=dict(
+ aci_class="cloudRegion", aci_rn="region-{0}".format(region), target_filter='eq(cloudRegion.name, "{0}")'.format(region), module_object=region
+ ),
+ subclass_2=dict(aci_class="cloudZone", aci_rn="zone-{0}".format(name), target_filter='eq(cloudZone.name, "{0}")'.format(name), module_object=name),
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py b/ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py
new file mode 100644
index 00000000..0ff42a28
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_config_rollback.py
@@ -0,0 +1,324 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_config_rollback
+short_description: Provides rollback and rollback preview functionality (config:ImportP)
+description:
+- Provides rollback and rollback preview functionality for Cisco ACI fabrics.
+- Config Rollbacks are done using snapshots C(aci_snapshot) with the configImportP class.
+options:
+ compare_export_policy:
+ description:
+ - The export policy that the C(compare_snapshot) is associated to.
+ type: str
+ compare_snapshot:
+ description:
+ - The name of the snapshot to compare with C(snapshot).
+ type: str
+ description:
+ description:
+ - The description for the Import Policy.
+ type: str
+ aliases: [ descr ]
+ export_policy:
+ description:
+ - The export policy that the C(snapshot) is associated to.
+ type: str
+ fail_on_decrypt:
+ description:
+ - Determines if the APIC should fail the rollback if unable to decrypt secured data.
+ - The APIC defaults to C(yes) when unset.
+ type: bool
+ import_mode:
+ description:
+ - Determines how the import should be handled by the APIC.
+ - The APIC defaults to C(atomic) when unset.
+ type: str
+ choices: [ atomic, best-effort ]
+ import_policy:
+ description:
+ - The name of the Import Policy to use for config rollback.
+ type: str
+ import_type:
+ description:
+ - Determines how the current and snapshot configuration should be compared for replacement.
+ - The APIC defaults to C(replace) when unset.
+ type: str
+ choices: [ merge, replace ]
+ snapshot:
+ description:
+ - The name of the snapshot to rollback to, or the base snapshot to use for comparison.
+ - The C(aci_snapshot) module can be used to query the list of available snapshots.
+ type: str
+ required: yes
+ state:
+ description:
+ - Use C(preview) for previewing the diff between two snapshots.
+ - Use C(rollback) for reverting the configuration to a previous snapshot.
+ type: str
+ choices: [ preview, rollback ]
+ default: rollback
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_config_snapshot
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(config:ImportP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+---
+- name: Create a Snapshot
+ cisco.aci.aci_config_snapshot:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ export_policy: config_backup
+ state: present
+ delegate_to: localhost
+
+- name: Query Existing Snapshots
+ cisco.aci.aci_config_snapshot:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ export_policy: config_backup
+ state: query
+ delegate_to: localhost
+
+- name: Compare Snapshot Files
+ cisco.aci.aci_config_rollback:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ export_policy: config_backup
+ snapshot: run-2017-08-28T06-24-01
+ compare_export_policy: config_backup
+ compare_snapshot: run-2017-08-27T23-43-56
+ state: preview
+ delegate_to: localhost
+
+- name: Rollback Configuration
+ cisco.aci.aci_config_rollback:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ import_policy: rollback_config
+ export_policy: config_backup
+ snapshot: run-2017-08-28T06-24-01
+ state: rollback
+ delegate_to: localhost
+
+- name: Rollback Configuration
+ cisco.aci.aci_config_rollback:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ import_policy: rollback_config
+ export_policy: config_backup
+ snapshot: run-2017-08-28T06-24-01
+ description: Rollback 8-27 changes
+ import_mode: atomic
+ import_type: replace
+ fail_on_decrypt: yes
+ state: rollback
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+preview:
+ description: A preview between two snapshots
+ returned: when state is preview
+ type: str
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_bytes
+from ansible.module_utils.urls import fetch_url
+
+# Optional, only used for rollback preview
+try:
+ import lxml.etree
+ from xmljson import cobra
+
+ XML_TO_JSON = True
+except ImportError:
+ XML_TO_JSON = False
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ compare_export_policy=dict(type="str"),
+ compare_snapshot=dict(type="str"),
+ description=dict(type="str", aliases=["descr"]),
+ export_policy=dict(type="str"),
+ fail_on_decrypt=dict(type="bool"),
+ import_mode=dict(type="str", choices=["atomic", "best-effort"]),
+ import_policy=dict(type="str"),
+ import_type=dict(type="str", choices=["merge", "replace"]),
+ snapshot=dict(type="str", required=True),
+ state=dict(type="str", default="rollback", choices=["preview", "rollback"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=False,
+ required_if=[
+ ["state", "preview", ["compare_export_policy", "compare_snapshot"]],
+ ["state", "rollback", ["import_policy"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ description = module.params.get("description")
+ export_policy = module.params.get("export_policy")
+ fail_on_decrypt = aci.boolean(module.params.get("fail_on_decrypt"))
+ import_mode = module.params.get("import_mode")
+ import_policy = module.params.get("import_policy")
+ import_type = module.params.get("import_type")
+ snapshot = module.params.get("snapshot")
+ state = module.params.get("state")
+
+ if state == "rollback":
+ if snapshot.startswith("run-"):
+ snapshot = snapshot.replace("run-", "", 1)
+
+ if not snapshot.endswith(".tar.gz"):
+ snapshot += ".tar.gz"
+
+ filename = "ce2_{0}-{1}".format(export_policy, snapshot)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="configImportP",
+ aci_rn="fabric/configimp-{0}".format(import_policy),
+ module_object=import_policy,
+ target_filter={"name": import_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ aci.payload(
+ aci_class="configImportP",
+ class_config=dict(
+ adminSt="triggered",
+ descr=description,
+ failOnDecryptErrors=fail_on_decrypt,
+ fileName=filename,
+ importMode=import_mode,
+ importType=import_type,
+ name=import_policy,
+ snapshot="yes",
+ ),
+ )
+
+ aci.get_diff(aci_class="configImportP")
+
+ aci.post_config()
+
+ elif state == "preview":
+ aci.url = "%(protocol)s://%(host)s/mqapi2/snapshots.diff.xml" % module.params
+ aci.filter_string = (
+ "?s1dn=uni/backupst/snapshots-[uni/fabric/configexp-%(export_policy)s]/snapshot-%(snapshot)s&"
+ "s2dn=uni/backupst/snapshots-[uni/fabric/configexp-%(compare_export_policy)s]/snapshot-%(compare_snapshot)s"
+ ) % module.params
+
+ # Generate rollback comparison
+ get_preview(aci)
+
+ aci.exit_json()
+
+
+def get_preview(aci):
+ """
+ This function is used to generate a preview between two snapshots and add the parsed results to the aci module return data.
+ """
+ uri = aci.url + aci.filter_string
+ resp, info = fetch_url(
+ aci.module, uri, headers=aci.headers, method="GET", timeout=aci.module.params.get("timeout"), use_proxy=aci.module.params.get("use_proxy")
+ )
+ aci.method = "GET"
+ aci.response = info.get("msg")
+ aci.status = info.get("status")
+
+ # Handle APIC response
+ if info.get("status") == 200:
+ xml_to_json(aci, resp.read())
+ else:
+ aci.result["raw"] = resp.read()
+ aci.fail_json(msg="Request failed: %(code)s %(text)s (see 'raw' output)" % aci.error)
+
+
+def xml_to_json(aci, response_data):
+ """
+ This function is used to convert preview XML data into JSON.
+ """
+ if XML_TO_JSON:
+ xml = lxml.etree.fromstring(to_bytes(response_data))
+ xmldata = cobra.data(xml)
+ aci.result["preview"] = xmldata
+ else:
+ aci.result["preview"] = response_data
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py b/ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py
new file mode 100644
index 00000000..8bb13307
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_config_snapshot.py
@@ -0,0 +1,348 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_config_snapshot
+short_description: Manage Config Snapshots (config:Snapshot, config:ExportP)
+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.
+options:
+ description:
+ description:
+ - The description for the Config Export Policy.
+ type: str
+ aliases: [ descr ]
+ export_policy:
+ description:
+ - The name of the Export Policy to use for Config Snapshots.
+ type: str
+ aliases: [ name ]
+ format:
+ description:
+ - Sets the config backup to be formatted in JSON or XML.
+ - The APIC defaults to C(json) when unset.
+ type: str
+ choices: [ json, xml ]
+ include_secure:
+ description:
+ - Determines if secure information should be included in the backup.
+ - The APIC defaults to C(yes) when unset.
+ type: bool
+ max_count:
+ description:
+ - Determines how many snapshots can exist for the Export Policy before the APIC starts to rollover.
+ - Accepted values range between C(1) and C(10).
+ - The APIC defaults to C(3) when unset.
+ type: int
+ snapshot:
+ description:
+ - The name of the snapshot to delete.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+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>.'
+seealso:
+- module: cisco.aci.aci_config_rollback
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(config:Snapshot) and B(config:ExportP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Create a Snapshot
+ cisco.aci.aci_config_snapshot:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: present
+ export_policy: config_backup
+ max_count: 10
+ description: Backups taken before new configs are applied.
+ delegate_to: localhost
+
+- name: Query all Snapshots
+ cisco.aci.aci_config_snapshot:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query Snapshots associated with a particular Export Policy
+ cisco.aci.aci_config_snapshot:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ export_policy: config_backup
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a Snapshot
+ cisco.aci.aci_config_snapshot:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ export_policy: config_backup
+ snapshot: run-2017-08-24T17-20-05
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ description=dict(type="str", aliases=["descr"]),
+ 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"),
+ snapshot=dict(type="str"),
+ state=dict(type="str", choices=["absent", "present", "query"], default="present"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=False,
+ required_if=[
+ ["state", "absent", ["export_policy", "snapshot"]],
+ ["state", "present", ["export_policy"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ description = module.params.get("description")
+ export_policy = module.params.get("export_policy")
+ file_format = module.params.get("format")
+ include_secure = aci.boolean(module.params.get("include_secure"))
+ max_count = module.params.get("max_count")
+ if max_count is not None:
+ if max_count in range(1, 11):
+ max_count = str(max_count)
+ else:
+ module.fail_json(msg="Parameter 'max_count' must be a number between 1 and 10")
+ snapshot = module.params.get("snapshot")
+ if snapshot is not None and not snapshot.startswith("run-"):
+ snapshot = "run-" + snapshot
+ state = module.params.get("state")
+
+ if state == "present":
+ aci.construct_url(
+ root_class=dict(
+ aci_class="configExportP",
+ aci_rn="fabric/configexp-{0}".format(export_policy),
+ module_object=export_policy,
+ target_filter={"name": export_policy},
+ ),
+ )
+ # Variable set for reuse of correct url in get_existing() triggered from exit_json() after query request
+ reset_url = aci.url
+ aci.get_existing()
+
+ aci.payload(
+ aci_class="configExportP",
+ class_config=dict(
+ adminSt="triggered",
+ descr=description,
+ format=file_format,
+ includeSecureFields=include_secure,
+ maxSnapshotCount=max_count,
+ name=export_policy,
+ snapshot="yes",
+ ),
+ )
+
+ aci.get_diff("configExportP")
+
+ # Create a new Snapshot
+ aci.post_config()
+
+ # Query for job information and add to results
+ # Change state to query else aci.request() will not execute a GET request but POST
+ aci.params["state"] = "query"
+ aci.request(path="/api/node/mo/uni/backupst/jobs-[uni/fabric/configexp-{0}].json".format(export_policy))
+ aci.result["job_details"] = aci.imdata[0].get("configJobCont", {})
+ # Reset state and url to display correct in output and trigger get_existing() function with correct url
+ aci.url = reset_url
+ aci.params["state"] = "present"
+
+ else:
+ # Prefix the proper url to export_policy
+ if export_policy is not None:
+ export_policy = "uni/fabric/configexp-{0}".format(export_policy)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="configSnapshotCont",
+ aci_rn="backupst/snapshots-[{0}]".format(export_policy),
+ module_object=export_policy,
+ target_filter={"name": export_policy},
+ ),
+ subclass_1=dict(
+ aci_class="configSnapshot",
+ aci_rn="snapshot-{0}".format(snapshot),
+ module_object=snapshot,
+ target_filter={"name": snapshot},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "absent":
+ # Build POST request to used to remove Snapshot
+ aci.payload(
+ aci_class="configSnapshot",
+ class_config=dict(
+ name=snapshot,
+ retire="yes",
+ ),
+ )
+
+ if aci.existing:
+ aci.get_diff("configSnapshot")
+
+ # Mark Snapshot for Deletion
+ aci.post_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_contract.py b/ansible_collections/cisco/aci/plugins/modules/aci_contract.py
new file mode 100644
index 00000000..55803556
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_contract.py
@@ -0,0 +1,340 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_contract
+short_description: Manage contract resources (vz:BrCP)
+description:
+- Manage Contract resources on Cisco ACI fabrics.
+options:
+ contract:
+ description:
+ - The name of the contract.
+ type: str
+ aliases: [ contract_name, name ]
+ description:
+ description:
+ - Description for the contract.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ scope:
+ description:
+ - The scope of a service contract.
+ - The APIC defaults to C(context) when unset during creation.
+ type: str
+ choices: [ application-profile, context, global, tenant ]
+ priority:
+ description:
+ - The desired QoS class to be used.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ level1, level2, level3, unspecified ]
+ dscp:
+ description:
+ - The target Differentiated Service (DSCP) value.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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 ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- This module does not manage Contract Subjects, see M(cisco.aci.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(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_contract_subject
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:BrCP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a new contract
+ cisco.aci.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
+ delegate_to: localhost
+
+- name: Remove an existing contract
+ cisco.aci.aci_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific contract
+ cisco.aci.aci_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all contracts
+ cisco.aci.aci_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ contract=dict(type="str", aliases=["contract_name", "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"]),
+ scope=dict(type="str", choices=["application-profile", "context", "global", "tenant"]),
+ priority=dict(type="str", choices=["level1", "level2", "level3", "unspecified"]), # No default provided on purpose
+ dscp=dict(
+ type="str",
+ 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"],
+ ), # No default provided on purpose
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["contract", "tenant"]],
+ ["state", "present", ["contract", "tenant"]],
+ ],
+ )
+
+ contract = module.params.get("contract")
+ description = module.params.get("description")
+ scope = module.params.get("scope")
+ priority = module.params.get("priority")
+ dscp = module.params.get("dscp")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzBrCP",
+ aci_rn="brc-{0}".format(contract),
+ module_object=contract,
+ target_filter={"name": contract},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vzBrCP",
+ class_config=dict(
+ name=contract,
+ descr=description,
+ scope=scope,
+ prio=priority,
+ targetDscp=dscp,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="vzBrCP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_contract_export.py b/ansible_collections/cisco/aci/plugins/modules/aci_contract_export.py
new file mode 100644
index 00000000..0f2e9905
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_contract_export.py
@@ -0,0 +1,287 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_contract_export
+short_description: Manage contract interfaces (vz:CPIf)
+description:
+- Manage Contract interfaces on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the contract interface.
+ type: str
+ aliases: [ interface_name ]
+ destination_tenant:
+ description:
+ - The The tenant associated with the contract interface.
+ type: str
+ description:
+ description:
+ - Description for the contract interface.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant hosting the contract to export.
+ type: str
+ aliases: [ tenant_name ]
+ contract:
+ description:
+ - The name of the contract to export.
+ type: str
+ aliases: [ contract_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_contract
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:BrCP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Create a new contract interface
+ cisco.aci.aci_contract_export:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: contractintf
+ destination_tenant: tndest
+ tenant: tnsrc
+ contract: web_to_db
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing contract interface
+ cisco.aci.aci_contract_export:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: contractintf
+ destination_tenant: tndest
+ tenant: tnsrc
+ contract: web_to_db
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific contract interface
+ cisco.aci.aci_contract_export:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: contractintf
+ destination_tenant: tndest
+ tenant: tnsrc
+ contract: web_to_db
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all contract interfaces
+ cisco.aci.aci_contract_export:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["interface_name"]),
+ destination_tenant=dict(type="str"),
+ description=dict(type="str", aliases=["descr"]),
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ contract=dict(type="str", aliases=["contract_name"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name", "destination_tenant", "tenant", "contract"]],
+ ["state", "present", ["name", "destination_tenant", "tenant", "contract"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ destination_tenant = module.params.get("destination_tenant")
+ description = module.params.get("description")
+ tenant = module.params.get("tenant")
+ contract = module.params.get("contract")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(destination_tenant),
+ module_object=destination_tenant,
+ target_filter={"name": destination_tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzCPIf",
+ aci_rn="cif-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["vzRsIf"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = [dict(vzRsIf=dict(attributes=dict(tDn="uni/tn-{0}/brc-{1}".format(tenant, contract))))]
+
+ aci.payload(aci_class="vzCPIf", class_config=dict(name=name, descr=description), child_configs=child_configs)
+
+ aci.get_diff(aci_class="vzCPIf")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py b/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py
new file mode 100644
index 00000000..8b41bf91
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject.py
@@ -0,0 +1,433 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_contract_subject
+short_description: Manage initial Contract Subjects (vz:Subj)
+description:
+- Manage initial Contract Subjects on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ subject:
+ description:
+ - The contract subject name.
+ type: str
+ aliases: [ contract_subject, name, subject_name ]
+ apply_both_direction:
+ description:
+ - The direction of traffic matching for the filter.
+ type: str
+ default: both
+ choices: [ both, one-way ]
+ contract:
+ description:
+ - The name of the Contract.
+ type: str
+ aliases: [ contract_name ]
+ reverse_filter:
+ description:
+ - Determines if the APIC should reverse the src and dst ports to allow the
+ return traffic back, since ACI is stateless filter.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ priority:
+ description:
+ - The QoS class.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ level1, level2, level3, unspecified ]
+ dscp:
+ description:
+ - The target DSCP.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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 ]
+ priority_consumer_to_provider:
+ description:
+ - The QoS class of Filter Chain For Consumer to Provider.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ level1, level2, level3, unspecified ]
+ dscp_consumer_to_provider:
+ description:
+ - The target DSCP of Filter Chain For Consumer to Provider.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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_consumer_to_provider ]
+ priority_provider_to_consumer:
+ description:
+ - The QoS class of Filter Chain For Provider to Consumer.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ level1, level2, level3, unspecified ]
+ dscp_provider_to_consumer:
+ description:
+ - The target DSCP of Filter Chain For Provider to Consumer.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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_provider_to_consumer ]
+ description:
+ description:
+ - Description for the contract subject.
+ type: str
+ aliases: [ descr ]
+ consumer_match:
+ description:
+ - The match criteria across consumers.
+ - The APIC defaults to C(at_least_one) when unset during creation.
+ type: str
+ choices: [ all, at_least_one, at_most_one, none ]
+ provider_match:
+ description:
+ - The match criteria across providers.
+ - The APIC defaults to C(at_least_one) when unset during creation.
+ type: str
+ choices: [ all, at_least_one, at_most_one, none ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(contract) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_contract) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_contract
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:Subj).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Swetha Chunduri (@schunduri)
+"""
+
+EXAMPLES = r"""
+- name: Add a new contract subject
+ cisco.aci.aci_contract_subject:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: default
+ description: test
+ reverse_filter: yes
+ priority: level1
+ dscp: unspecified
+ state: present
+ register: query_result
+
+- name: Remove a contract subject
+ cisco.aci.aci_contract_subject:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: default
+ state: absent
+ delegate_to: localhost
+
+- name: Query a contract subject
+ cisco.aci.aci_contract_subject:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: default
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all contract subjects
+ cisco.aci.aci_contract_subject:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, \
+ aci_contract_dscp_spec, aci_contract_qos_spec
+
+MATCH_MAPPING = dict(
+ all="All",
+ at_least_one="AtleastOne",
+ at_most_one="AtmostOne",
+ none="None",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ 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
+ reverse_filter=dict(type="bool"),
+ description=dict(type="str", aliases=["descr"]),
+ consumer_match=dict(type="str", choices=["all", "at_least_one", "at_most_one", "none"]),
+ provider_match=dict(type="str", choices=["all", "at_least_one", "at_most_one", "none"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ # default both because of back-worth compatibility and for determining which config to push
+ apply_both_direction=dict(type="str", default="both", choices=["both", "one-way"]),
+ priority=aci_contract_qos_spec(),
+ dscp=aci_contract_dscp_spec(),
+ priority_consumer_to_provider=aci_contract_qos_spec(),
+ dscp_consumer_to_provider=aci_contract_dscp_spec("consumer_to_provider"),
+ priority_provider_to_consumer=aci_contract_qos_spec(),
+ dscp_provider_to_consumer=aci_contract_dscp_spec("provider_to_consumer"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["contract", "subject", "tenant"]],
+ ["state", "present", ["contract", "subject", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ subject = module.params.get("subject")
+ priority = module.params.get("priority")
+ dscp = module.params.get("dscp")
+ priority_consumer_to_provider = module.params.get("priority_consumer_to_provider")
+ dscp_consumer_to_provider = module.params.get("dscp_consumer_to_provider")
+ priority_provider_to_consumer = module.params.get("priority_provider_to_consumer")
+ dscp_provider_to_consumer = module.params.get("dscp_provider_to_consumer")
+ reverse_filter = aci.boolean(module.params.get("reverse_filter"))
+ contract = module.params.get("contract")
+ description = module.params.get("description")
+ consumer_match = module.params.get("consumer_match")
+ if consumer_match is not None:
+ consumer_match = MATCH_MAPPING.get(consumer_match)
+ provider_match = module.params.get("provider_match")
+ if provider_match is not None:
+ provider_match = MATCH_MAPPING.get(provider_match)
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+ direction = module.params.get("apply_both_direction")
+
+ subject_class = "vzSubj"
+ base_subject_dict = dict(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzBrCP",
+ aci_rn="brc-{0}".format(contract),
+ module_object=contract,
+ target_filter={"name": contract},
+ ),
+ subclass_2=dict(
+ aci_class=subject_class,
+ aci_rn="subj-{0}".format(subject),
+ module_object=subject,
+ target_filter={"name": subject},
+ )
+ )
+
+ # start logic to be consistent with GUI to only allow both direction or one-way
+ aci.construct_url(root_class=base_subject_dict.get("root_class"),
+ subclass_1=base_subject_dict.get("subclass_1"),
+ subclass_2=base_subject_dict.get("subclass_2"),
+ child_classes=["vzInTerm", "vzOutTerm"])
+ aci.get_existing()
+ direction_options = ["both", "one-way"]
+ if state != "query":
+ if aci.existing and subject_class in aci.existing[0]:
+ direction_options = ["one-way"] if "children" in aci.existing[0][subject_class] else ["both"]
+ if direction not in direction_options:
+ module.fail_json(msg="Direction is not allowed, valid option is {0}.".format(" or ".join(direction_options)))
+ # end logic to be consistent with GUI to only allow both direction or one-way
+
+ if state == "present":
+
+ config = dict(
+ name=subject,
+ prio=priority,
+ revFltPorts=reverse_filter,
+ targetDscp=dscp,
+ consMatchT=consumer_match,
+ provMatchT=provider_match,
+ descr=description,
+ nameAlias=name_alias,
+ )
+
+ child_configs = []
+ if direction == "one-way" and (
+ len(direction_options) == 2 or
+ dscp_consumer_to_provider is not None or
+ priority_consumer_to_provider is not None or
+ dscp_provider_to_consumer is not None or
+ priority_provider_to_consumer is not None):
+ subj_dn = "uni/tn-{0}/brc-{1}/subj-{2}".format(tenant, contract, subject)
+ child_configs = [
+ dict(vzInTerm=dict(attributes=dict(dn="{0}/intmnl".format(subj_dn),
+ targetDscp=dscp_consumer_to_provider,
+ prio=priority_consumer_to_provider))),
+ dict(vzOutTerm=dict(attributes=dict(dn="{0}/outtmnl".format(subj_dn),
+ targetDscp=dscp_provider_to_consumer,
+ prio=priority_provider_to_consumer))),
+ ]
+
+ aci.payload(aci_class=subject_class, class_config=config, child_configs=child_configs)
+
+ aci.get_diff(aci_class=subject_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py b/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py
new file mode 100644
index 00000000..0d47bf4b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_filter.py
@@ -0,0 +1,373 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_contract_subject_to_filter
+short_description: Bind Contract Subjects to Filters (vz:RsSubjFiltAtt)
+description:
+- Bind Contract Subjects to Filters on Cisco ACI fabrics.
+options:
+ contract:
+ description:
+ - The name of the contract.
+ type: str
+ aliases: [ contract_name ]
+ filter:
+ description:
+ - The name of the Filter to bind to the Subject.
+ type: str
+ aliases: [ filter_name ]
+ direction:
+ description:
+ - The direction of traffic matching for the filter.
+ type: str
+ default: both
+ choices: [ both, consumer_to_provider, provider_to_consumer ]
+ action:
+ description:
+ - The action required when the condition is met.
+ - The APIC defaults to C(permit) when unset during creation.
+ type: str
+ choices: [ deny, permit ]
+ priority_override:
+ description:
+ - Overrides the filter priority for the a single applied filter.
+ type: str
+ choices: [ default, level1, level2, level3 ]
+ directives:
+ description:
+ - Determines if the binding should be set to log.
+ - The APIC defaults to C(none) when unset during creation.
+ type: str
+ choices: [ log, no_stats, none ]
+ aliases: [ log, directive]
+ subject:
+ description:
+ - The name of the Contract Subject.
+ type: str
+ aliases: [ contract_subject, subject_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(contract), C(subject), and C(filter_name) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_contract), M(cisco.aci.aci_contract_subject), and M(cisco.aci.aci_filter) modules can be used for these.
+seealso:
+- module: cisco.aci.aci_contract_subject
+- module: cisco.aci.aci_filter
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:RsSubjFiltAtt).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add a new contract subject to filer binding
+ cisco.aci.aci_contract_subject_to_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ filter: '{{ filter }}'
+ log: '{{ log }}'
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing contract subject to filter binding
+ cisco.aci.aci_contract_subject_to_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ filter: '{{ filter }}'
+ log: '{{ log }}'
+ state: present
+ delegate_to: localhost
+
+- name: Query a specific contract subject to filter binding
+ cisco.aci.aci_contract_subject_to_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ filter: '{{ filter }}'
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all contract subject to filter bindings
+ cisco.aci.aci_contract_subject_to_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ 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
+ subject=dict(type="str", aliases=["contract_subject", "subject_name"]), # Not required for querying all objects
+ # default both because of back-worth compatibility and for determining which config to push
+ direction=dict(type="str", default="both", choices=["both", "consumer_to_provider", "provider_to_consumer"]),
+ action=dict(type="str", choices=["deny", "permit"]),
+ # named directives instead of log/directive for readability of code, aliases and input "none are kept for back-worth compatibility
+ directives=dict(type="str", choices=["log", "no_stats", "none"], aliases=["log", "directive"]),
+ priority_override=dict(type="str", choices=["default", "level1", "level2", "level3"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["contract", "filter", "subject", "tenant"]],
+ ["state", "present", ["contract", "filter", "subject", "tenant"]],
+ ],
+ )
+
+ contract = module.params.get("contract")
+ filter_name = module.params.get("filter")
+ # "none" is kept because of back-worth compatibility, could be deleted and keep only None
+ directives = "" if (module.params.get("directives") is None or module.params.get("directives") == "none") else module.params.get("directives")
+ subject = module.params.get("subject")
+ direction = module.params.get("direction")
+ action = module.params.get("action")
+ priority_override = module.params.get("priority_override")
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+
+ base_subject_dict = dict(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzBrCP",
+ aci_rn="brc-{0}".format(contract),
+ module_object=contract,
+ target_filter={"name": contract},
+ ),
+ subclass_2=dict(
+ aci_class="vzSubj",
+ aci_rn="subj-{0}".format(subject),
+ module_object=subject,
+ target_filter={"name": subject},
+ )
+ )
+
+ aci = ACIModule(module)
+
+ # start logic to be consistent with GUI to only allow both direction or a one-way connection
+ aci.construct_url(root_class=base_subject_dict.get("root_class"),
+ subclass_1=base_subject_dict.get("subclass_1"),
+ subclass_2=base_subject_dict.get("subclass_2"),
+ child_classes=["vzInTerm", "vzOutTerm"])
+ aci.get_existing()
+ direction_options = ["both"]
+ if aci.existing:
+ direction_options = ["consumer_to_provider", "provider_to_consumer"] if "children" in aci.existing[0]["vzSubj"] else ["both"]
+
+ if state != "query" and direction not in direction_options:
+ module.fail_json(msg="Direction is not allowed, valid option is {0}.".format(" or ".join(direction_options)))
+ # end logic to be consistent with GUI to only allow both direction or a one-way connection
+
+ if direction == "both":
+ filter_class = "vzRsSubjFiltAtt"
+ # dict unpacking with **base_subject_dict raises syntax error in python2.7 thus dict lookup
+ aci.construct_url(root_class=base_subject_dict.get("root_class"),
+ subclass_1=base_subject_dict.get("subclass_1"),
+ subclass_2=base_subject_dict.get("subclass_2"),
+ subclass_3=dict(
+ aci_class=filter_class,
+ aci_rn="rssubjFiltAtt-{0}".format(filter_name),
+ module_object=filter_name,
+ target_filter=dict(tnVzFilterName=filter_name)))
+ else:
+ term_class, term = ("vzInTerm", "intmnl") if direction == "consumer_to_provider" else ("vzOutTerm", "outtmnl")
+ filter_class = "vzRsFiltAtt"
+ # dict unpacking with **base_subject_dict raises syntax error in python2.7 thus dict lookup
+ aci.construct_url(root_class=base_subject_dict.get("root_class"),
+ subclass_1=base_subject_dict.get("subclass_1"),
+ subclass_2=base_subject_dict.get("subclass_2"),
+ subclass_3=dict(aci_class=term_class, aci_rn=term),
+ child_classes=[filter_class])
+
+ aci.get_existing()
+
+ if state == "present":
+ config = dict(tnVzFilterName=filter_name, directives=directives, action=action, priorityOverride=priority_override)
+ if direction == "both":
+ aci.payload(aci_class=filter_class, class_config=config)
+ aci.get_diff(aci_class=filter_class)
+ else:
+ child_config = [dict(vzRsFiltAtt=dict(attributes=config))]
+ aci.payload(aci_class=term_class, class_config=dict(), child_configs=child_config)
+ aci.get_diff(aci_class=term_class)
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ if direction == "both":
+ aci.exit_json()
+ else:
+ # filter the output of current/previous to tnVzFilterName only since existing consist full vzInTerm/vzOutTerm
+ def filter_result(input_list, name):
+ return [{key: filter_entry} for entry in input_list if 'children' in entry[term_class] for children in entry[term_class]['children']
+ for key, filter_entry in children.items() if filter_entry['attributes']['tnVzFilterName'] == name]
+ # pass function to
+ filter_existing = (filter_result, filter_name)
+ aci.exit_json(filter_existing)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_service_graph.py b/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_service_graph.py
new file mode 100644
index 00000000..eb8f9fa3
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_contract_subject_to_service_graph.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_contract_subject_to_service_graph
+short_description: Bind contract subject to service graph (vz:RsSubjGraphAtt).
+description:
+- Bind contract subject to service graph.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ contract:
+ description:
+ - The name of the contract.
+ type: str
+ subject:
+ description:
+ - The contract subject name.
+ type: str
+ service_graph:
+ description:
+ - The service graph name.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new contract subject to service graph binding
+ cisco.aci.aci_contract_subject_to_service_graph:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ service_graph: '{{ service_graph }}'
+ state: present
+ delegate_to: localhost
+- name: Remove an existing contract subject to service graph binding
+ cisco.aci.aci_contract_subject_to_service_graph:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ service_graph: '{{ service_graph }}'
+ state: absent
+ delegate_to: localhost
+- name: Query a specific contract subject to service graph binding
+ cisco.aci.aci_contract_subject_to_service_graph:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ contract: web_to_db
+ subject: test
+ service_graph: '{{ service_graph }}'
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str"),
+ contract=dict(type="str"),
+ subject=dict(type="str"),
+ service_graph=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["contract", "service_graph", "subject", "tenant"]],
+ ["state", "present", ["contract", "service_graph", "subject", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ contract = module.params.get("contract")
+ subject = module.params.get("subject")
+ service_graph = module.params.get("service_graph")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(aci_class="vzBrCP", aci_rn="brc-{0}".format(contract), module_object=contract, target_filter={"name": contract}),
+ subclass_2=dict(aci_class="vzSubj", aci_rn="subj-{0}".format(subject), module_object=subject, target_filter={"name": subject}),
+ subclass_3=dict(aci_class="vzRsSubjGraphAtt", aci_rn="rsSubjGraphAtt", module_object=service_graph, target_filter={"name": service_graph}),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="vzRsSubjGraphAtt", class_config=dict(tnVnsAbsGraphName=service_graph))
+
+ aci.get_diff(aci_class="vzRsSubjGraphAtt")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay.py b/ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay.py
new file mode 100644
index 00000000..f3227ff8
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay.py
@@ -0,0 +1,274 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_dhcp_relay
+short_description: Manage DHCP relay policies.
+description:
+- Manage DHCP relay policy (dhcpRelayP) configuration on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ name:
+ description:
+ - Name of the DHCP relay policy
+ type: str
+ aliases: [ relay_policy ]
+ description:
+ description:
+ - Description of the relay policy
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(dhcpRelayP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new DHCP relay policy
+ cisco.aci.aci_dhcp_relay:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ name: my_dhcp_relay
+ description: via Ansible
+ state: present
+ delegate_to: localhost
+
+- name: Remove a DHCP relay policy
+ cisco.aci.aci_dhcp_relay:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ name: my_dhcp_relay
+ state: absent
+ delegate_to: localhost
+
+- name: Query a DHCP relay policy
+ cisco.aci.aci_dhcp_relay:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ name: my_dhcp_relay
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all DHCP relay policies in a specific tenant
+ cisco.aci.aci_dhcp_relay:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type='str', aliases=['relay_policy']),
+ description=dict(type='str'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ tenant=dict(type='str'),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['name', 'tenant']],
+ ['state', 'present', ['name', 'tenant']],
+ ],
+ )
+
+ name = module.params.get('name')
+ description = module.params.get('description')
+ state = module.params.get('state')
+ tenant = module.params.get('tenant')
+ child_classes = ['dhcpRsProv']
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class='fvTenant',
+ aci_rn='tn-{0}'.format(tenant),
+ module_object=tenant,
+ target_filter={'name': tenant},
+ ),
+ subclass_1=dict(
+ aci_class='dhcpRelayP',
+ aci_rn='relayp-{0}'.format(name),
+ module_object=name,
+ target_filter={'name': name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='dhcpRelayP',
+ class_config=dict(
+ name=name,
+ descr=description,
+ owner='tenant'
+ ),
+ )
+
+ aci.get_diff(aci_class='dhcpRelayP')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay_provider.py b/ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay_provider.py
new file mode 100644
index 00000000..85a0f21d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_dhcp_relay_provider.py
@@ -0,0 +1,386 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_dhcp_relay_provider
+short_description: Manage DHCP relay policy providers.
+description:
+- Manage DHCP relay policy providers (dhcpRsProv) configuration on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of the tenant the relay_policy is in.
+ type: str
+ relay_policy:
+ description:
+ - Name of an existing DHCP relay policy
+ type: str
+ aliases: [ relay_policy_name ]
+ provider_tenant:
+ description:
+ - Name of the tenant the epg or external_epg is in
+ - Only required if the epg or external_epg is in a different tenant than the relay_policy
+ type: str
+ epg_type:
+ description:
+ - Type of EPG the DHCP server is in
+ type: str
+ choices: [ epg, l2_external, l3_external, dn ]
+ required: yes
+ anp:
+ description:
+ - Application Profile the EPG is in.
+ - Only used when epg_type is app_epg.
+ type: str
+ epg:
+ description:
+ - Name of the Application EPG the DHCP server is in.
+ - Only used when epg_type is epg
+ type: str
+ aliases: [ app_epg ]
+ l2out_name:
+ description:
+ - Name of the L2out the DHCP server is in.
+ - Only used when epg_type is l2_external
+ type: str
+ l3out_name:
+ description:
+ - Name of the L3out the DHCP server is in.
+ - Only used when epg_type is l3_external.
+ type: str
+ external_epg:
+ description:
+ - Name of the external network object the DHCP server is in.
+ - Only used when epg_type is l2_external or l3_external.
+ type: str
+ aliases: [ external_net ]
+ dn:
+ description:
+ - dn of the EPG the DHCP server is in
+ - Only used when epg_type is dn
+ type: str
+ dhcp_server_addr:
+ description:
+ - DHCP server address
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(relay_policy) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and C(cisco.aci.aci_dhcp_relay) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(dhcpRsProv).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new DHCP relay App EPG provider
+ cisco.aci.aci_dhcp_relay_provider:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ relay_policy: my_dhcp_relay
+ epg_type: epg
+ anp: my_anp
+ epg: my_app_epg
+ dhcp_server_addr: 10.20.30.40
+ state: present
+ delegate_to: localhost
+
+- name: Add a new DHCP relay L3out provider
+ cisco.aci.aci_dhcp_relay_provider:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ relay_policy: my_dhcp_relay
+ epg_type: l3_external
+ l3out_name: my_l3out
+ external_net: my_l3out_ext_net
+ dhcp_server_addr: 10.20.30.40
+ state: present
+ delegate_to: localhost
+
+- name: Remove a DHCP relay provider
+ cisco.aci.aci_dhcp_relay_provider:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ relay_policy: my_dhcp_relay
+ epg_type: epg
+ anp: my_anp
+ epg: my_app_epg
+ state: absent
+ delegate_to: localhost
+
+- name: Query a DHCP relay provider
+ cisco.aci.aci_dhcp_relay_provider:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Auto-Demo
+ relay_policy: my_dhcp_relay
+ epg_type: epg
+ anp: my_anp
+ epg: my_app_epg
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ relay_policy=dict(type='str', aliases=['relay_policy_name']),
+ epg_type=dict(type='str', required=True,
+ choices=['epg',
+ 'l2_external',
+ 'l3_external',
+ 'dn']),
+ anp=dict(type='str'),
+ epg=dict(type='str', aliases=['app_epg']),
+ l2out_name=dict(type='str'),
+ l3out_name=dict(type='str'),
+ external_epg=dict(type='str', aliases=['external_net']),
+ dhcp_server_addr=dict(type='str'),
+ state=dict(type='str', default='present',
+ choices=['absent', 'present', 'query']),
+ tenant=dict(type='str'),
+ provider_tenant=dict(type='str'),
+ dn=dict(type='str'),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['relay_policy', 'tenant']],
+ ['state', 'present', ['relay_policy', 'tenant']],
+ ['epg_type', 'epg', ['anp', 'epg']],
+ ['epg_type', 'l2_external', ['l2out_name', 'external_epg']],
+ ['epg_type', 'l3_external', ['l3out_name', 'external_epg']],
+ ['epg_type', 'dn', ['dn']],
+ ],
+ mutually_exclusive=[
+ ['anp', 'l2out_name'],
+ ['anp', 'l3out_name'],
+ ['anp', 'external_epg'],
+ ['anp', 'dn'],
+ ['epg', 'l2out_name'],
+ ['epg', 'l3out_name'],
+ ['epg', 'external_epg'],
+ ['epg', 'dn'],
+ ['l2out_name', 'l3out_name'],
+ ['l2out_name', 'dn'],
+ ['l3out_name', 'dn'],
+ ['external_epg', 'dn'],
+ ],
+ )
+
+ relay_policy = module.params.get('relay_policy')
+ state = module.params.get('state')
+ tenant = module.params.get('tenant')
+ epg_type = module.params.get('epg_type')
+ anp = module.params.get('anp')
+ epg = module.params.get('epg')
+ l2out_name = module.params.get('l2out_name')
+ l3out_name = module.params.get('l3out_name')
+ external_epg = module.params.get('external_epg')
+ dhcp_server_addr = module.params.get('dhcp_server_addr')
+ provider_tenant = module.params.get('provider_tenant')
+ dn = module.params.get('dn')
+
+ if provider_tenant is None:
+ provider_tenant = tenant
+
+ if epg_type == 'epg':
+ tdn = 'uni/tn-{0}/ap-{1}/epg-{2}'.format(provider_tenant, anp, epg)
+ elif epg_type == 'l2_external':
+ tdn = ('uni/tn-{0}/l2out-{1}/instP-{2}'
+ .format(provider_tenant, l2out_name, external_epg))
+ elif epg_type == 'l3_external':
+ tdn = ('uni/tn-{0}/out-{1}/instP-{2}'
+ .format(provider_tenant, l3out_name, external_epg))
+ elif epg_type == 'dn':
+ tdn = dn
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class='fvTenant',
+ aci_rn='tn-{0}'.format(tenant),
+ module_object=tenant,
+ target_filter={'name': tenant},
+ ),
+ subclass_1=dict(
+ aci_class='dhcpRelayP',
+ aci_rn='relayp-{0}'.format(relay_policy),
+ module_object=relay_policy,
+ target_filter={'name': relay_policy},
+ ),
+ subclass_2=dict(
+ aci_class='dhcpRsProv',
+ aci_rn='rsprov-[{0}]'.format(tdn),
+ module_object=tdn,
+ target_filter={'tDn': tdn},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='dhcpRsProv',
+ class_config=dict(
+ addr=dhcp_server_addr,
+ tDn=tdn
+ ),
+ )
+
+ aci.get_diff(aci_class='dhcpRsProv')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_dns_domain.py b/ansible_collections/cisco/aci/plugins/modules/aci_dns_domain.py
new file mode 100644
index 00000000..7a1ead65
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_dns_domain.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_dns_domain
+short_description: Manage DNS Provider (dnsDomain) objects.
+description:
+- Manage DNS Domain configuration on Cisco ACI fabrics.
+options:
+ dns_profile:
+ description:
+ - Name of the DNS profile.
+ type: str
+ aliases: [ profile_name ]
+ required: yes
+ domain:
+ description:
+ - DNS domain name
+ type: str
+ aliases: [ name, domain_name ]
+ default:
+ description:
+ - Whether this is the default domain
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(dns_profile) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_dns_profile) modules can be used for this.
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(dnsDomain).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new DNS domain
+ cisco.aci.aci_dns_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ domain: example.com
+ state: present
+ delegate_to: localhost
+
+- name: Remove a DNS domain
+ cisco.aci.aci_dns_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ domain: example.com
+ state: absent
+ delegate_to: localhost
+
+- name: Query a DNS domain
+ cisco.aci.aci_dns_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ domain: example.com
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all DNS domains within a DNS profile
+ cisco.aci.aci_dns_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ dns_profile=dict(type='str', aliases=['profile_name'], required=True),
+ domain=dict(type='str', aliases=['name', 'domain_name']),
+ default=dict(type='bool'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['domain']],
+ ['state', 'present', ['domain']],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ dns_profile = module.params.get('dns_profile')
+ domain = module.params.get('domain')
+ default = aci.boolean(module.params.get('default'))
+ state = module.params.get('state')
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class='dnsProfile',
+ aci_rn='fabric/dnsp-{0}'.format(dns_profile),
+ module_object=dns_profile,
+ target_filter={'name': dns_profile},
+ ),
+ subclass_1=dict(
+ aci_class='dnsDomain',
+ aci_rn='dom-{0}'.format(domain),
+ module_object=domain,
+ target_filter={'name': domain}
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='dnsDomain',
+ class_config=dict(
+ name=domain,
+ isDefault=default
+ ),
+ )
+
+ aci.get_diff(aci_class='dnsDomain')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_dns_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_dns_profile.py
new file mode 100644
index 00000000..19c4c380
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_dns_profile.py
@@ -0,0 +1,246 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_dns_profile
+short_description: Manage DNS Profile (dnsProfile) objects.
+description:
+- Manage DNS Profile configuration on Cisco ACI fabrics.
+options:
+ dns_profile:
+ description:
+ - Name of the DNS profile.
+ type: str
+ aliases: [ name, profile_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(dnsProfile).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new DNS profile
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ state: present
+ delegate_to: localhost
+
+- name: Remove a DNS profile
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ state: absent
+ delegate_to: localhost
+
+- name: Query a DNS profile
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all DNS profiles
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ dns_profile=dict(type='str', aliases=['name', 'profile_name']),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['dns_profile']],
+ ['state', 'present', ['dns_profile']],
+ ],
+ )
+
+ dns_profile = module.params.get('dns_profile')
+ state = module.params.get('state')
+ child_classes = ['dnsProv', 'dnsDomain']
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class='dnsProfile',
+ aci_rn='fabric/dnsp-{0}'.format(dns_profile),
+ module_object=dns_profile,
+ target_filter={'name': dns_profile},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='dnsProfile',
+ class_config=dict(
+ name=dns_profile
+ ),
+ )
+
+ aci.get_diff(aci_class='dnsProfile')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_dns_provider.py b/ansible_collections/cisco/aci/plugins/modules/aci_dns_provider.py
new file mode 100644
index 00000000..f8cf2627
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_dns_provider.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_dns_provider
+short_description: Manage DNS Provider (dnsProv) objects.
+description:
+- Manage DNS Provider configuration on Cisco ACI fabrics.
+options:
+ dns_profile:
+ description:
+ - Name of the DNS profile.
+ type: str
+ aliases: [ profile_name ]
+ required: yes
+ address:
+ description:
+ - address of the DNS server
+ type: str
+ aliases: [ addr, ip_address ]
+ preferred:
+ description:
+ - Whether this is the preferred DNS server
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(dns_profile) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_dns_profile) modules can be used for this.
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(dnsProv).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new DNS provider
+ cisco.aci.aci_dns_provider:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ address: 10.20.30.40
+ state: present
+ delegate_to: localhost
+
+- name: Remove a DNS provider
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ address: 10.20.30.40
+ state: absent
+ delegate_to: localhost
+
+- name: Query a DNS provider
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dns_profile: my_dns_prof
+ address: 10.20.30.40
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all DNS providers within a DNS profile
+ cisco.aci.aci_dns_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ dns_profile=dict(type='str', aliases=['profile_name'], required=True),
+ address=dict(type='str', aliases=['addr', 'ip_address']),
+ preferred=dict(type='bool'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['address']],
+ ['state', 'present', ['address']],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ dns_profile = module.params.get('dns_profile')
+ address = module.params.get('address')
+ preferred = aci.boolean(module.params.get('preferred'))
+ state = module.params.get('state')
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class='dnsProfile',
+ aci_rn='fabric/dnsp-{0}'.format(dns_profile),
+ module_object=dns_profile,
+ target_filter={'name': dns_profile},
+ ),
+ subclass_1=dict(
+ aci_class='dnsProv',
+ aci_rn='prov-{0}'.format(address),
+ module_object=address,
+ target_filter={'address': address}
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='dnsProv',
+ class_config=dict(
+ addr=address,
+ preferred=preferred
+ ),
+ )
+
+ aci.get_diff(aci_class='dnsProv')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_domain.py b/ansible_collections/cisco/aci/plugins/modules/aci_domain.py
new file mode 100644
index 00000000..61cfef4b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_domain.py
@@ -0,0 +1,460 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_domain
+short_description: Manage physical, virtual, bridged, routed or FC domain profiles (phys:DomP, vmm:DomP, l2ext:DomP, l3ext:DomP, fc:DomP)
+description:
+- Manage physical, virtual, bridged, routed or FC domain profiles on Cisco ACI fabrics.
+options:
+ domain:
+ description:
+ - Name of the physical, virtual, bridged routed or FC domain profile.
+ type: str
+ aliases: [ domain_name, domain_profile, name ]
+ domain_type:
+ description:
+ - The type of domain profile.
+ - 'C(fc): The FC domain profile is a policy pertaining to single FC Management domain'
+ - 'C(l2dom): The external bridged domain profile is a policy for managing L2 bridged infrastructure bridged outside the fabric.'
+ - 'C(l3dom): The external routed domain profile is a policy for managing L3 routed infrastructure outside the fabric.'
+ - 'C(phys): The physical domain profile stores the physical resources and encap resources that should be used for EPGs associated with this domain.'
+ - 'C(vmm): The VMM domain profile is a policy for grouping VM controllers with similar networking policy requirements.'
+ type: str
+ required: yes
+ choices: [ fc, l2dom, l3dom, phys, vmm ]
+ aliases: [ type ]
+ dscp:
+ description:
+ - The target Differentiated Service (DSCP) value.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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 ]
+ encap_mode:
+ description:
+ - The layer 2 encapsulation protocol to use with the virtual switch.
+ type: str
+ choices: [ unknown, vlan, vxlan ]
+ add_infra_pg:
+ description:
+ - Configure port groups for infra VLAN (e.g. Virtual APIC).
+ type: bool
+ aliases: [ infra_pg ]
+ tag_collection:
+ description:
+ - Enables Cisco APIC to collect VMs that have been assigned tags in VMware vCenter for microsegmentation.
+ type: bool
+ multicast_address:
+ description:
+ - The multicast IP address to use for the virtual switch.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+ vswitch:
+ description:
+ - The virtual switch to use for vmm domains.
+ - The APIC defaults to C(default) when unset during creation.
+ type: str
+ choices: [ avs, default, dvs, unknown ]
+ access_mode:
+ description:
+ - Access mode for vmm domains
+ - This parameter cannot be changed after a domain is created
+ type: str
+ choices: [ read-only, read-write ]
+ enable_vm_folder:
+ description:
+ - Enable VM folder data retrieval
+ type: bool
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_aep_to_domain
+- module: cisco.aci.aci_domain_to_encap_pool
+- module: cisco.aci.aci_domain_to_vlan_pool
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(phys:DomP),
+ B(vmm:DomP), B(l2ext:DomP), B(l3ext:DomP) and B(fc:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a new physical domain
+ cisco.aci.aci_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ state: present
+
+- name: Remove a physical domain
+ cisco.aci.aci_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ state: absent
+
+- name: Add a new VMM domain
+ cisco.aci.aci_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: hyperv_dom
+ domain_type: vmm
+ vm_provider: microsoft
+ state: present
+ delegate_to: localhost
+
+- name: Remove a VMM domain
+ cisco.aci.aci_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: hyperv_dom
+ domain_type: vmm
+ vm_provider: microsoft
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific physical domain
+ cisco.aci.aci_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all domains
+ cisco.aci.aci_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain_type: phys
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+VSWITCH_MAPPING = dict(
+ avs="n1kv",
+ default="default",
+ dvs="default",
+ unknown="unknown",
+)
+
+BOOL_TO_ACI_MAPPING = {True: "yes", False: "no", None: None}
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ domain_type=dict(type="str", required=True, choices=["fc", "l2dom", "l3dom", "phys", "vmm"], aliases=["type"]),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile", "name"]), # Not required for querying all objects
+ dscp=dict(
+ type="str",
+ 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"],
+ ),
+ encap_mode=dict(type="str", choices=["unknown", "vlan", "vxlan"]),
+ add_infra_pg=dict(type="bool", aliases=["infra_pg"]),
+ tag_collection=dict(type="bool"),
+ multicast_address=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ vm_provider=dict(type="str", choices=["cloudfoundry", "kubernetes", "microsoft", "openshift", "openstack", "redhat", "vmware"]),
+ vswitch=dict(type="str", choices=["avs", "default", "dvs", "unknown"]),
+ name_alias=dict(type="str"),
+ access_mode=dict(type="str", choices=["read-write", "read-only"]),
+ enable_vm_folder=dict(type="bool"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["domain_type", "vmm", ["vm_provider"]],
+ ["state", "absent", ["domain", "domain_type"]],
+ ["state", "present", ["domain", "domain_type"]],
+ ],
+ )
+
+ dscp = module.params.get("dscp")
+ domain = module.params.get("domain")
+ domain_type = module.params.get("domain_type")
+ encap_mode = module.params.get("encap_mode")
+ add_infra_pg = BOOL_TO_ACI_MAPPING[module.params.get("add_infra_pg")]
+ tag_collection = BOOL_TO_ACI_MAPPING[module.params.get("tag_collection")]
+ multicast_address = module.params.get("multicast_address")
+ vm_provider = module.params.get("vm_provider")
+ vswitch = module.params.get("vswitch")
+ if vswitch is not None:
+ vswitch = VSWITCH_MAPPING.get(vswitch)
+ access_mode = module.params.get("access_mode")
+ enable_vm_folder = BOOL_TO_ACI_MAPPING[module.params.get("enable_vm_folder")]
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ if domain_type != "vmm":
+ if vm_provider is not None:
+ 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 parameter 'encap_mode'".format(domain_type))
+ if multicast_address is not None:
+ 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 parameter 'vswitch'".format(domain_type))
+ if access_mode is not None:
+ module.fail_json(msg="Domain type '{0}' cannot have parameter 'access_mode'".format(domain_type))
+ if enable_vm_folder is not None:
+ module.fail_json(msg="Domain type '{0}' cannot have parameter 'enable_vm_folder'".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")
+
+ # 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 == "l2dom":
+ domain_class = "l2extDomP"
+ domain_mo = "uni/l2dom-{0}".format(domain)
+ domain_rn = "l2dom-{0}".format(domain)
+ elif domain_type == "l3dom":
+ 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.get(vm_provider), domain)
+ domain_rn = "vmmp-{0}/dom-{1}".format(VM_PROVIDER_MAPPING.get(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(
+ aci_class=domain_class,
+ aci_rn=domain_rn,
+ module_object=domain_mo,
+ target_filter={"name": domain},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=domain_class,
+ class_config=dict(
+ encapMode=encap_mode,
+ mcastAddr=multicast_address,
+ configInfraPg=add_infra_pg,
+ enableTag=tag_collection,
+ mode=vswitch,
+ name=domain,
+ targetDscp=dscp,
+ nameAlias=name_alias,
+ accessMode=access_mode,
+ enableVmFolder=enable_vm_folder,
+ ),
+ )
+
+ aci.get_diff(aci_class=domain_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py b/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py
new file mode 100644
index 00000000..78474795
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_encap_pool.py
@@ -0,0 +1,379 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_domain_to_encap_pool
+short_description: Bind Domain to Encap Pools (infra:RsVlanNs)
+description:
+- Bind Domain to Encap Pools on Cisco ACI fabrics.
+notes:
+- The C(domain) and C(encap_pool) parameters should exist before using this module.
+ The M(cisco.aci.aci_domain) and M(cisco.aci.aci_encap_pool) can be used for these.
+options:
+ domain:
+ description:
+ - Name of the domain being associated with the Encap Pool.
+ type: str
+ aliases: [ domain_name, domain_profile ]
+ domain_type:
+ description:
+ - Determines if the Domain is physical (phys) or virtual (vmm).
+ type: str
+ required: yes
+ choices: [ fc, l2dom, l3dom, phys, vmm ]
+ pool:
+ description:
+ - The name of the pool.
+ type: str
+ aliases: [ pool_name ]
+ pool_allocation_mode:
+ description:
+ - The method used for allocating encaps to resources.
+ - Only vlan and vsan support allocation modes.
+ type: str
+ choices: [ dynamic, static]
+ aliases: [ allocation_mode, mode ]
+ pool_type:
+ description:
+ - The encap type of C(pool).
+ type: str
+ required: yes
+ choices: [ vlan, vsan, vxlan ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_encap_pool
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:RsVlanNs).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add domain to VLAN pool binding
+ cisco.aci.aci_domain_to_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ pool: test_pool
+ pool_type: vlan
+ pool_allocation_mode: dynamic
+ state: present
+ delegate_to: localhost
+
+- name: Remove domain to VLAN pool binding
+ cisco.aci.aci_domain_to_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ pool: test_pool
+ pool_type: vlan
+ pool_allocation_mode: dynamic
+ state: absent
+ delegate_to: localhost
+
+- name: Query our domain to VLAN pool binding
+ cisco.aci.aci_domain_to_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ pool: test_pool
+ pool_type: vlan
+ pool_allocation_mode: dynamic
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all domain to VLAN pool bindings
+ cisco.aci.aci_domain_to_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain_type: phys
+ pool_type: vlan
+ pool_allocation_mode: dynamic
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+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-{0}",
+ child_class="infraRsVlanNs",
+ ),
+ vxlan=dict(
+ aci_mo="uni/infra/vxlanns-{0}",
+ child_class="vmmRsVxlanNs",
+ ),
+ vsan=dict(
+ aci_mo="uni/infra/vsanns-{0}",
+ child_class="fcRsVsanNs",
+ ),
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ domain_type=dict(type="str", required=True, choices=["fc", "l2dom", "l3dom", "phys", "vmm"]),
+ pool_type=dict(type="str", required=True, choices=["vlan", "vsan", "vxlan"]),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile"]), # Not required for querying all objects
+ pool=dict(type="str", aliases=["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"]),
+ 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.get("domain")
+ domain_type = module.params.get("domain_type")
+ pool = module.params.get("pool")
+ pool_allocation_mode = module.params.get("pool_allocation_mode")
+ pool_type = module.params.get("pool_type")
+ vm_provider = module.params.get("vm_provider")
+ state = module.params.get("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 == "l2dom":
+ domain_class = "l2extDomP"
+ domain_mo = "uni/l2dom-{0}".format(domain)
+ domain_rn = "l2dom-{0}".format(domain)
+ elif domain_type == "l3dom":
+ 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)
+
+ # Ensure that querying all objects works when only domain_type is provided
+ if domain is None:
+ domain_mo = None
+
+ pool_mo = POOL_MAPPING[pool_type]["aci_mo"].format(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,
+ module_object=domain_mo,
+ target_filter={"name": domain},
+ ),
+ 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()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py b/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py
new file mode 100644
index 00000000..3019d04b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_domain_to_vlan_pool.py
@@ -0,0 +1,367 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_domain_to_vlan_pool
+short_description: Bind Domain to VLAN Pools (infra:RsVlanNs)
+description:
+- Bind Domain to VLAN Pools on Cisco ACI fabrics.
+options:
+ domain:
+ description:
+ - Name of the domain being associated with the VLAN Pool.
+ type: str
+ aliases: [ domain_name, domain_profile ]
+ domain_type:
+ description:
+ - Determines if the Domain is physical (phys) or virtual (vmm).
+ type: str
+ required: yes
+ choices: [ fc, l2dom, l3dom, phys, vmm ]
+ pool:
+ description:
+ - The name of the pool.
+ type: str
+ aliases: [ pool_name, vlan_pool ]
+ pool_allocation_mode:
+ description:
+ - The method used for allocating VLANs to resources.
+ type: str
+ required: yes
+ choices: [ dynamic, static]
+ aliases: [ allocation_mode, mode ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(domain) and C(vlan_pool) parameters should exist before using this module.
+ The M(cisco.aci.aci_domain) and M(cisco.aci.aci_vlan_pool) can be used for these.
+seealso:
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vlan_pool
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:RsVlanNs).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Bind a VMM domain to VLAN pool
+ cisco.aci.aci_domain_to_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmw_dom
+ domain_type: vmm
+ pool: vmw_pool
+ pool_allocation_mode: dynamic
+ vm_provider: vmware
+ state: present
+ delegate_to: localhost
+
+- name: Remove a VMM domain to VLAN pool binding
+ cisco.aci.aci_domain_to_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmw_dom
+ domain_type: vmm
+ pool: vmw_pool
+ pool_allocation_mode: dynamic
+ vm_provider: vmware
+ state: absent
+ delegate_to: localhost
+
+- name: Bind a physical domain to VLAN pool
+ cisco.aci.aci_domain_to_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ pool: phys_pool
+ pool_allocation_mode: static
+ state: present
+ delegate_to: localhost
+
+- name: Bind a physical domain to VLAN pool
+ cisco.aci.aci_domain_to_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ pool: phys_pool
+ pool_allocation_mode: static
+ state: absent
+ delegate_to: localhost
+
+- name: Query an domain to VLAN pool binding
+ cisco.aci.aci_domain_to_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: phys_dom
+ domain_type: phys
+ pool: phys_pool
+ pool_allocation_mode: static
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all domain to VLAN pool bindings
+ cisco.aci.aci_domain_to_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain_type: phys
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ domain_type=dict(type="str", required=True, choices=["fc", "l2dom", "l3dom", "phys", "vmm"]),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile"]), # 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"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["domain_type", "vmm", ["vm_provider"]],
+ ["state", "absent", ["domain", "domain_type", "pool"]],
+ ["state", "present", ["domain", "domain_type", "pool"]],
+ ],
+ )
+
+ domain = module.params.get("domain")
+ domain_type = module.params.get("domain_type")
+ pool = module.params.get("pool")
+ pool_allocation_mode = module.params.get("pool_allocation_mode")
+ vm_provider = module.params.get("vm_provider")
+ state = module.params.get("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 is not None:
+ pool_name = "[{0}]-{1}".format(pool, pool_allocation_mode)
+
+ # 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 == "l2dom":
+ domain_class = "l2extDomP"
+ domain_mo = "uni/l2dom-{0}".format(domain)
+ domain_rn = "l2dom-{0}".format(domain)
+ elif domain_type == "l3dom":
+ 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)
+
+ # 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(
+ root_class=dict(
+ aci_class=domain_class,
+ aci_rn=domain_rn,
+ module_object=domain_mo,
+ target_filter={"name": domain},
+ ),
+ child_classes=["infraRsVlanNs"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=domain_class,
+ class_config=dict(name=domain),
+ child_configs=[
+ {"infraRsVlanNs": {"attributes": {"tDn": aci_mo}}},
+ ],
+ )
+
+ aci.get_diff(aci_class=domain_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py b/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py
new file mode 100644
index 00000000..8d7f462b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool.py
@@ -0,0 +1,319 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_encap_pool
+short_description: Manage encap pools (fvns:VlanInstP, fvns:VxlanInstP, fvns:VsanInstP)
+description:
+- Manage vlan, vxlan, and vsan pools on Cisco ACI fabrics.
+options:
+ description:
+ description:
+ - Description for the C(pool).
+ type: str
+ aliases: [ descr ]
+ pool:
+ description:
+ - The name of the pool.
+ type: str
+ aliases: [ name, pool_name ]
+ pool_allocation_mode:
+ description:
+ - The method used for allocating encaps to resources.
+ - Only vlan and vsan support allocation modes.
+ type: str
+ choices: [ dynamic, static ]
+ aliases: [ allocation_mode, mode ]
+ pool_type:
+ description:
+ - The encap type of C(pool).
+ type: str
+ required: yes
+ aliases: [ type ]
+ choices: [ vlan, vsan, vxlan ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_encap_pool_range
+- module: cisco.aci.aci_vlan_pool
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fvns:VlanInstP),
+ B(fvns:VxlanInstP) and B(fvns:VsanInstP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add a new vlan pool
+ cisco.aci.aci_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_type: vlan
+ description: Production VLANs
+ state: present
+ delegate_to: localhost
+
+- name: Remove a vlan pool
+ cisco.aci.aci_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_type: vlan
+ state: absent
+ delegate_to: localhost
+
+- name: Query a vlan pool
+ cisco.aci.aci_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_type: vlan
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all vlan pools
+ cisco.aci.aci_encap_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool_type: vlan
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+ACI_POOL_MAPPING = dict(
+ vlan=dict(
+ aci_class="fvnsVlanInstP",
+ aci_mo="infra/vlanns-",
+ ),
+ vxlan=dict(
+ aci_class="fvnsVxlanInstP",
+ aci_mo="infra/vxlanns-",
+ ),
+ vsan=dict(
+ aci_class="fvnsVsanInstP",
+ aci_mo="infra/vsanns-",
+ ),
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ pool_type=dict(type="str", required=True, aliases=["type"], choices=["vlan", "vsan", "vxlan"]),
+ description=dict(type="str", aliases=["descr"]),
+ 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"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["pool"]],
+ ["state", "present", ["pool"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ pool = module.params.get("pool")
+ pool_type = module.params.get("pool_type")
+ pool_allocation_mode = module.params.get("pool_allocation_mode")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci_class = ACI_POOL_MAPPING[pool_type]["aci_class"]
+ aci_mo = ACI_POOL_MAPPING[pool_type]["aci_mo"]
+ pool_name = pool
+
+ # ACI Pool URL requires the pool_allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static)
+ 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 parameter 'pool_allocation_mode' for 'pool_type' of 'vlan' and 'vsan' when parameter 'pool' is provided")
+
+ # Vxlan pools do not support pool allocation modes
+ if pool_type == "vxlan" and pool_allocation_mode is not None:
+ module.fail_json(msg="vxlan pools do not support setting the 'pool_allocation_mode'; please remove this parameter from the task")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_class,
+ aci_rn="{0}{1}".format(aci_mo, pool_name),
+ module_object=pool,
+ target_filter={"name": pool},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ # Filter out module parameters with null values
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(
+ allocMode=pool_allocation_mode,
+ descr=description,
+ name=pool,
+ nameAlias=name_alias,
+ ),
+ )
+
+ # 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":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py b/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py
new file mode 100644
index 00000000..f2d7501c
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_encap_pool_range.py
@@ -0,0 +1,450 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_encap_pool_range
+short_description: Manage encap ranges assigned to pools (fvns:EncapBlk, fvns:VsanEncapBlk)
+description:
+- Manage vlan, vxlan, and vsan ranges that are assigned to pools on Cisco ACI fabrics.
+options:
+ allocation_mode:
+ description:
+ - The method used for allocating encaps to resources.
+ - Only vlan and vsan support allocation modes.
+ type: str
+ choices: [ dynamic, inherit, static]
+ aliases: [ mode ]
+ description:
+ description:
+ - Description for the pool range.
+ type: str
+ aliases: [ descr ]
+ pool:
+ description:
+ - The name of the pool that the range should be assigned to.
+ type: str
+ aliases: [ pool_name ]
+ pool_allocation_mode:
+ description:
+ - The method used for allocating encaps to resources.
+ - Only vlan and vsan support allocation modes.
+ type: str
+ choices: [ dynamic, static]
+ aliases: [ pool_mode ]
+ pool_type:
+ description:
+ - The encap type of C(pool).
+ type: str
+ required: yes
+ aliases: [ type ]
+ choices: [ vlan, vxlan, vsan]
+ range_end:
+ description:
+ - The end of encap range.
+ type: int
+ aliases: [ end ]
+ range_name:
+ description:
+ - The name to give to the encap range.
+ type: str
+ aliases: [ name, range ]
+ range_start:
+ description:
+ - The start of the encap range.
+ type: int
+ aliases: [ start ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(pool) must exist in order to add or delete a range.
+seealso:
+- module: cisco.aci.aci_encap_pool
+- module: cisco.aci.aci_vlan_pool_encap_block
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fvns:EncapBlk) and B(fvns:VsanEncapBlk).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add a new VLAN pool range
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_type: vlan
+ pool_allocation_mode: static
+ range_name: anstest
+ range_start: 20
+ range_end: 40
+ allocation_mode: inherit
+ state: present
+ delegate_to: localhost
+
+- name: Remove a VLAN pool range
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_type: vlan
+ pool_allocation_mode: static
+ range_name: anstest
+ range_start: 20
+ range_end: 40
+ state: absent
+ delegate_to: localhost
+
+- name: Query a VLAN range
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_type: vlan
+ pool_allocation_mode: static
+ range_name: anstest
+ range_start: 20
+ range_end: 50
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a VLAN pool for ranges by range_name
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool_type: vlan
+ range_name: anstest
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a VLAN pool for ranges by range_start
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool_type: vlan
+ range_start: 20
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a VLAN pool for ranges by range_start and range_end
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool_type: vlan
+ range_start: 20
+ range_end: 40
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all VLAN pool ranges
+ cisco.aci.aci_encap_pool_range:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool_type: vlan
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+ACI_POOL_MAPPING = dict(
+ vlan=dict(
+ aci_class="fvnsVlanInstP",
+ aci_mo="infra/vlanns-",
+ ),
+ vxlan=dict(
+ aci_class="fvnsVxlanInstP",
+ aci_mo="infra/vxlanns-",
+ ),
+ vsan=dict(
+ aci_class="fvnsVsanInstP",
+ aci_mo="infra/vsanns-",
+ ),
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ pool_type=dict(type="str", required=True, aliases=["type"], choices=["vlan", "vxlan", "vsan"]),
+ allocation_mode=dict(type="str", aliases=["mode"], choices=["dynamic", "inherit", "static"]),
+ description=dict(type="str", aliases=["descr"]),
+ 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"]),
+ 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"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["pool", "range_end", "range_start"]],
+ ["state", "present", ["pool", "range_end", "range_start"]],
+ ],
+ )
+
+ allocation_mode = module.params.get("allocation_mode")
+ description = module.params.get("description")
+ pool = module.params.get("pool")
+ pool_allocation_mode = module.params.get("pool_allocation_mode")
+ pool_type = module.params.get("pool_type")
+ range_end = module.params.get("range_end")
+ range_name = module.params.get("range_name")
+ range_start = module.params.get("range_start")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ if range_end is not None:
+ encap_end = "{0}-{1}".format(pool_type, range_end)
+ else:
+ encap_end = None
+
+ if range_start is not None:
+ encap_start = "{0}-{1}".format(pool_type, range_start)
+ else:
+ encap_start = None
+
+ ACI_RANGE_MAPPING = dict(
+ vlan=dict(
+ aci_class="fvnsEncapBlk",
+ aci_mo="from-[{0}]-to-[{1}]".format(encap_start, encap_end),
+ ),
+ vxlan=dict(
+ aci_class="fvnsEncapBlk",
+ aci_mo="from-[{0}]-to-[{1}]".format(encap_start, encap_end),
+ ),
+ vsan=dict(
+ aci_class="fvnsVsanEncapBlk",
+ aci_mo="vsanfrom-[{0}]-to-[{1}]".format(encap_start, encap_end),
+ ),
+ )
+
+ # Collect proper class and mo information based on pool_type
+ aci_range_class = ACI_RANGE_MAPPING[pool_type]["aci_class"]
+ aci_range_mo = ACI_RANGE_MAPPING[pool_type]["aci_mo"]
+ aci_pool_class = ACI_POOL_MAPPING[pool_type]["aci_class"]
+ aci_pool_mo = ACI_POOL_MAPPING[pool_type]["aci_mo"]
+ pool_name = pool
+
+ # Validate range_end and range_start are valid for its respective encap type
+ for encap_id in range_end, range_start:
+ if encap_id is not None:
+ if pool_type == "vlan":
+ if not 1 <= encap_id <= 4094:
+ module.fail_json(msg='vlan pools must have "range_start" and "range_end" values between 1 and 4094')
+ elif pool_type == "vxlan":
+ if not 5000 <= encap_id <= 16777215:
+ module.fail_json(msg='vxlan pools must have "range_start" and "range_end" values between 5000 and 16777215')
+ elif pool_type == "vsan":
+ if not 1 <= encap_id <= 4093:
+ module.fail_json(msg='vsan pools must have "range_start" and "range_end" values between 1 and 4093')
+
+ if range_end is not None and range_start is not None:
+ # Validate range_start is less than range_end
+ if range_start > range_end:
+ module.fail_json(msg='The "range_start" must be less than or equal to the "range_end"')
+
+ elif range_end is None and range_start is None:
+ if range_name is None:
+ # Reset range managed object to None for aci util to properly handle query
+ aci_range_mo = None
+
+ # Vxlan does not support setting the allocation mode
+ if pool_type == "vxlan" and allocation_mode is not None:
+ module.fail_json(msg='vxlan pools do not support setting the "allocation_mode"; please omit this parameter for vxlan pools')
+
+ # ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static)
+ 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 the "pool" is provided')
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_pool_class,
+ aci_rn="{0}{1}".format(aci_pool_mo, pool_name),
+ module_object=pool,
+ target_filter={"name": pool},
+ ),
+ subclass_1=dict(
+ aci_class=aci_range_class,
+ aci_rn="{0}".format(aci_range_mo),
+ module_object=aci_range_mo,
+ target_filter={"from": encap_start, "to": encap_end, "name": range_name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=aci_range_class,
+ class_config={
+ "allocMode": allocation_mode,
+ "descr": description,
+ "from": encap_start,
+ "name": range_name,
+ "to": encap_end,
+ "nameAlias": name_alias,
+ },
+ )
+
+ aci.get_diff(aci_class=aci_range_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_epg.py
new file mode 100644
index 00000000..183a4f06
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_epg.py
@@ -0,0 +1,429 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_epg
+short_description: Manage End Point Groups (EPG) objects (fv:AEPg)
+description:
+- Manage End Point Groups (EPG) on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - Name of an existing application network profile, that will contain the EPGs.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ epg:
+ description:
+ - Name of the end point group.
+ type: str
+ aliases: [ epg_name, name ]
+ bd:
+ description:
+ - Name of the bridge domain being associated with the EPG.
+ type: str
+ aliases: [ bd_name, bridge_domain ]
+ priority:
+ description:
+ - The QoS class.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ level1, level2, level3, unspecified ]
+ intra_epg_isolation:
+ description:
+ - The Intra EPG Isolation.
+ - The APIC defaults to C(unenforced) when unset during creation.
+ type: str
+ choices: [ enforced, unenforced ]
+ description:
+ description:
+ - Description for the EPG.
+ type: str
+ aliases: [ descr ]
+ fwd_control:
+ description:
+ - The forwarding control used by the EPG.
+ - The APIC defaults to C(none) when unset during creation.
+ type: str
+ choices: [ none, proxy-arp ]
+ preferred_group:
+ description:
+ - Whether or not the EPG is part of the Preferred Group and can communicate without contracts.
+ - This is very convenient for migration scenarios, or when ACI is used for network automation but not for policy.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ monitoring_policy:
+ description:
+ - The name of the monitoring policy.
+ type: str
+ custom_qos_policy:
+ description:
+ - The name of the custom Quality of Service policy.
+ type: str
+ useg:
+ description:
+ - Use C(yes) to create uSeg EPG and C(no) is used to create Application EPG.
+ type: str
+ choices: [ 'yes', 'no' ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(app_profile) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_ap) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_ap
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:AEPg).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Swetha Chunduri (@schunduri)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new EPG
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: intranet
+ epg: web_epg
+ description: Web Intranet EPG
+ bd: prod_bd
+ monitoring_policy: default
+ preferred_group: yes
+ state: present
+ delegate_to: localhost
+
+- aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: ticketing
+ epg: "{{ item.epg }}"
+ description: Ticketing EPG
+ bd: "{{ item.bd }}"
+ priority: unspecified
+ intra_epg_isolation: unenforced
+ state: present
+ delegate_to: localhost
+ with_items:
+ - epg: web
+ bd: web_bd
+ - epg: database
+ bd: database_bd
+
+- name: Add a new uSeg EPG
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: intranet
+ epg: web_epg
+ description: Web Intranet EPG
+ bd: prod_bd
+ monitoring_policy: default
+ preferred_group: yes
+ useg: yes
+ state: present
+ delegate_to: localhost
+
+- name: Remove an EPG
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ tenant: production
+ app_profile: intranet
+ epg: web_epg
+ monitoring_policy: default
+ state: absent
+ delegate_to: localhost
+
+- name: Query an EPG
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: ticketing
+ epg: web_epg
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all EPGs
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all EPGs with a Specific Name
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ epg: web_epg
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all EPGs of an App Profile
+ cisco.aci.aci_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ ap: ticketing
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ epg=dict(type="str", aliases=["epg_name", "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"]), # 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"]),
+ fwd_control=dict(type="str", choices=["none", "proxy-arp"]),
+ preferred_group=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ monitoring_policy=dict(type="str"),
+ custom_qos_policy=dict(type="str"),
+ useg=dict(type="str", choices=['yes', 'no']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["ap", "epg", "tenant"]],
+ ["state", "present", ["ap", "epg", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ epg = module.params.get("epg")
+ bd = module.params.get("bd")
+ description = module.params.get("description")
+ priority = module.params.get("priority")
+ intra_epg_isolation = module.params.get("intra_epg_isolation")
+ fwd_control = module.params.get("fwd_control")
+ preferred_group = aci.boolean(module.params.get("preferred_group"), "include", "exclude")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ name_alias = module.params.get("name_alias")
+ monitoring_policy = module.params.get("monitoring_policy")
+ custom_qos_policy = module.params.get("custom_qos_policy")
+ useg = module.params.get("useg")
+
+ child_configs = [dict(fvRsBd=dict(attributes=dict(tnFvBDName=bd))), dict(fvRsAEPgMonPol=dict(attributes=dict(tnMonEPGPolName=monitoring_policy)))]
+
+ if custom_qos_policy is not None:
+ child_configs.append(dict(fvRsCustQosPol=dict(attributes=dict(tnQosCustomPolName=custom_qos_policy))))
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ child_classes=["fvRsBd", "fvRsAEPgMonPol", "fvRsCustQosPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvAEPg",
+ class_config=dict(
+ name=epg,
+ descr=description,
+ prio=priority,
+ pcEnfPref=intra_epg_isolation,
+ fwdCtrl=fwd_control,
+ prefGrMemb=preferred_group,
+ nameAlias=name_alias,
+ isAttrBasedEPg=useg,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="fvAEPg")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py
new file mode 100644
index 00000000..7f0c81cd
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_epg_monitoring_policy.py
@@ -0,0 +1,279 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_epg_monitoring_policy
+short_description: Manage monitoring policies (mon:EPGPol)
+description:
+- Manage monitoring policies on Cisco ACI fabrics.
+options:
+ monitoring_policy:
+ description:
+ - The name of the monitoring policy.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - Description for the monitoring policy.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(mon:EPGPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a monitoring policy
+ cisco.aci.aci_epg_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ monitoring_policy: web_servers_monitoring
+ tenant: prod
+ state: present
+ delegate_to: localhost
+
+- name: Delete a monitoring policy
+ cisco.aci.aci_epg_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ monitoring_policy: web_servers_monitoring
+ tenant: prod
+ state: absent
+ delegate_to: localhost
+
+- name: Query all monitoring policy
+ cisco.aci.aci_epg_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific monitoring policy
+ cisco.aci.aci_epg_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ monitoring_policy: web_servers_monitoring
+ tenant: prod
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ monitoring_policy=dict(type="str", aliases=["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"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["monitoring_policy", "tenant"]],
+ ["state", "present", ["monitoring_policy", "tenant"]],
+ ],
+ )
+
+ monitoring_policy = module.params.get("monitoring_policy")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="monEPGPol",
+ aci_rn="monepg-{0}".format(monitoring_policy),
+ module_object=monitoring_policy,
+ target_filter={"name": monitoring_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="monEPGPol",
+ class_config=dict(
+ name=monitoring_policy,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="monEPGPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py
new file mode 100644
index 00000000..3621c20c
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract.py
@@ -0,0 +1,391 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_epg_to_contract
+short_description: Bind EPGs to Contracts (fv:RsCons, 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(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg), and M(cisco.aci.aci_contract) modules can be used for this.
+options:
+ ap:
+ description:
+ - Name of an existing application network profile, that will contain the EPGs.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ contract:
+ description:
+ - The name of the contract.
+ type: str
+ aliases: [ contract_name ]
+ contract_type:
+ description:
+ - Determines if the EPG should Provide or Consume the Contract.
+ type: str
+ required: yes
+ choices: [ consumer, provider ]
+ epg:
+ description:
+ - The name of the end point group.
+ type: str
+ aliases: [ epg_name ]
+ priority:
+ description:
+ - QoS class.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ level1, level2, level3, level4, level5, level6, unspecified ]
+ provider_match:
+ description:
+ - The matching algorithm for Provided Contracts.
+ - The APIC defaults to C(at_least_one) when unset during creation.
+ type: str
+ choices: [ all, at_least_one, at_most_one, none ]
+ contract_label:
+ description:
+ - Contract label to match
+ type: str
+ subject_label:
+ description:
+ - Subject label to match
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_ap
+- module: cisco.aci.aci_epg
+- module: cisco.aci.aci_contract
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fv:RsCons) and B(fv:RsProv).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add a new contract to EPG binding
+ cisco.aci.aci_epg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ contract: anstest_http
+ contract_type: provider
+ contract_label: contractlabel
+ subject_label: subjlabel
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing contract to EPG binding
+ cisco.aci.aci_epg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ contract: anstest_http
+ contract_type: provider
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific contract to EPG binding
+ cisco.aci.aci_epg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ contract: anstest_http
+ contract_type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all provider contract to EPG bindings
+ cisco.aci.aci_epg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ contract_type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+ACI_CLASS_MAPPING = dict(
+ consumer={
+ "class": "fvRsCons",
+ "rn": "rscons-",
+ },
+ provider={
+ "class": "fvRsProv",
+ "rn": "rsprov-",
+ },
+)
+
+PROVIDER_MATCH_MAPPING = dict(
+ all="All",
+ at_least_one="AtleastOne",
+ at_most_one="AtmostOne",
+ none="None",
+)
+
+CONTRACT_LABEL_MAPPING = dict(
+ consumer="vzConsLbl",
+ provider="vzProvLbl",
+)
+
+SUBJ_LABEL_MAPPING = dict(
+ consumer="vzConsSubjLbl",
+ provider="vzProvSubjLbl",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ contract_type=dict(type="str", required=True, choices=["consumer", "provider"]),
+ 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
+ priority=dict(type="str", choices=["level1", "level2", "level3", "level4", "level5", "level6", "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"]), # Not required for querying all objects
+ contract_label=dict(type="str"),
+ subject_label=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["ap", "contract", "epg", "tenant"]],
+ ["state", "present", ["ap", "contract", "epg", "tenant"]],
+ ],
+ )
+
+ ap = module.params.get("ap")
+ contract = module.params.get("contract")
+ contract_type = module.params.get("contract_type")
+ epg = module.params.get("epg")
+ priority = module.params.get("priority")
+ provider_match = module.params.get("provider_match")
+ if provider_match is not None:
+ provider_match = PROVIDER_MATCH_MAPPING[provider_match]
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ contract_label = module.params.get("contract_label")
+ subject_label = module.params.get("subject_label")
+
+ aci_class = ACI_CLASS_MAPPING[contract_type]["class"]
+ aci_rn = ACI_CLASS_MAPPING[contract_type]["rn"]
+ contract_label_class = CONTRACT_LABEL_MAPPING[contract_type]
+ subject_label_class = SUBJ_LABEL_MAPPING[contract_type]
+
+ if contract_type == "consumer" and provider_match is not None:
+ module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts")
+
+ child_classes = [subject_label_class, contract_label_class]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ subclass_3=dict(
+ aci_class=aci_class,
+ aci_rn="{0}{1}".format(aci_rn, contract),
+ module_object=contract,
+ target_filter={"tnVzBrCPName": contract},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if contract_label:
+ child_configs.append(
+ {contract_label_class: {"attributes": {"name": contract_label}}}
+ )
+ if subject_label:
+ child_configs.append(
+ {subject_label_class: {"attributes": {"name": subject_label}}}
+ )
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(
+ matchT=provider_match,
+ prio=priority,
+ tnVzBrCPName=contract,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_interface.py b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_interface.py
new file mode 100644
index 00000000..bc0ed04f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_interface.py
@@ -0,0 +1,306 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+
+DOCUMENTATION = r"""
+---
+module: aci_epg_to_contract_interface
+short_description: Bind EPGs to Consumed Contracts Interface (fv:RsConsIf).
+
+description:
+- Bind EPGs to Consumed Contracts Interface on Cisco ACI fabrics.
+notes:
+- The C(tenant), C(app_profile), C(EPG), and C(Contract Interface) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg), and M(cisco.aci.aci_contract_export) modules can be used for this.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - Name of an existing application profile, that will contain the EPGs.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ epg:
+ description:
+ - The name of the end point group.
+ type: str
+ aliases: [ epg_name ]
+ contract_interface:
+ description:
+ - Name of the Contract interface, which is the contract with a "Global" scope which is exported to the tenant.
+ type: str
+ aliases: [ contract_interface_name ]
+ priority:
+ description:
+ - QoS class.
+ - The default value of QoS is C(unspecified) during the creation.
+ type: str
+ choices: [ level1, level2, level3, level4, level5, level6, unspecified ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_ap
+- module: cisco.aci.aci_epg
+- module: cisco.aci.aci_contract_export
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fv:RsCons) and B(fv:RsProv).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+EXAMPLES = r"""
+- name: Add a new consumed contract interface to EPG
+ cisco.aci.aci_epg_to_contract_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ contract_interface: anstest_http
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing consumed contract interface
+ cisco.aci.aci_epg_to_contract_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ contract_interface: anstest_http
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific consumed contract interface
+ cisco.aci.aci_epg_to_contract_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ contract_interface: anstest_http
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all consumed contract interfaces
+ cisco.aci.aci_epg_to_contract_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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
+ contract_interface=dict(type="str", aliases=["contract_interface_name"]), # Not required for querying all objects
+ priority=dict(type="str", choices=["level1", "level2", "level3", "level4", "level5", "level6", "unspecified"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap", "epg", "contract_interface"]],
+ ["state", "present", ["tenant", "ap", "epg", "contract_interface"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ epg = module.params.get("epg")
+ contract_interface = module.params.get("contract_interface")
+ priority = module.params.get("priority")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ subclass_3=dict(
+ aci_class="fvRsConsIf",
+ aci_rn="rsconsIf-{0}".format(contract_interface),
+ module_object=contract_interface,
+ target_filter={"name": contract_interface},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvRsConsIf",
+ class_config=dict(
+ prio=priority,
+ tnVzCPIfName=contract_interface,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvRsConsIf")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py
new file mode 100644
index 00000000..4ad76e06
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_contract_master.py
@@ -0,0 +1,297 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_epg_to_contract_master
+short_description: Manage End Point Group (EPG) contract master relationships (fv:RsSecInherited)
+description:
+- Manage End Point Groups (EPG) contract master relationships on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ required: yes
+ ap:
+ description:
+ - Name of an existing application network profile, that will contain the EPGs.
+ type: str
+ required: yes
+ aliases: [ app_profile, app_profile_name ]
+ epg:
+ description:
+ - Name of the end point group.
+ type: str
+ required: yes
+ aliases: [ epg_name, name ]
+ contract_master_ap:
+ description:
+ - Name of the application profile where the contract master EPG is.
+ type: str
+ contract_master_epg:
+ description:
+ - Name of the end point group which serves as contract master.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(app_profile) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_ap) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_epg
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:AEPg).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Cindy Zhao (@cizhao)
+"""
+
+EXAMPLES = r"""
+- name: Add contract master
+ cisco.aci.aci_epg_to_contract_master:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: apName
+ epg: epgName
+ contract_master_ap: ap
+ contract_master_epg: epg
+ state: present
+ delegate_to: localhost
+
+- name: Remove contract master
+ cisco.aci.aci_epg_to_contract_master:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: apName
+ epg: epgName
+ contract_master_ap: ap
+ contract_master_epg: epg
+ state: absent
+ delegate_to: localhost
+
+- name: Query contract master
+ cisco.aci.aci_epg_to_contract_master:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: apName
+ epg: epgName
+ contract_master_ap: ap
+ contract_master_epg: epg
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"], required=True),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"], required=True),
+ epg=dict(type="str", aliases=["epg_name", "name"], required=True),
+ contract_master_ap=dict(type="str"),
+ contract_master_epg=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["contract_master_ap", "contract_master_epg"]],
+ ["state", "present", ["contract_master_ap", "contract_master_epg"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ epg = module.params.get("epg")
+ contract_master_ap = module.params.get("contract_master_ap")
+ contract_master_epg = module.params.get("contract_master_epg")
+ state = module.params.get("state")
+
+ contract_master = "uni/tn-{0}/ap-{1}/epg-{2}".format(tenant, contract_master_ap, contract_master_epg)
+
+ child_configs = []
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ subclass_3=dict(
+ aci_class="fvRsSecInherited",
+ aci_rn="rssecInherited-[{0}]".format(contract_master),
+ module_object=contract_master,
+ target_filter={"tDn": contract_master},
+ ),
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="fvRsSecInherited", class_config=dict(tDn=contract_master), child_configs=child_configs)
+
+ aci.get_diff(aci_class="fvRsSecInherited")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py
new file mode 100644
index 00000000..3bff1fd3
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_epg_to_domain.py
@@ -0,0 +1,552 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Jacob McGill <jmcgill298>
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_epg_to_domain
+short_description: Bind EPGs to Domains (fv:RsDomAtt)
+description:
+- Bind EPGs to Physical and Virtual Domains on Cisco ACI fabrics.
+options:
+ allow_useg:
+ description:
+ - Allows micro-segmentation.
+ - The APIC defaults to C(encap) when unset during creation.
+ type: str
+ choices: [ encap, useg ]
+ ap:
+ description:
+ - Name of an existing application network profile, that will contain the EPGs.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ deploy_immediacy:
+ description:
+ - Determines when the policy is pushed to hardware Policy CAM.
+ - The APIC defaults to C(lazy) when unset during creation.
+ type: str
+ choices: [ immediate, lazy ]
+ domain:
+ description:
+ - Name of the physical or virtual domain being associated with the EPG.
+ type: str
+ aliases: [ domain_name, domain_profile ]
+ domain_type:
+ description:
+ - Specify whether the Domain is a physical (phys), a virtual (vmm) or an L2 external domain association (l2dom).
+ type: str
+ choices: [ l2dom, phys, vmm ]
+ aliases: [ type ]
+ encap:
+ description:
+ - The VLAN encapsulation for the EPG when binding a VMM Domain with static C(encap_mode).
+ - This acts as the secondary encap when using useg.
+ - Accepted values range between C(1) and C(4096).
+ type: int
+ encap_mode:
+ description:
+ - The encapsulation method to be used.
+ - The APIC defaults to C(auto) when unset during creation.
+ - If vxlan is selected, switching_mode must be "AVE".
+ type: str
+ choices: [ auto, vlan, vxlan ]
+ switching_mode:
+ description:
+ - Switching Mode used by the switch
+ type: str
+ choices: [ AVE, native ]
+ default: native
+ epg:
+ description:
+ - Name of the end point group.
+ type: str
+ aliases: [ epg_name, name ]
+ enhanced_lag_policy:
+ description:
+ - Name of the VMM Domain Enhanced Lag Policy.
+ type: str
+ aliases: [ lag_policy ]
+ vmm_uplink_active:
+ description:
+ - A list of active uplink IDs.
+ - The order decides the order in which active uplinks take over for a failed uplink.
+ - At least one active uplink must remain specified in the list when an active uplink was previously configured.
+ type: list
+ elements: str
+ vmm_uplink_standby:
+ description:
+ - A list of standby uplink IDs.
+ - At least one standby uplink must remain specified in the list when no active uplink is configured.
+ type: list
+ elements: str
+ netflow:
+ description:
+ - Determines if netflow should be enabled.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ primary_encap:
+ description:
+ - Determines the primary VLAN ID when using useg.
+ - Accepted values range between C(1) and C(4096).
+ type: int
+ resolution_immediacy:
+ description:
+ - Determines when the policies should be resolved and available.
+ - The APIC defaults to C(lazy) when unset during creation.
+ type: str
+ choices: [ immediate, lazy, pre-provision ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ promiscuous:
+ description:
+ - Allow/Disallow promiscuous mode in vmm domain
+ type: str
+ choices: [ accept, reject ]
+ default: reject
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+ custom_epg_name:
+ description:
+ - The custom epg name in VMM domain association.
+ type: str
+ delimiter:
+ description:
+ - The delimiter.
+ type: str
+ choices: [ "|", "~", "!", "@", "^", "+", "=" ]
+ untagged_vlan:
+ description:
+ - The access vlan is untagged.
+ type: bool
+ port_binding:
+ description:
+ - The port binding method.
+ type: str
+ choices: [ dynamic, ephemeral, static ]
+ port_allocation:
+ description:
+ - The port allocation method.
+ type: str
+ choices: [ elastic, fixed ]
+ number_of_ports:
+ description:
+ - The number of ports.
+ type: int
+ forged_transmits:
+ description:
+ - Allow forged transmits. A forged transmit occurs when a network adapter starts sending out traffic that identifies itself as something else.
+ type: str
+ choices: [ accept, reject ]
+ default: reject
+ mac_changes:
+ description:
+ - Allows definition of new MAC addresses for the network adapter within the virtual machine (VM).
+ type: str
+ choices: [ accept, reject ]
+ default: reject
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(ap), C(epg), and C(domain) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) M(cisco.aci.aci_ap), M(cisco.aci.aci_epg) M(cisco.aci.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.
+seealso:
+- module: cisco.aci.aci_ap
+- module: cisco.aci.aci_epg
+- module: cisco.aci.aci_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:RsDomAtt).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new physical domain to EPG binding
+ cisco.aci.aci_epg_to_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ domain: anstest
+ domain_type: phys
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing physical domain to EPG binding
+ cisco.aci.aci_epg_to_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ domain: anstest
+ domain_type: phys
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific physical domain to EPG binding
+ cisco.aci.aci_epg_to_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: anstest
+ epg: anstest
+ domain: anstest
+ domain_type: phys
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all domain to EPG bindings
+ cisco.aci.aci_epg_to_domain:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ allow_useg=dict(type="str", choices=["encap", "useg"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]), # Not required for querying all objects
+ deploy_immediacy=dict(type="str", choices=["immediate", "lazy"]),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile"]), # Not required for querying all objects
+ domain_type=dict(type="str", choices=["l2dom", "phys", "vmm"], aliases=["type"]), # Not required for querying all objects
+ encap=dict(type="int"),
+ encap_mode=dict(type="str", choices=["auto", "vlan", "vxlan"]),
+ switching_mode=dict(type="str", default="native", choices=["AVE", "native"]),
+ epg=dict(type="str", aliases=["name", "epg_name"]), # Not required for querying all objects
+ enhanced_lag_policy=dict(type="str", aliases=["lag_policy"]),
+ vmm_uplink_active=dict(type='list', elements='str'),
+ vmm_uplink_standby=dict(type='list', elements='str'),
+ netflow=dict(type="bool"),
+ 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"]), # Not required for querying all objects
+ vm_provider=dict(type="str", choices=["cloudfoundry", "kubernetes", "microsoft", "openshift", "openstack", "redhat", "vmware"]),
+ promiscuous=dict(type="str", default="reject", choices=["accept", "reject"]),
+ custom_epg_name=dict(type="str"),
+ delimiter=dict(type="str", choices=["|", "~", "!", "@", "^", "+", "="]),
+ untagged_vlan=dict(type="bool"),
+ port_binding=dict(type="str", choices=["dynamic", "ephemeral", "static"]),
+ port_allocation=dict(type="str", choices=["elastic", "fixed"]),
+ number_of_ports=dict(type="int"),
+ forged_transmits=dict(type="str", default="reject", choices=["accept", "reject"]),
+ mac_changes=dict(type="str", default="reject", choices=["accept", "reject"])
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["domain_type", "vmm", ["vm_provider"]],
+ ["state", "absent", ["ap", "domain", "domain_type", "epg", "tenant"]],
+ ["state", "present", ["ap", "domain", "domain_type", "epg", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ allow_useg = module.params.get("allow_useg")
+ ap = module.params.get("ap")
+ deploy_immediacy = module.params.get("deploy_immediacy")
+ domain = module.params.get("domain")
+ domain_type = module.params.get("domain_type")
+ vm_provider = module.params.get("vm_provider")
+ promiscuous = module.params.get("promiscuous")
+ custom_epg_name = module.params.get("custom_epg_name")
+ encap = module.params.get("encap")
+ if encap is not None:
+ if encap in range(1, 4097):
+ encap = "vlan-{0}".format(encap)
+ else:
+ module.fail_json(msg="Valid VLAN assignments are from 1 to 4096")
+ encap_mode = module.params.get("encap_mode")
+ switching_mode = module.params.get("switching_mode")
+ epg = module.params.get("epg")
+ enhanced_lag_policy = module.params.get("enhanced_lag_policy")
+ vmm_uplink_active = module.params.get("vmm_uplink_active")
+ vmm_uplink_standby = module.params.get("vmm_uplink_standby")
+ netflow = aci.boolean(module.params.get("netflow"), "enabled", "disabled")
+ primary_encap = module.params.get("primary_encap")
+ if primary_encap is not None:
+ if primary_encap in range(1, 4097):
+ primary_encap = "vlan-{0}".format(primary_encap)
+ else:
+ module.fail_json(msg="Valid VLAN assignments are from 1 to 4096")
+ resolution_immediacy = module.params.get("resolution_immediacy")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+
+ if domain_type in ["l2dom", "phys"] and vm_provider is not None:
+ module.fail_json(msg="Domain type '%s' cannot have a 'vm_provider'" % domain_type)
+
+ delimiter = module.params.get("delimiter")
+ untagged_vlan = 'yes' if module.params.get("untagged_vlan") is True else 'no'
+ port_binding = module.params.get("port_binding")
+ if port_binding == "static" or port_binding == "dynamic":
+ port_binding = "{0}Binding".format(port_binding)
+ port_allocation = module.params.get("port_allocation")
+ number_of_ports = module.params.get("number_of_ports")
+ forged_transmits = module.params.get("forged_transmits")
+ mac_changes = module.params.get("mac_changes")
+
+ child_classes = None
+ child_configs = None
+
+ # Compile the full domain for URL building
+ if domain_type == "vmm":
+ epg_domain = "uni/vmmp-{0}/dom-{1}".format(VM_PROVIDER_MAPPING[vm_provider], domain)
+ child_configs = [dict(vmmSecP=dict(attributes=dict(allowPromiscuous=promiscuous, forgedTransmits=forged_transmits, macChanges=mac_changes)))]
+ # check with child classes added on all versions
+ child_classes = ["vmmSecP"]
+
+ if vmm_uplink_active is not None or vmm_uplink_standby is not None:
+ uplink_order_cont = dict(fvUplinkOrderCont=dict(attributes=dict()))
+ if vmm_uplink_active is not None:
+ uplink_order_cont['fvUplinkOrderCont']['attributes']['active'] = ",".join(vmm_uplink_active)
+ if vmm_uplink_standby is not None:
+ uplink_order_cont['fvUplinkOrderCont']['attributes']['standby'] = ",".join(vmm_uplink_standby)
+ child_configs.append(uplink_order_cont)
+ child_classes.append("fvUplinkOrderCont")
+
+ if enhanced_lag_policy is not None:
+ lag_policy = epg_domain + "/vswitchpolcont/enlacplagp-{0}".format(enhanced_lag_policy)
+ child_configs.append(
+ dict(
+ fvAEPgLagPolAtt=dict(
+ attributes=dict(annotation=""), children=[dict(fvRsVmmVSwitchEnhancedLagPol=dict(attributes=dict(annotation="", tDn=lag_policy)))]
+ )
+ )
+ )
+ child_classes.append("fvAEPgLagPolAtt")
+
+ elif domain_type == "l2dom":
+ epg_domain = "uni/l2dom-{0}".format(domain)
+ elif domain_type == "phys":
+ epg_domain = "uni/phys-{0}".format(domain)
+ else:
+ epg_domain = None
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ subclass_3=dict(
+ aci_class="fvRsDomAtt",
+ aci_rn="rsdomAtt-[{0}]".format(epg_domain),
+ module_object=epg_domain,
+ target_filter={"tDn": epg_domain},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvRsDomAtt",
+ class_config=dict(
+ classPref=allow_useg,
+ encap=encap,
+ encapMode=encap_mode,
+ switchingMode=switching_mode,
+ instrImedcy=deploy_immediacy,
+ netflowPref=netflow,
+ primaryEncap=primary_encap,
+ resImedcy=resolution_immediacy,
+ customEpgName=custom_epg_name,
+ delimiter=delimiter,
+ untagged=untagged_vlan,
+ bindingType=port_binding,
+ portAllocation=port_allocation,
+ numPorts=number_of_ports
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="fvRsDomAtt")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_esg.py b/ansible_collections/cisco/aci/plugins/modules/aci_esg.py
new file mode 100644
index 00000000..e4cdbb9c
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_esg.py
@@ -0,0 +1,374 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+
+DOCUMENTATION = r"""
+---
+module: aci_esg
+short_description: Manage Endpoint Security Groups (ESGs) objects (fv:ESg)
+description:
+- Manage Endpoint Security Groups (ESGs) on Cisco ACI fabrics.
+
+options:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application profile.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ esg:
+ description:
+ - Name of the Endpoint Security Group.
+ type: str
+ aliases: [ esg_name, name ]
+ admin_state:
+ description:
+ - Use C(false) to set 'Admin Up' on the ESG Admin state and is the default.
+ - Use C(true) to set 'Admin Shut' on the ESG Admin state
+ type: bool
+ choices: [ false, true ]
+ vrf:
+ description:
+ - Name of the VRF
+ type: str
+ aliases: [ vrf_name ]
+ description:
+ description:
+ - Endpoint security group description.
+ type: str
+ aliases: [ descr ]
+ intra_esg_isolation:
+ description:
+ - The default value of Intra ESG Isolation is C(unenforced).
+ type: str
+ choices: [ enforced, unenforced ]
+ preferred_group_member:
+ description:
+ - The default value of Preferred Group Member is C(exclude).
+ type: str
+ choices: [ exclude, include ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_aep_to_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:AttEntityP) and B(infra:ProvAcc).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+
+EXAMPLES = r"""
+- name: Add a new ESG
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: intranet
+ esg: web_esg
+ vrf: 'default'
+ description: Web Intranet ESG
+ state: present
+ delegate_to: localhost
+
+- name: Add list of ESGs
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: ticketing
+ esg: "{{ item.esg }}"
+ description: Ticketing ESG
+ vrf: 'default'
+ state: present
+ delegate_to: localhost
+ with_items:
+ - esg: web
+ - esg: database
+
+- name: Query an ESG
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: ticketing
+ esg: web_esg
+ state: query
+ delegate_to: localhost
+
+- name: Query all ESGs
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Query all ESGs with a Specific Name
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ esg: web_esg
+ state: query
+ delegate_to: localhost
+
+- name: Query all ESGs of an App Profile
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ ap: ticketing
+ state: query
+ delegate_to: localhost
+
+- name: Remove an ESG
+ cisco.aci.aci_esg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ app_profile: intranet
+ esg: web_esg
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ esg=dict(type="str", aliases=["name", "esg_name"]),
+ admin_state=dict(type="bool", choices=[False, True]), # ESG Admin State
+ vrf=dict(type="str", aliases=["vrf_name"]), # ESG VRF name
+ description=dict(type="str", aliases=["descr"]),
+ intra_esg_isolation=dict(
+ type="str",
+ choices=["enforced", "unenforced"],
+ ), # Intra ESG Isolation
+ preferred_group_member=dict(type="str", choices=["exclude", "include"]), # Preferred Group Member
+ state=dict(
+ type="str",
+ default="present",
+ choices=["absent", "present", "query"],
+ ),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap", "esg"]],
+ ["state", "present", ["tenant", "ap", "esg", "vrf"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ esg = module.params.get("esg")
+ admin_state = module.params.get("admin_state")
+ vrf = module.params.get("vrf")
+ description = module.params.get("description")
+ intra_esg_isolation = module.params.get("intra_esg_isolation")
+ preferred_group_member = module.params.get("preferred_group_member")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvESg",
+ aci_rn="esg-{0}".format(esg),
+ module_object=esg,
+ target_filter={"name": esg},
+ ),
+ child_classes=[
+ "fvRsScope",
+ ],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ state_mapping = {True: "yes", False: "no"}
+ shutdown = state_mapping.get(admin_state)
+ # VRF Selection - fvRsScope
+ aci.payload(
+ aci_class="fvESg",
+ class_config=dict(
+ name=esg,
+ descr=description,
+ shutdown=shutdown,
+ pcEnfPref=intra_esg_isolation,
+ prefGrMemb=preferred_group_member,
+ nameAlias=name_alias,
+ ),
+ child_configs=[dict(fvRsScope=dict(attributes=dict(tnFvCtxName=vrf)))],
+ )
+
+ aci.get_diff(aci_class="fvESg")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_esg_contract_master.py b/ansible_collections/cisco/aci/plugins/modules/aci_esg_contract_master.py
new file mode 100644
index 00000000..2641f4dc
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_esg_contract_master.py
@@ -0,0 +1,296 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_esg_contract_master
+short_description: Manage ESG contract master relationships (fv:RsSecInherited)
+description:
+- Manage Endpoint Security Groups (ESG) contract master relationships on Cisco ACI fabrics.
+
+options:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application profile.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ esg:
+ description:
+ - Name of the Endpoint Security Group.
+ type: str
+ aliases: [ esg_name ]
+ contract_master_ap:
+ description:
+ - Name of the application profile where the contract master ESG is.
+ type: str
+ contract_master_esg:
+ description:
+ - Name of the ESG which serves as contract master.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_esg
+- name: Manage Endpoint Security Groups (ESGs) objects (fv:ESg)
+ description: Manage Endpoint Security Groups (ESGs) on Cisco ACI fabrics.
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add an ESG contract master
+ cisco.aci.aci_esg_contract_master:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: apName
+ esg: esgName
+ contract_master_ap: ap
+ contract_master_esg: contract_esg
+ state: present
+ delegate_to: localhost
+
+- name: Query an ESG contract master
+ cisco.aci.aci_esg_contract_master:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: apName
+ esg: esgName
+ contract_master_ap: ap
+ contract_master_esg: contract_esg
+ state: query
+ delegate_to: localhost
+
+- name: Remove an ESG contract master
+ cisco.aci.aci_esg_contract_master:
+ host: apic_host
+ username: admin
+ password: SomeSecretPassword
+ tenant: anstest
+ ap: apName
+ esg: esgName
+ contract_master_ap: ap
+ contract_master_esg: contract_esg
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ esg=dict(type="str", aliases=["esg_name"]),
+ contract_master_ap=dict(type="str"),
+ contract_master_esg=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap", "esg", "contract_master_ap", "contract_master_esg"]],
+ ["state", "present", ["tenant", "ap", "esg", "contract_master_ap", "contract_master_esg"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ esg = module.params.get("esg")
+ contract_master_ap = module.params.get("contract_master_ap")
+ contract_master_esg = module.params.get("contract_master_esg")
+ state = module.params.get("state")
+
+ contract_master = None
+ if contract_master_ap is not None and contract_master_esg is not None:
+ contract_master = "uni/tn-{0}/ap-{1}/esg-{2}".format(tenant, contract_master_ap, contract_master_esg)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvESg",
+ aci_rn="esg-{0}".format(esg),
+ module_object=esg,
+ target_filter={"name": esg},
+ ),
+ subclass_3=dict(
+ aci_class="fvRsSecInherited",
+ aci_rn="rssecInherited-[{0}]".format(contract_master),
+ module_object=contract_master,
+ target_filter={"tDn": contract_master},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="fvRsSecInherited", class_config=dict(tDn=contract_master))
+
+ aci.get_diff(aci_class="fvRsSecInherited")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_esg_epg_selector.py b/ansible_collections/cisco/aci/plugins/modules/aci_esg_epg_selector.py
new file mode 100644
index 00000000..5ad3d344
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_esg_epg_selector.py
@@ -0,0 +1,348 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+
+DOCUMENTATION = r"""
+---
+module: aci_esg_epg_selector
+short_description: Manage ESG - EPG Selectors (fv:fvEPgSelector)
+description:
+- Manage Endpoint Security Groups - EPG Selectors on Cisco ACI fabrics.
+
+options:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application profile.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ esg:
+ description:
+ - Name of the Endpoint Security Group.
+ type: str
+ aliases: [ esg_name ]
+ epg_ap:
+ description:
+ - Name of the Application profile which contains the EPG.
+ type: str
+ epg:
+ description:
+ - Name of the EPG which is used to create EPG Selector object under the ESG.
+ type: str
+ aliases: [ epg_name ]
+ description:
+ description:
+ - Description of the ESG Tag Selector.
+ type: str
+ aliases: [ epg_selector_description ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_esg
+- name: Manage Endpoint Security Groups (ESGs) objects (fv:ESg)
+ description: Manage Endpoint Security Groups (ESGs) on Cisco ACI fabrics.
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+
+EXAMPLES = r"""
+- name: Add an EPG selector
+ cisco.aci.aci_esg_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ epg_ap: production_ap1
+ epg: production_ap1-epg
+ description: epg-test-description
+ state: present
+ delegate_to: localhost
+
+- name: Add list of EPG selectors
+ cisco.aci.aci_esg_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: "{{ item.esg }}"
+ epg_ap: "{{ item.epg_ap }}"
+ epg: "{{ item.epg }}"
+ description: epg-test-description
+ state: present
+ delegate_to: localhost
+ with_items:
+ - {"epg_ap": "production_ap1", "epg": "epg-test1", "esg": "web_esg"}
+ - {"epg_ap": "production_ap1", "epg": "epg-test2", "esg": "web_esg"}
+
+- name: Query an EPG selector with esg and epg name
+ cisco.aci.aci_esg_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ epg_ap: production_ap1
+ epg: production_ap1-epg
+ state: query
+ delegate_to: localhost
+
+- name: Query all EPG selectors under a application profile
+ cisco.aci.aci_esg_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ state: query
+ delegate_to: localhost
+
+- name: Query all EPG selectors
+ cisco.aci.aci_esg_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove an EPG selector
+ cisco.aci.aci_esg_epg_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ epg_ap: production_ap1
+ epg: production_ap1-epg
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ esg=dict(type="str", aliases=["esg_name"]),
+ epg_ap=dict(type="str"),
+ epg=dict(type="str", aliases=["epg_name"]),
+ description=dict(type="str", aliases=["epg_selector_description"]),
+ state=dict(
+ type="str",
+ default="present",
+ choices=["absent", "present", "query"],
+ ),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap", "esg", "epg", "epg_ap"]],
+ ["state", "present", ["tenant", "ap", "esg", "epg", "epg_ap"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ esg = module.params.get("esg")
+ epg_ap = module.params.get("epg_ap")
+ epg = module.params.get("epg")
+ description = module.params.get("description")
+ state = module.params.get("state")
+
+ matchEpgDn = "uni/tn-{0}/ap-{1}/epg-{2}".format(tenant, epg_ap, epg)
+ epgselector = "epgselector-[{0}]".format(matchEpgDn)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvESg",
+ aci_rn="esg-{0}".format(esg),
+ module_object=esg,
+ target_filter={"name": esg},
+ ),
+ subclass_3=dict(
+ aci_class="fvEPgSelector",
+ aci_rn=epgselector,
+ module_object=epg,
+ target_filter={"matchEpgDn": epg},
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvEPgSelector",
+ class_config=dict(
+ matchEpgDn=matchEpgDn,
+ descr=description,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvEPgSelector")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_esg_ip_subnet_selector.py b/ansible_collections/cisco/aci/plugins/modules/aci_esg_ip_subnet_selector.py
new file mode 100644
index 00000000..368f9e6d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_esg_ip_subnet_selector.py
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+
+DOCUMENTATION = r"""
+---
+module: aci_esg_ip_subnet_selector
+short_description: Manage ESG IP Subnet selector(fv:EPSelector)
+description:
+- Manage Endpoint Security Groups (ESG) IP Subnet selector on Cisco ACI fabrics.
+
+options:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application profile.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ esg:
+ description:
+ - Name of the Endpoint Security Group.
+ type: str
+ aliases: [ esg_name ]
+ ip:
+ description:
+ - IP address of the subnet selector.
+ type: str
+ aliases: [ subnet ]
+ description:
+ description:
+ - Description of the ESG IP Subnet selector.
+ type: str
+ aliases: [ ip_subnet_selector_description ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_esg
+- name: Manage Endpoint Security Groups (ESGs) objects (fv:ESg)
+ description: Manage Endpoint Security Groups (ESGs) on Cisco ACI fabrics.
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+
+EXAMPLES = r"""
+- name: Add an IP subnet selector
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ ip: "10.0.0.0"
+ description: "IP Subnet Selector Description"
+ state: present
+ delegate_to: localhost
+
+- name: Query all IP subnet selector
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove an IP subnet selector
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ ip: "10.0.0.0"
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ esg=dict(type="str", aliases=["esg_name"]),
+ ip=dict(type="str", aliases=["subnet"]),
+ description=dict(type="str", aliases=["ip_subnet_selector_description"]),
+ state=dict(
+ type="str",
+ default="present",
+ choices=["absent", "present", "query"],
+ ),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap", "esg", "ip"]],
+ ["state", "present", ["tenant", "ap", "esg", "ip"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ esg = module.params.get("esg")
+ ip = module.params.get("ip")
+ description = module.params.get("description")
+ state = module.params.get("state")
+
+ match_expression = "ip=='{0}'".format(ip)
+ subnet_selector_rn = "epselector-[{0}]".format(match_expression)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvESg",
+ aci_rn="esg-{0}".format(esg),
+ module_object=esg,
+ target_filter={"name": esg},
+ ),
+ subclass_3=dict(
+ aci_class="fvEPSelector",
+ aci_rn=subnet_selector_rn,
+ module_object=ip,
+ target_filter={},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvEPSelector",
+ class_config=dict(
+ matchExpression=match_expression,
+ descr=description,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvEPSelector")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_esg_tag_selector.py b/ansible_collections/cisco/aci/plugins/modules/aci_esg_tag_selector.py
new file mode 100644
index 00000000..834249b4
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_esg_tag_selector.py
@@ -0,0 +1,344 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+
+DOCUMENTATION = r"""
+---
+module: aci_esg_tag_selector
+short_description: Manage ESG Tag Selectors (fv:TagSelector)
+description:
+- Manage Endpoint Security Groups Tag Selectors on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - The name of the application profile.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ esg:
+ description:
+ - Name of the Endpoint Security Group.
+ type: str
+ aliases: [ esg_name ]
+ name:
+ description:
+ - ESG Tag Selector Key Name
+ type: str
+ aliases: [ match_key ]
+ operator:
+ description:
+ - C(equals) is the default operator type of the ESG Tag Selector.
+ - C(contains) is used to match values partially.
+ - C(regex) allows to pass patterns values.
+ type: str
+ choices: [ contains, equals, regex ]
+ aliases: [ value_operator ]
+ match_value:
+ description:
+ - Value filed of the ESG Tag Selector.
+ type: str
+ description:
+ description:
+ - Description of the ESG Tag Selector.
+ type: str
+ aliases: [ tag_selector_description ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_esg
+- name: Manage Endpoint Security Groups (ESGs) objects (fv:ESg)
+ description: Manage Endpoint Security Groups (ESGs) on Cisco ACI fabrics.
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Tag Selector
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ name: tag-selector-key
+ match_value: tag-selector-value
+ description: tag-selector-description
+ state: present
+ delegate_to: localhost
+
+- name: Add list of Tag Selectors
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ name: "{{ item.key }}"
+ match_value: "{{ item.value }}"
+ description: "{{ item.description }}"
+ state: present
+ delegate_to: localhost
+ with_items:
+ - {key: tag_selector_key_0, value: tag_selector_value_0, description: tag_selector_description_0}
+ - {key: tag_selector_key_1, value: tag_selector_value_1, description: tag_selector_description_1}
+
+- name: Query all Tag Selectors
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Query all Tag Selectors with name and value
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: tag-selector-key
+ match_value: tag-selector-value
+ state: query
+ delegate_to: localhost
+
+- name: Remove a Tag Selectors
+ cisco.aci.aci_esg_tag_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ap: production_ap
+ esg: web_esg
+ name: tag-selector-key
+ match_value: tag-selector-value
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ ap=dict(type="str", aliases=["app_profile", "app_profile_name"]),
+ esg=dict(type="str", aliases=["esg_name"]),
+ name=dict(type="str", aliases=["match_key"]), # ESG Tag Selector key name
+ operator=dict(type="str", choices=["contains", "equals", "regex"], aliases=["value_operator"]), # ESG Tag Selector operator type
+ match_value=dict(type="str"), # ESG Tag Selector match value
+ description=dict(type="str", aliases=["tag_selector_description"]),
+ state=dict(
+ type="str",
+ default="present",
+ choices=["absent", "present", "query"],
+ ),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "ap", "esg", "name", "match_value"]],
+ ["state", "present", ["tenant", "ap", "esg", "name", "match_value"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ esg = module.params.get("esg")
+ name = module.params.get("name")
+ operator = module.params.get("operator")
+ match_value = module.params.get("match_value")
+ description = module.params.get("description")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvESg",
+ aci_rn="esg-{0}".format(esg),
+ module_object=esg,
+ target_filter={"name": esg},
+ ),
+ subclass_3=dict(
+ aci_class="fvTagSelector",
+ aci_rn="tagselectorkey-[{0}]-value-[{1}]".format(name, match_value),
+ module_object=name,
+ target_filter={"matchKey": name, "matchValue": match_value},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvTagSelector",
+ class_config=dict(
+ matchKey=name,
+ matchValue=match_value,
+ valueOperator=operator,
+ descr=description,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvTagSelector")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_profile.py
new file mode 100644
index 00000000..09c87a38
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_profile.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_fabric_leaf_profile
+short_description: Manage fabric leaf profiles (fabric:LeafP).
+description:
+- Manage fabric leaf switch profiles in an ACI fabric.
+options:
+ name:
+ description:
+ - Name of the fabric leaf switch profile
+ type: str
+ aliases: [ leaf_profile, leaf_switch_profile ]
+ description:
+ description:
+ - description of the profile
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fabricLeafP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a leaf switch profile
+ cisco.aci.aci_fabric_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_leaf_profile
+ state: present
+ delegate_to: localhost
+
+- name: Remove a leaf switch profile
+ cisco.aci.aci_fabric_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_leaf_profile
+ state: absent
+ delegate_to: localhost
+
+- name: Query a leaf profile
+ cisco.aci.aci_fabric_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_leaf_profile
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all leaf profiles
+ cisco.aci.aci_fabric_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["leaf_switch_profile", "leaf_profile"]),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ child_classes = ["fabricLeafS"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricLeafP",
+ aci_rn="fabric/leprof-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricLeafP",
+ class_config=dict(name=name, descr=description),
+ )
+
+ aci.get_diff(aci_class="fabricLeafP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_switch_assoc.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_switch_assoc.py
new file mode 100644
index 00000000..c979f039
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_leaf_switch_assoc.py
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_fabric_leaf_switch_assoc
+short_description: Manage leaf switch bindings to profiles and policy groups (fabric:LeafS and fabric:RsLeNodePGrp).
+description:
+- Manage fabric leaf switch associations (fabric:LeafS) to an existing fabric
+ leaf profile (fabric:leafP) in an ACI fabric, and bind them to a
+ policy group (fabric:RsLeNodePGrp)
+options:
+ profile:
+ description:
+ - Name of an existing fabric leaf switch profile
+ type: str
+ aliases: [ leaf_profile, leaf_switch_profile ]
+ name:
+ description:
+ - Name of the switch association
+ type: str
+ aliases: [ association_name, switch_association ]
+ policy_group:
+ description:
+ - Name of an existing leaf switch policy group
+ type: str
+ description:
+ description:
+ - Description of the Fabric Switch Association
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(profile) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_fabric_leaf_profile) module can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fabricLeafS) and B(fabricRsLeNodePGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a leaf switch profile association
+ cisco.aci.aci_fabric_leaf_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ profile: my_leaf_profile
+ name: my_leaf_switch_assoc
+ policy_group: my_leaf_pol_grp
+ state: present
+ delegate_to: localhost
+- name: Remove a leaf switch profile association
+ cisco.aci.aci_fabric_leaf_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ profile: my_leaf_profile
+ name: my_leaf_switch_assoc
+ state: absent
+ delegate_to: localhost
+- name: Query a leaf profile association
+ cisco.aci.aci_fabric_leaf_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ profile: my_leaf_profile
+ name: my_leaf_switch_assoc
+ state: query
+ delegate_to: localhost
+ register: query_result
+- name: Query all leaf profiles
+ cisco.aci.aci_fabric_leaf_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ profile=dict(type="str", aliases=["leaf_profile", "leaf_switch_profile"]),
+ name=dict(type="str", aliases=["association_name", "switch_association"]),
+ policy_group=dict(type="str"),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["profile", "name"]],
+ ["state", "present", ["profile", "name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ profile = module.params.get("profile")
+ name = module.params.get("name")
+ policy_group = module.params.get("policy_group")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ child_classes = ["fabricRsLeNodePGrp", "fabricNodeBlk"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricLeafP",
+ aci_rn="fabric/leprof-{0}".format(profile),
+ module_object=profile,
+ target_filter={"name": profile},
+ ),
+ subclass_1=dict(
+ aci_class="fabricLeafS",
+ aci_rn="leaves-{0}-typ-range".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if policy_group:
+ tDn = "uni/fabric/funcprof/lenodepgrp-{0}".format(policy_group)
+ child_configs.append(dict(fabricRsLeNodePGrp=dict(attributes=dict(tDn=tDn))))
+ aci.payload(
+ aci_class="fabricLeafS",
+ class_config=dict(name=name, descr=description),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="fabricLeafS")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py
new file mode 100644
index 00000000..defa4f78
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_node.py
@@ -0,0 +1,290 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_fabric_node
+short_description: Manage Fabric Node Members (fabric:NodeIdentP)
+description:
+- Manage Fabric Node Members on Cisco ACI fabrics.
+options:
+ pod_id:
+ description:
+ - The pod id of the new Fabric Node Member.
+ type: int
+ serial:
+ description:
+ - Serial Number for the new Fabric Node Member.
+ type: str
+ aliases: [ serial_number ]
+ node_id:
+ description:
+ - Node ID Number for the new Fabric Node Member.
+ type: int
+ switch:
+ description:
+ - Switch Name for the new Fabric Node Member.
+ type: str
+ aliases: [ name, switch_name ]
+ description:
+ description:
+ - Description for the new Fabric Node Member.
+ type: str
+ aliases: [ descr ]
+ role:
+ description:
+ - Role for the new Fabric Node Member.
+ type: str
+ aliases: [ role_name ]
+ choices: [ leaf, spine, unspecified ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fabric:NodeIdentP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+"""
+
+EXAMPLES = r"""
+- name: Add fabric node
+ cisco.aci.aci_fabric_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ serial: FDO2031124L
+ node_id: 1011
+ switch: fab4-sw1011
+ state: present
+ delegate_to: localhost
+
+- name: Remove fabric node
+ cisco.aci.aci_fabric_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ serial: FDO2031124L
+ node_id: 1011
+ state: absent
+ delegate_to: localhost
+
+- name: Query fabric nodes
+ cisco.aci.aci_fabric_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: '?rsp-prop-include=config-only'
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+# NOTE: (This problem is also present on the APIC GUI)
+# NOTE: When specifying a C(role) the new Fabric Node Member will be created but Role on GUI will be "unknown", hence not what seems to be a module problem
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["node_id", "serial"]],
+ ["state", "present", ["node_id", "serial"]],
+ ],
+ )
+
+ pod_id = module.params.get("pod_id")
+ serial = module.params.get("serial")
+ node_id = module.params.get("node_id")
+ switch = module.params.get("switch")
+ description = module.params.get("description")
+ role = module.params.get("role")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricNodeIdentP",
+ aci_rn="controller/nodeidentpol/nodep-{0}".format(serial),
+ module_object=serial,
+ target_filter={"serial": serial},
+ )
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricNodeIdentP",
+ class_config=dict(
+ descr=description,
+ name=switch,
+ nodeId=node_id,
+ podId=pod_id,
+ # NOTE: Originally we were sending 'rn', but now we need 'dn' for idempotency
+ # FIXME: Did this change with ACI version ?
+ dn="uni/controller/nodeidentpol/nodep-{0}".format(serial),
+ # rn='nodep-{0}'.format(serial),
+ role=role,
+ serial=serial,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fabricNodeIdentP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json(**aci.result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_pod_policy_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_pod_policy_group.py
new file mode 100644
index 00000000..2d518021
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_pod_policy_group.py
@@ -0,0 +1,320 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_fabric_pod_policy_group
+short_description: Manage Fabric Pod Policy Groups (fabric:PodPGrp)
+description:
+- Fabric Pod Policy Group (fabric:PodPGrp) configuration on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - Name of the policy group
+ type: str
+ aliases: [ policy_group, policy_group_name, pod_policy_group ]
+ date_time_policy:
+ description:
+ - NTP policy to bind to the policy group
+ type: str
+ aliases: [ ntp_policy ]
+ isis_policy:
+ description:
+ - IS-IS policy to bind to the policy group
+ type: str
+ coop_group_policy:
+ description:
+ - COOP group policy to bind to the policy group
+ type: str
+ aliases: [ coop_policy ]
+ bgp_rr_policy:
+ description:
+ - BGP route reflector policy to bind to the policy group
+ type: str
+ management_access_policy:
+ description:
+ - Management access policy to bind to the policy group
+ type: str
+ aliases: [ management_policy, mgmt_policy ]
+ snmp_policy:
+ description:
+ - SNMP policy to bind to the policy group
+ type: str
+ macsec_policy:
+ description:
+ - MACSec policy to bind to the policy group
+ type: str
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fabricPodPGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new fabric pod policy group
+ cisco.aci.aci_fabric_pod_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_pod_pol_grp
+ snmp_policy: my_snmp_pol
+ bgp_rr_policy: my_bgp_rr_pol
+ state: present
+ delegate_to: localhost
+
+- name: Remove a fabric pod policy group
+ cisco.aci.aci_fabric_pod_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_pod_pol_grp
+ state: absent
+ delegate_to: localhost
+
+- name: Query a fabric pod policy group
+ cisco.aci.aci_fabric_pod_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_pod_pol_grp
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all fabric pod policy groups
+ cisco.aci.aci_fabric_pod_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type='str', aliases=['policy_group', 'policy_group_name', 'pod_policy_group']),
+ date_time_policy=dict(type='str', aliases=['ntp_policy']),
+ isis_policy=dict(type='str'),
+ coop_group_policy=dict(type='str', aliases=['coop_policy']),
+ bgp_rr_policy=dict(type='str'),
+ management_access_policy=dict(type='str', aliases=['management_policy', 'mgmt_policy']),
+ snmp_policy=dict(type='str'),
+ macsec_policy=dict(type='str'),
+ name_alias=dict(type="str"),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['name']],
+ ['state', 'present', ['name']],
+ ],
+ )
+ aci = ACIModule(module)
+
+ name = module.params.get('name')
+ date_time_policy = module.params.get('date_time_policy')
+ isis_policy = module.params.get('isis_policy')
+ coop_group_policy = module.params.get('coop_group_policy')
+ bgp_rr_policy = module.params.get('bgp_rr_policy')
+ management_access_policy = module.params.get('management_access_policy')
+ snmp_policy = module.params.get('snmp_policy')
+ macsec_policy = module.params.get('macsec_policy')
+ name_alias = module.params.get("name_alias")
+ state = module.params.get('state')
+ child_classes = ['fabricRsSnmpPol', 'fabricRsPodPGrpIsisDomP',
+ 'fabricRsPodPGrpCoopP', 'fabricRsPodPGrpBGPRRP',
+ 'fabricRsTimePol', 'fabricRsMacsecPol', 'fabricRsCommPol']
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class='fabricPodPGrp',
+ aci_rn='fabric/funcprof/podpgrp-{0}'.format(name),
+ module_object=name,
+ target_filter={'name': name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ child_configs = []
+ if date_time_policy is not None:
+ child_configs.append(dict(fabricRsTimePol=dict(attributes=dict(tnDatetimePolName=date_time_policy))))
+ if isis_policy is not None:
+ child_configs.append(dict(fabricRsPodPGrpIsisDomP=dict(attributes=dict(tnIsisDomPolName=isis_policy))))
+ if coop_group_policy is not None:
+ child_configs.append(dict(fabricRsPodPGrpCoopP=dict(attributes=dict(tnCoopPolName=coop_group_policy))))
+ if bgp_rr_policy is not None:
+ child_configs.append(dict(fabricRsPodPGrpBGPRRP=dict(attributes=dict(tnBgpInstPolName=bgp_rr_policy))))
+ if management_access_policy is not None:
+ child_configs.append(dict(fabricRsCommPol=dict(attributes=dict(tnCommPolName=management_access_policy))))
+ if snmp_policy is not None:
+ child_configs.append(dict(fabricRsSnmpPol=dict(attributes=dict(tnSnmpPolName=snmp_policy))))
+ if macsec_policy is not None:
+ child_configs.append(dict(fabricRsMacsecPol=dict(attributes=dict(tnMacsecFabIfPolName=macsec_policy))))
+ aci.payload(
+ aci_class='fabricPodPGrp',
+ class_config=dict(
+ name=name,
+ nameAlias=name_alias
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class='fabricPodPGrp')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py
new file mode 100644
index 00000000..e4f8e814
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_scheduler.py
@@ -0,0 +1,353 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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 = """
+---
+module: aci_fabric_scheduler
+
+short_description: This modules creates ACI schedulers.
+
+
+description:
+ - With the module you can create schedule policies that can be a shell, onetime execution or recurring
+
+options:
+ name:
+ description:
+ - The name of the Scheduler.
+ type: str
+ aliases: [ scheduler_name ]
+ description:
+ description:
+ - Description for the Scheduler.
+ type: str
+ aliases: [ descr ]
+ recurring:
+ description:
+ - If you want to make the Scheduler a recurring it would be a "True" and for a
+ oneTime execution it would be "False". For a shell just exclude this option from
+ the task
+ type: bool
+ windowname:
+ description:
+ - This is the name for your what recurring or oneTime execution
+ type: str
+ concurCap:
+ description:
+ - This is the amount of devices that can be executed on at a time
+ type: int
+ maxTime:
+ description:
+ - This is the amount MAX amount of time a process can be executed
+ type: str
+ date:
+ description:
+ - This is the date and time that the scheduler will execute
+ type: str
+ hour:
+ description:
+ - This set the hour of execution
+ type: int
+ minute:
+ description:
+ - This sets the minute of execution, used in conjunction with hour
+ type: int
+ day:
+ description:
+ - This sets the day when execution will take place
+ type: str
+ default: "every-day"
+ choices: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday', 'even-day', 'odd-day', 'every-day']
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ default: present
+ choices: [ absent, present, query ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+author:
+ - Steven Gerhart (@sgerhart)
+"""
+
+EXAMPLES = r"""
+ - name: Simple Scheduler (Empty)
+ cisco.aci.aci_fabric_scheduler:
+ host: "{{ inventory_hostname }}"
+ username: "{{ user }}"
+ password: "{{ pass }}"
+ validate_certs: no
+ name: simpleScheduler
+ state: present
+ - name: Remove Simple Scheduler
+ cisco.aci.aci_fabric_scheduler:
+ host: "{{ inventory_hostname }}"
+ username: "{{ user }}"
+ password: "{{ pass }}"
+ validate_certs: no
+ name: simpleScheduler
+ state: absent
+ - name: One Time Scheduler
+ cisco.aci.aci_fabric_scheduler:
+ host: "{{ inventory_hostname }}"
+ username: "{{ user }}"
+ password: "{{ pass }}"
+ validate_certs: no
+ name: OneTime
+ windowname: OneTime
+ recurring: False
+ concurCap: 20
+ date: "2018-11-20T24:00:00"
+ state: present
+ - name: Recurring Scheduler
+ cisco.aci.aci_fabric_scheduler:
+ host: "{{ inventory_hostname }}"
+ username: "{{ user }}"
+ password: "{{ pass }}"
+ validate_certs: no
+ name: Recurring
+ windowname: Recurring
+ recurring: True
+ concurCap: 20
+ hour: 13
+ minute: 30
+ day: Tuesday
+ state: present
+"""
+
+RETURN = """
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["scheduler_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ windowname=dict(type="str"),
+ recurring=dict(type="bool"),
+ concurCap=dict(type="int"), # Number of devices it will run against concurrently
+ maxTime=dict(type="str"), # The amount of minutes a process will be able to run (unlimited or dd:hh:mm:ss)
+ date=dict(type="str"), # The date the process will run YYYY-MM-DDTHH:MM:SS
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ hour=dict(type="int"),
+ minute=dict(type="int"),
+ day=dict(
+ type="str",
+ default="every-day",
+ choices=["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "every-day", "even-day", "odd-day"],
+ ),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ state = module.params.get("state")
+ name = module.params.get("name")
+ windowname = module.params.get("windowname")
+ recurring = module.params.get("recurring")
+ date = module.params.get("date")
+ hour = module.params.get("hour")
+ minute = module.params.get("minute")
+ maxTime = module.params.get("maxTime")
+ concurCap = module.params.get("concurCap")
+ day = module.params.get("day")
+ description = module.params.get("description")
+ name_alias = module.params.get("name_alias")
+
+ if recurring:
+ child_configs = [
+ dict(
+ trigRecurrWindowP=dict(
+ attributes=dict(
+ name=windowname,
+ hour=hour,
+ minute=minute,
+ procCa=maxTime,
+ concurCap=concurCap,
+ day=day,
+ )
+ )
+ )
+ ]
+ elif recurring is False:
+ child_configs = [
+ dict(
+ trigAbsWindowP=dict(
+ attributes=dict(
+ name=windowname,
+ procCap=maxTime,
+ concurCap=concurCap,
+ date=date,
+ )
+ )
+ )
+ ]
+ else:
+ child_configs = []
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="trigSchedP",
+ aci_rn="fabric/schedp-{0}".format(name),
+ target_filter={"name": name},
+ module_object=name,
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="trigSchedP",
+ class_config=dict(
+ name=name,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="trigSchedP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_profile.py
new file mode 100644
index 00000000..364b613f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_profile.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_fabric_spine_profile
+short_description: Manage fabric spine profiles (fabric:SpineP).
+description:
+- Manage fabric spine switch profiles in an ACI fabric.
+options:
+ name:
+ description:
+ - Name of the fabric spine switch profile
+ type: str
+ aliases: [ spine_profile, spine_switch_profile ]
+ description:
+ description:
+ - description of the profile
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fabricSpineP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a spine switch profile
+ cisco.aci.aci_fabric_spine_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_spine_profile
+ state: present
+ delegate_to: localhost
+
+- name: Remove a spine switch profile
+ cisco.aci.aci_fabric_spine_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_spine_profile
+ state: absent
+ delegate_to: localhost
+
+- name: Query a spine profile
+ cisco.aci.aci_fabric_spine_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_spine_profile
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all spine profiles
+ cisco.aci.aci_fabric_spine_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["spine_switch_profile", "spine_profile"]),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ child_classes = ["fabricSpineS"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricSpineP",
+ aci_rn="fabric/spprof-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricSpineP",
+ class_config=dict(name=name, descr=description),
+ )
+
+ aci.get_diff(aci_class="fabricSpineP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_switch_assoc.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_switch_assoc.py
new file mode 100644
index 00000000..46ee17a5
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_spine_switch_assoc.py
@@ -0,0 +1,286 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_fabric_spine_switch_assoc
+short_description: Manage spine switch bindings to profiles and policy groups (fabric:SpineS and fabric:RsSpNodePGrp).
+description:
+- Manage fabric spine switch associations (fabric:SpineS) to an existing fabric
+ spine profile (fabric:SpineP) in an ACI fabric, and bind them to a
+ policy group (fabric:RsSpNodePGrp)
+options:
+ profile:
+ description:
+ - Name of an existing fabric spine switch profile
+ type: str
+ aliases: [ spine_profile, spine_switch_profile ]
+ name:
+ description:
+ - Name of the switch association
+ type: str
+ aliases: [ association_name, switch_association ]
+ policy_group:
+ description:
+ - Name of an existing spine switch policy group
+ type: str
+ description:
+ description:
+ - Description of the Fabric Switch Association
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(profile) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_fabric_spine_profile) module can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fabricSpineS) and B(fabricRsSpNodePGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a spine switch profile association
+ cisco.aci.aci_fabric_spine_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ profile: my_spine_profile
+ name: my_spine_switch_assoc
+ policy_group: my_spine_pol_grp
+ state: present
+ delegate_to: localhost
+
+- name: Remove a spine switch profile association
+ cisco.aci.aci_fabric_spine_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ profile: my_spine_profile
+ name: my_spine_switch_assoc
+ state: absent
+ delegate_to: localhost
+
+- name: Query a spine profile association
+ cisco.aci.aci_fabric_spine_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ profile: my_spine_profile
+ name: my_spine_switch_assoc
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all spine profiles
+ cisco.aci.aci_fabric_spine_switch_assoc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ profile=dict(type="str", aliases=["spine_profile", "spine_switch_profile"]),
+ name=dict(type="str", aliases=["association_name", "switch_association"]),
+ policy_group=dict(type="str"),
+ description=dict(type='str', aliases=['descr']),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["profile", "name"]],
+ ["state", "present", ["profile", "name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ profile = module.params.get("profile")
+ name = module.params.get("name")
+ policy_group = module.params.get("policy_group")
+ description = module.params.get('description')
+ state = module.params.get("state")
+ child_classes = ["fabricRsSpNodePGrp", "fabricNodeBlk"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricSpineP",
+ aci_rn="fabric/spprof-{0}".format(profile),
+ module_object=profile,
+ target_filter={"name": profile},
+ ),
+ subclass_1=dict(
+ aci_class="fabricSpineS",
+ aci_rn="spines-{0}-typ-range".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if policy_group:
+ tDn = "uni/fabric/funcprof/spnodepgrp-{0}".format(policy_group)
+ child_configs.append(dict(fabricRsSpNodePGrp=dict(attributes=dict(tDn=tDn))))
+ aci.payload(
+ aci_class="fabricSpineS",
+ class_config=dict(name=name, descr=description),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="fabricSpineS")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_block.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_block.py
new file mode 100644
index 00000000..c54543b4
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_block.py
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_fabric_switch_block
+short_description: Manage switch blocks (fabric:NodeBlk).
+description:
+- Manage fabric node blocks within switch associations (fabric:SpineS and
+ fabric:LeafS) contained within fabric switch profiles (fabric:SpineP and fabric:LeafP)
+options:
+ name:
+ description:
+ - Name of the block
+ type: str
+ aliases: [ block_name ]
+ switch_type:
+ description:
+ - Type of switch profile, leaf or spine
+ type: str
+ choices: [ leaf, spine ]
+ required: yes
+ profile:
+ description:
+ - Name of an existing fabric spine or leaf switch profile
+ type: str
+ aliases: [ profile_name, switch_profile ]
+ association:
+ description:
+ - Name of an existing switch association
+ type: str
+ aliases: [ association_name, switch_association ]
+ description:
+ description:
+ - Description of the Node Block
+ type: str
+ aliases: [ descr ]
+ from_node:
+ description:
+ - First Node ID of the block
+ type: int
+ aliases: [ from, from_ ]
+ to_node:
+ description:
+ - Last Node ID of the block
+ type: int
+ aliases: [ to, to_ ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fabricNodeBlk)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a spine switch association block
+ cisco.aci.aci_fabric_switch_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ switch_type: spine
+ profile: my_spine_profile
+ association: my_spine_switch_assoc
+ name: my_spine_block
+ from_node: 101
+ to_node: 101
+ state: present
+ delegate_to: localhost
+
+- name: Remove a spine switch profile association
+ cisco.aci.aci_fabric_switch_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ switch_type: spine
+ profile: my_spine_profile
+ association: my_spine_switch_assoc
+ name: my_spine_block
+ state: absent
+ delegate_to: localhost
+
+- name: Query a spine profile association
+ cisco.aci.aci_fabric_switch_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ switch_type: spine
+ profile: my_spine_profile
+ association: my_spine_switch_assoc
+ name: my_spine_block
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["block_name"]),
+ switch_type=dict(type="str", choices=["leaf", "spine"], required=True),
+ profile=dict(type="str", aliases=["profile_name", "switch_profile"]),
+ association=dict(type="str", aliases=["association_name", "switch_association"]),
+ description=dict(type="str", aliases=["descr"]),
+ from_node=dict(type="int", aliases=["from", "from_"]),
+ to_node=dict(type="int", aliases=["to", "to_"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["profile", "association", "name"]],
+ ["state", "present", ["profile", "association", "name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ profile = module.params.get("profile")
+ switch_type = module.params.get("switch_type")
+ association = module.params.get("association")
+ descr = module.params.get("descr")
+ from_node = module.params.get("from_node")
+ to_node = module.params.get("to_node")
+ state = module.params.get("state")
+
+ if switch_type == "spine":
+ aci_root_class = "fabricSpineP"
+ aci_root_rn = "fabric/spprof-{0}".format(profile)
+ aci_subclass1_class = "fabricSpineS"
+ aci_subclass1_rn = "spines-{0}-typ-range".format(association)
+ elif switch_type == "leaf":
+ aci_root_class = "fabricLeafP"
+ aci_root_rn = "fabric/leprof-{0}".format(profile)
+ aci_subclass1_class = "fabricLeafS"
+ aci_subclass1_rn = "leaves-{0}-typ-range".format(association)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_root_class,
+ aci_rn=aci_root_rn,
+ module_object=profile,
+ target_filter={"name": profile},
+ ),
+ subclass_1=dict(
+ aci_class=aci_subclass1_class,
+ aci_rn=aci_subclass1_rn,
+ module_object=association,
+ target_filter={"name": association},
+ ),
+ subclass_2=dict(
+ aci_class="fabricNodeBlk",
+ aci_rn="nodeblk-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricNodeBlk",
+ class_config=dict(name=name, descr=descr, from_=from_node, to_=to_node),
+ )
+
+ aci.get_diff(aci_class="fabricNodeBlk")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_policy_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_policy_group.py
new file mode 100644
index 00000000..31875bc2
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_fabric_switch_policy_group.py
@@ -0,0 +1,486 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_fabric_switch_policy_group
+short_description: Manage Fabric Switch Policy Group objects.
+description:
+- Manage Fabric Switch Policy Group (fabricLeNodePGrp and fabricSpNodePGrp) configuration on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the Leaf Policy Group.
+ type: str
+ aliases: [ 'policy_group', 'policy_group_name' ]
+ description:
+ description:
+ - Description for the Leaf Policy Group.
+ type: str
+ switch_type:
+ description:
+ - Whether this is a leaf or spine policy group
+ type: str
+ choices: [ leaf, spine ]
+ required: yes
+ monitoring_policy:
+ description:
+ - Monitoring Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'monitoring', 'fabricRsMonInstFabricPol' ]
+ tech_support_export_policy:
+ description:
+ - Tech Support Export Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'tech_support', 'tech_support_export', 'fabricRsNodeTechSupP']
+ core_export_policy:
+ description:
+ - Core Export Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'core', 'core_export', 'fabricRsNodeCoreP' ]
+ inventory_policy:
+ description:
+ - Inventory Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'inventory', 'fabricRsCallhomeInvPol' ]
+ power_redundancy_policy:
+ description:
+ - Power Redundancy Policy to atttach to this Policy Group
+ type: str
+ aliases: [ 'power_redundancy', 'fabricRsPsuInstPol' ]
+ twamp_server_policy:
+ description:
+ - TWAMP Server Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'twamp_server', 'fabricRsTwampServerPol' ]
+ twamp_responder_policy:
+ description:
+ - TWAMP Responder Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'twamp_responder', 'fabricRsTwampResponderPol' ]
+ node_control_policy:
+ description:
+ - Node Control Policy to attach to this Policy Group
+ type: str
+ aliases: [ 'node_control', 'fabricRsNodeCtrl' ]
+ analytics_cluster:
+ description:
+ - Name of the analytics cluster. Requires analytics_name to be present
+ type: str
+ analytics_name:
+ description:
+ - Name of the analytics policy. Requires analytics_cluster to be present
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fabricLeNodePGrp and fabricSpNodePGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new Fabric Leaf Policy Group
+ cisco.aci.aci_fabric_switch_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_fabric_leaf_policy_group
+ switch_type: leaf
+ monitoring_policy: my_monitor_policy
+ inventory_policy: my_inv_policy
+ state: present
+ delegate_to: localhost
+
+- name: Remove existing analytics and monitoring policy bindings from a Policy Group
+ cisco.aci.aci_fabric_switch_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_fabric_leaf_policy_group
+ switch_type: leaf
+ monitoring_policy: ""
+ analytics_cluster: ""
+ analytics_name: ""
+ state: present
+ delegate_to: localhost
+
+- name: Remove a Fabric Leaf Policy Group
+ cisco.aci.aci_fabric_switch_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_fabric_leaf_policy_group
+ switch_type: leaf
+ state: absent
+ delegate_to: localhost
+
+- name: Query a Fabric Leaf Policy Group
+ cisco.aci.aci_fabric_switch_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_fabric_leaf_policy_group
+ switch_type: leaf
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all Fabric Leaf Policy Groups
+ cisco.aci.aci_fabric_switch_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ switch_type: leaf
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ '''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+ACI_CLASS_MAPPING = dict(
+ spine={
+ 'class': 'fabricSpNodePGrp',
+ 'rn': 'spnodepgrp-',
+ },
+ leaf={
+ 'class': 'fabricLeNodePGrp',
+ 'rn': 'lenodepgrp-',
+ },
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type='str', aliases=['policy_group', 'policy_group_name']),
+ switch_type=dict(type='str', choices=['leaf', 'spine'], required=True),
+ monitoring_policy=dict(type='str',
+ aliases=['monitoring',
+ 'fabricRsMonInstFabricPol']),
+ tech_support_export_policy=dict(type='str',
+ aliases=['tech_support',
+ 'tech_support_export',
+ 'fabricRsNodeTechSupP']),
+ core_export_policy=dict(type='str', aliases=['core',
+ 'core_export',
+ 'fabricRsNodeCoreP']),
+ inventory_policy=dict(type='str', aliases=['inventory',
+ 'fabricRsCallhomeInvPol']),
+ power_redundancy_policy=dict(type='str',
+ aliases=['power_redundancy',
+ 'fabricRsPsuInstPol']),
+ twamp_server_policy=dict(type='str',
+ aliases=['twamp_server',
+ 'fabricRsTwampServerPol']),
+ twamp_responder_policy=dict(type='str',
+ aliases=['twamp_responder',
+ 'fabricRsTwampResponderPol']),
+ node_control_policy=dict(type='str', aliases=['node_control',
+ 'fabricRsNodeCtrl']),
+ analytics_cluster=dict(type='str'),
+ analytics_name=dict(type='str'),
+ description=dict(type='str'),
+ state=dict(type='str', default='present',
+ choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['name']],
+ ['state', 'present', ['name']],
+ ],
+ required_together=[
+ ('analytics_cluster', 'analytics_name'),
+ ],
+ )
+
+ name = module.params.get('name')
+ switch_type = module.params.get('switch_type')
+ description = module.params.get('description')
+ monitoring_policy = module.params.get('monitoring_policy')
+ tech_support_export_policy = module.params.get('tech_support_export_policy')
+ core_export_policy = module.params.get('core_export_policy')
+ inventory_policy = module.params.get('inventory_policy')
+ power_redundancy_policy = module.params.get('power_redundancy_policy')
+ twamp_server_policy = module.params.get('twamp_server_policy')
+ twamp_responder_policy = module.params.get('twamp_responder_policy')
+ node_control_policy = module.params.get('node_control_policy')
+ analytics_cluster = module.params.get('analytics_cluster')
+ analytics_name = module.params.get('analytics_name')
+ state = module.params.get('state')
+ child_classes = [
+ 'fabricRsMonInstFabricPol',
+ 'fabricRsNodeTechSupP',
+ 'fabricRsNodeCoreP',
+ 'fabricRsCallhomeInvPol',
+ 'fabricRsPsuInstPol',
+ 'fabricRsTwampServerPol',
+ 'fabricRsTwampResponderPol',
+ 'fabricRsNodeCtrl',
+ 'fabricRsNodeCfgSrv'
+ ]
+
+ aci_class = ACI_CLASS_MAPPING[switch_type]["class"]
+ aci_rn = ACI_CLASS_MAPPING[switch_type]["rn"]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_class,
+ aci_rn='fabric/funcprof/{0}{1}'.format(aci_rn, name),
+ module_object=name,
+ target_filter={'name': name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ child_configs = []
+
+ if monitoring_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsMonInstFabricPol=dict(
+ attributes=dict(
+ tnMonFabricPolName=monitoring_policy
+ )
+ )
+ )
+ )
+ if tech_support_export_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsNodeTechSupP=dict(
+ attributes=dict(
+ tnDbgexpTechSupPName=tech_support_export_policy
+ )
+ )
+ )
+ )
+ if core_export_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsNodeCoreP=dict(
+ attributes=dict(
+ tnDbgexpCorePName=core_export_policy
+ )
+ )
+ )
+ )
+ if inventory_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsCallhomeInvPol=dict(
+ attributes=dict(
+ tnCallhomeInvPName=inventory_policy
+ )
+ )
+ )
+ )
+ if power_redundancy_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsPsuInstPol=dict(
+ attributes=dict(
+ tnPsuInstPolName=power_redundancy_policy
+ )
+ )
+ )
+ )
+ if twamp_server_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsTwampServerPol=dict(
+ attributes=dict(
+ tnTwampServerPolName=twamp_server_policy
+ )
+ )
+ )
+ )
+ if twamp_responder_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsTwampResponderPol=dict(
+ attributes=dict(
+ tnTwampResponderPolName=twamp_responder_policy
+ )
+ )
+ )
+ )
+ if node_control_policy is not None:
+ child_configs.append(
+ dict(
+ fabricRsNodeCtrl=dict(
+ attributes=dict(
+ tnFabricNodeControlName=node_control_policy
+ )
+ )
+ )
+ )
+ if analytics_cluster and analytics_name:
+ analytics_tdn = ('uni/fabric/analytics/cluster-{0}/cfgsrv-{1}'.format(analytics_cluster, analytics_name))
+ child_configs.append(
+ dict(
+ fabricRsNodeCfgSrv=dict(
+ attributes=dict(
+ tDn=analytics_tdn
+ )
+ )
+ )
+ )
+
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(
+ name=name,
+ descr=description,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_filter.py b/ansible_collections/cisco/aci/plugins/modules/aci_filter.py
new file mode 100644
index 00000000..51daa432
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_filter.py
@@ -0,0 +1,282 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_filter
+short_description: Manages top level filter objects (vz:Filter)
+description:
+- Manages top level filter objects on Cisco ACI fabrics.
+- This modules does not manage filter entries, see M(cisco.aci.aci_filter_entry) for this functionality.
+options:
+ filter:
+ description:
+ - The name of the filter.
+ type: str
+ aliases: [ filter_name, name ]
+ description:
+ description:
+ - Description for the filter.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:Filter).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a new filter to a tenant
+ cisco.aci.aci_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ filter: web_filter
+ description: Filter for web protocols
+ tenant: production
+ state: present
+ delegate_to: localhost
+
+- name: Remove a filter for a tenant
+ cisco.aci.aci_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ filter: web_filter
+ tenant: production
+ state: absent
+ delegate_to: localhost
+
+- name: Query a filter of a tenant
+ cisco.aci.aci_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ filter: web_filter
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all filters for a tenant
+ cisco.aci.aci_filter:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ filter=dict(type="str", aliases=["name", "filter_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"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["filter", "tenant"]],
+ ["state", "present", ["filter", "tenant"]],
+ ],
+ )
+
+ filter_name = module.params.get("filter")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzFilter",
+ aci_rn="flt-{0}".format(filter_name),
+ module_object=filter_name,
+ target_filter={"name": filter_name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vzFilter",
+ class_config=dict(
+ name=filter_name,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="vzFilter")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py b/ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py
new file mode 100644
index 00000000..c029442b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_filter_entry.py
@@ -0,0 +1,434 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_filter_entry
+short_description: Manage filter entries (vz:Entry)
+description:
+- Manage filter entries for a filter on Cisco ACI fabrics.
+options:
+ arp_flag:
+ description:
+ - The arp flag to use when the ether_type is arp.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ arp_reply, arp_request, unspecified ]
+ description:
+ description:
+ - Description for the Filter Entry.
+ type: str
+ aliases: [ descr ]
+ dst_port:
+ description:
+ - Used to set both destination start and end ports to the same value when ip_protocol is tcp or udp.
+ - Accepted values are any valid TCP/UDP port range.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ dst_port_end:
+ description:
+ - Used to set the destination end port when ip_protocol is tcp or udp.
+ - Accepted values are any valid TCP/UDP port range.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ dst_port_start:
+ description:
+ - Used to set the destination start port when ip_protocol is tcp or udp.
+ - Accepted values are any valid TCP/UDP port range.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ entry:
+ description:
+ - Then name of the Filter Entry.
+ type: str
+ aliases: [ entry_name, filter_entry, name ]
+ ether_type:
+ description:
+ - The Ethernet type.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ arp, fcoe, ip, ipv4, ipv6, mac_security, mpls_ucast, trill, unspecified ]
+ filter:
+ description:
+ - The name of Filter that the entry should belong to.
+ type: str
+ aliases: [ filter_name ]
+ icmp_msg_type:
+ description:
+ - ICMPv4 message type; used when ip_protocol is icmp.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ dst_unreachable, echo, echo_reply, src_quench, time_exceeded, unspecified ]
+ icmp6_msg_type:
+ description:
+ - ICMPv6 message type; used when ip_protocol is icmpv6.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ dst_unreachable, echo_request, echo_reply, neighbor_advertisement, neighbor_solicitation, redirect, time_exceeded, unspecified ]
+ ip_protocol:
+ description:
+ - The IP Protocol type when ether_type is ip.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ eigrp, egp, icmp, icmpv6, igmp, igp, l2tp, ospfigp, pim, tcp, udp, unspecified ]
+ state:
+ description:
+ - present, absent, query
+ type: str
+ default: present
+ choices: [ absent, present, query ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ stateful:
+ description:
+ - Determines the statefulness of the filter entry.
+ type: bool
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(filter) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_filter) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_filter
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:Entry).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Create a filter entry
+ cisco.aci.aci_filter_entry:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ entry: https_allow
+ filter: web_filter
+ tenant: prod
+ ether_type: ip
+ ip_protocol: tcp
+ dst_port_start: 443
+ dst_port_end: 443
+ state: present
+ delegate_to: localhost
+
+- name: Delete a filter entry
+ cisco.aci.aci_filter_entry:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ entry: https_allow
+ filter: web_filter
+ tenant: prod
+ state: absent
+ delegate_to: localhost
+
+- name: Query all filter entries
+ cisco.aci.aci_filter_entry:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific filter entry
+ cisco.aci.aci_filter_entry:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ entry: https_allow
+ filter: web_filter
+ tenant: prod
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+VALID_ARP_FLAGS = ["arp_reply", "arp_request", "unspecified"]
+VALID_ETHER_TYPES = ["arp", "fcoe", "ip", "ipv4", "ipv6", "mac_security", "mpls_ucast", "trill", "unspecified"]
+VALID_ICMP_TYPES = ["dst_unreachable", "echo", "echo_reply", "src_quench", "time_exceeded", "unspecified"]
+VALID_ICMP6_TYPES = [
+ "dst_unreachable",
+ "echo_request",
+ "echo_reply",
+ "neighbor_advertisement",
+ "neighbor_solicitation",
+ "redirect",
+ "time_exceeded",
+ "unspecified",
+]
+VALID_IP_PROTOCOLS = ["eigrp", "egp", "icmp", "icmpv6", "igmp", "igp", "l2tp", "ospfigp", "pim", "tcp", "udp", "unspecified"]
+
+# mapping dicts are used to normalize the proposed data to what the APIC expects, which will keep diffs accurate
+ARP_FLAG_MAPPING = dict(arp_reply="reply", arp_request="req", unspecified=None)
+FILTER_PORT_MAPPING = {"443": "https", "25": "smtp", "80": "http", "20": "ftpData", "53": "dns", "110": "pop3", "554": "rtsp"}
+ICMP_MAPPING = {
+ "dst_unreachable": "dst-unreach",
+ "echo": "echo",
+ "echo_reply": "echo-rep",
+ "src_quench": "src-quench",
+ "time_exceeded": "time-exceeded",
+ "unspecified": "unspecified",
+ "echo-rep": "echo-rep",
+ "dst-unreach": "dst-unreach",
+}
+ICMP6_MAPPING = dict(
+ dst_unreachable="dst-unreach",
+ echo_request="echo-req",
+ echo_reply="echo-rep",
+ neighbor_advertisement="nbr-advert",
+ neighbor_solicitation="nbr-solicit",
+ redirect="redirect",
+ time_exceeded="time-exceeded",
+ unspecified="unspecified",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ arp_flag=dict(type="str", choices=VALID_ARP_FLAGS),
+ description=dict(type="str", aliases=["descr"]),
+ 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"]), # Not required for querying all objects
+ ether_type=dict(choices=VALID_ETHER_TYPES, type="str"),
+ 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"]), # Not required for querying all objects
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["entry", "filter", "tenant"]],
+ ["state", "present", ["entry", "filter", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ arp_flag = module.params.get("arp_flag")
+ if arp_flag is not None:
+ arp_flag = ARP_FLAG_MAPPING.get(arp_flag)
+ description = module.params.get("description")
+ dst_port = module.params.get("dst_port")
+ if FILTER_PORT_MAPPING.get(dst_port) is not None:
+ dst_port = FILTER_PORT_MAPPING.get(dst_port)
+ dst_end = module.params.get("dst_port_end")
+ if FILTER_PORT_MAPPING.get(dst_end) is not None:
+ dst_end = FILTER_PORT_MAPPING.get(dst_end)
+ dst_start = module.params.get("dst_port_start")
+ if FILTER_PORT_MAPPING.get(dst_start) is not None:
+ dst_start = FILTER_PORT_MAPPING.get(dst_start)
+ entry = module.params.get("entry")
+ ether_type = module.params.get("ether_type")
+ filter_name = module.params.get("filter")
+ icmp_msg_type = module.params.get("icmp_msg_type")
+ if icmp_msg_type is not None:
+ icmp_msg_type = ICMP_MAPPING.get(icmp_msg_type)
+ icmp6_msg_type = module.params.get("icmp6_msg_type")
+ if icmp6_msg_type is not None:
+ icmp6_msg_type = ICMP6_MAPPING.get(icmp6_msg_type)
+ ip_protocol = module.params.get("ip_protocol")
+ state = module.params.get("state")
+ stateful = aci.boolean(module.params.get("stateful"))
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ # validate that dst_port is not passed with dst_start or dst_end
+ if dst_port is not None and (dst_end is not None or dst_start is not None):
+ module.fail_json(msg="Parameter 'dst_port' cannot be used with 'dst_end' and 'dst_start'")
+ elif dst_port is not None:
+ dst_end = dst_port
+ dst_start = dst_port
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzFilter",
+ aci_rn="flt-{0}".format(filter_name),
+ module_object=filter_name,
+ target_filter={"name": filter_name},
+ ),
+ subclass_2=dict(
+ aci_class="vzEntry",
+ aci_rn="e-{0}".format(entry),
+ module_object=entry,
+ target_filter={"name": entry},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vzEntry",
+ class_config=dict(
+ arpOpc=arp_flag,
+ descr=description,
+ dFromPort=dst_start,
+ dToPort=dst_end,
+ etherT=ether_type,
+ icmpv4T=icmp_msg_type,
+ icmpv6T=icmp6_msg_type,
+ name=entry,
+ prot=ip_protocol,
+ stateful=stateful,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="vzEntry")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py
new file mode 100644
index 00000000..1a9db1a6
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group.py
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+
+# GNU General Public License v3.0+ (see LICENSE 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 = """
+---
+module: aci_firmware_group
+
+short_description: This module creates a firmware group
+
+
+description:
+ - This module creates a firmware group, so that you can apply firmware policy to nodes.
+options:
+ group:
+ description:
+ - This the name of the firmware group
+ type: str
+ firmwarepol:
+ description:
+ - This is the name of the firmware policy, which was create by aci_firmware_policy. It is important that
+ - you use the same name as the policy created with aci_firmware_policy
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ default: present
+ choices: [ absent, present, query ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+author:
+ - Steven Gerhart (@sgerhart)
+"""
+
+
+EXAMPLES = r"""
+- name: Create a firmware group
+ cisco.aci.aci_firmware_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: fmgroup
+ firmwarepol: fmpolicy1
+ state: present
+ delegate_to: localhost
+
+- name: Delete a firmware group
+ cisco.aci.aci_firmware_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: fmgroup
+ state: absent
+ delegate_to: localhost
+
+- name: Query all firmware groups
+ cisco.aci.aci_firmware_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific firmware group
+ cisco.aci.aci_firmware_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: fmgroup
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = """
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ group=dict(type="str"), # Not required for querying all objects
+ firmwarepol=dict(type="str"), # Not required for querying all objects
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["group"]],
+ ["state", "present", ["group", "firmwarepol"]],
+ ],
+ )
+
+ state = module.params.get("state")
+ group = module.params.get("group")
+ firmwarepol = module.params.get("firmwarepol")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="firmwareFwGrp",
+ aci_rn="fabric/fwgrp-{0}".format(group),
+ target_filter={"name": group},
+ module_object=group,
+ ),
+ child_classes=["firmwareRsFwgrpp"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="firmwareFwGrp",
+ class_config=dict(
+ name=group,
+ nameAlias=name_alias,
+ ),
+ child_configs=[
+ dict(
+ firmwareRsFwgrpp=dict(
+ attributes=dict(
+ tnFirmwareFwPName=firmwarepol,
+ ),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="firmwareFwGrp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py
new file mode 100644
index 00000000..b41a2601
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_group_node.py
@@ -0,0 +1,264 @@
+#!/usr/bin/python
+
+# GNU General Public License v3.0+ (see LICENSE 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 = """
+---
+module: aci_firmware_group_node
+
+short_description: This modules adds and remove nodes from the firmware group
+
+
+description:
+ - This module addes/deletes a node to the firmware group. This modules assigns 1 node at a time.
+
+options:
+ group:
+ description:
+ - This is the name of the firmware group
+ type: str
+ node:
+ description:
+ - The node to be added to the firmware group - the value equals the NodeID
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ default: present
+ choices: [ absent, present, query ]
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+ - Steven Gerhart (@sgerhart)
+"""
+
+EXAMPLES = r"""
+- name: Add a firmware group node
+ cisco.aci.aci_firmware_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: fmgroup
+ node: 1001
+ state: present
+ delegate_to: localhost
+
+- name: Remove a firmware group node
+ cisco.aci.aci_firmware_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: fmgroup
+ node: 1001
+ state: absent
+ delegate_to: localhost
+
+- name: Query all firmware groups nodes
+ cisco.aci.aci_firmware_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific firmware group node
+ cisco.aci.aci_firmware_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: fmgroup
+ node: 1001
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = """
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ group=dict(type="str"), # Not required for querying all objects
+ node=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["node", "group"]],
+ ["state", "present", ["node", "group"]],
+ ],
+ )
+
+ state = module.params.get("state")
+ group = module.params.get("group")
+ node = module.params.get("node")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="firmwareFwGrp",
+ aci_rn="fabric/fwgrp-{0}".format(group),
+ target_filter={"name": group},
+ module_object=group,
+ ),
+ subclass_1=dict(
+ aci_class="fabricNodeBlk",
+ aci_rn="nodeblk-blk{0}-{0}".format(node),
+ target_filter={"name": node},
+ module_object=node,
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricNodeBlk",
+ class_config=dict(
+ from_=node,
+ to_=node,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fabricNodeBlk")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py
new file mode 100644
index 00000000..d2895e2f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_policy.py
@@ -0,0 +1,250 @@
+#!/usr/bin/python
+
+
+# GNU General Public License v3.0+ (see LICENSE 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 = """
+---
+module: aci_firmware_policy
+
+short_description: This creates a firmware policy
+
+
+description:
+ - This module creates a firmware policy for firmware groups. The firmware policy is create first and then
+ - referenced by the firmware group. You will assign the firmware and specify if you want to ignore the compatibility
+ - check
+options:
+ name:
+ description:
+ - Name of the firmware policy
+ type: str
+ version:
+ description:
+ - The version of the firmware associated with this policy. This value is very import as well as constructing
+ - it correctly. The syntax for this field is n9000-xx.x. If you look at the firmware repository using the UI
+ - each version will have a "Full Version" column, this is the value you need to use. So, if the Full Version
+ - is 13.1(1i), the value for this field would be n9000-13.1(1i)
+ type: str
+ ignoreCompat:
+ description:
+ - Check if compatibility checks should be ignored
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [absent, present, query]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+author:
+ - Steven Gerhart (@sgerhart)
+"""
+
+# FIXME: Add more, better examples
+EXAMPLES = r"""
+ - name: firmware policy
+ cisco.aci.aci_firmware_policy:
+ host: "{{ inventory_hostname }}"
+ username: "{{ user }}"
+ password: "{{ pass }}"
+ validate_certs: no
+ name: test2FrmPol
+ version: n9000-13.2(1m)
+ ignoreCompat: False
+ state: present
+
+"""
+
+RETURN = """
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str"), # Not required for querying all objects
+ version=dict(type="str"),
+ ignoreCompat=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name", "version"]],
+ ],
+ )
+
+ state = module.params.get("state")
+ name = module.params.get("name")
+ version = module.params.get("version")
+ name_alias = module.params.get("name_alias")
+
+ if module.params.get("ignoreCompat"):
+ ignore = "yes"
+ else:
+ ignore = "no"
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="firmwareFwP",
+ aci_rn="fabric/fwpol-{0}".format(name),
+ target_filter={"name": name},
+ module_object=name,
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="firmwareFwP",
+ class_config=dict(
+ name=name,
+ version=version,
+ ignoreCompat=ignore,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="firmwareFwP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py
new file mode 100644
index 00000000..9058e60b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_firmware_source.py
@@ -0,0 +1,304 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com>
+# Copyright: (c) 2020, Cindy Zhao (cizhao) <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_firmware_source
+short_description: Manage firmware image sources (firmware:OSource)
+description:
+- Manage firmware image sources on Cisco ACI fabrics.
+options:
+ source:
+ description:
+ - The identifying name for the outside source of images, such as an HTTP or SCP server.
+ type: str
+ aliases: [ name, source_name ]
+ polling_interval:
+ description:
+ - Polling interval in minutes.
+ type: int
+ url_protocol:
+ description:
+ - The Firmware download protocol.
+ type: str
+ choices: [ http, local, scp, usbkey ]
+ default: scp
+ aliases: [ url_proto ]
+ url:
+ description:
+ The firmware URL for the image(s) on the source.
+ type: str
+ url_password:
+ description:
+ The Firmware password or key string.
+ type: str
+ url_username:
+ description:
+ The username for the source.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(firmware:OSource).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add firmware source
+ cisco.aci.aci_firmware_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ source: 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
+ delegate_to: localhost
+
+- name: Remove firmware source
+ cisco.aci.aci_firmware_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ source: aci-msft-pkg-3.1.1i.zip
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific firmware source
+ cisco.aci.aci_firmware_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ source: aci-msft-pkg-3.1.1i.zip
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all firmware sources
+ cisco.aci.aci_firmware_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ source=dict(type="str", aliases=["name", "source_name"]), # Not required for querying all objects
+ polling_interval=dict(type="int"),
+ url=dict(type="str"),
+ url_username=dict(type="str"),
+ url_password=dict(type="str", no_log=True),
+ url_protocol=dict(type="str", default="scp", choices=["http", "local", "scp", "usbkey"], aliases=["url_proto"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["source"]],
+ ["state", "present", ["url_protocol", "source", "url"]],
+ ],
+ )
+
+ polling_interval = module.params.get("polling_interval")
+ url_protocol = module.params.get("url_protocol")
+ state = module.params.get("state")
+ source = module.params.get("source")
+ url = module.params.get("url")
+ url_password = module.params.get("url_password")
+ url_username = module.params.get("url_username")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricInst",
+ aci_rn="fabric",
+ module_object="fabric",
+ target_filter={"name": "fabric"},
+ ),
+ subclass_1=dict(
+ aci_class="firmwareRepoP",
+ aci_rn="fwrepop",
+ module_object="fwrepop",
+ target_filter={"name": "fwrepop"},
+ ),
+ subclass_2=dict(
+ aci_class="firmwareOSource",
+ aci_rn="osrc-{0}".format(source),
+ module_object=source,
+ target_filter={"name": source},
+ ),
+ config_only=False,
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="firmwareOSource",
+ class_config=dict(
+ name=source,
+ url=url,
+ password=url_password,
+ pollingInterval=polling_interval,
+ proto=url_protocol,
+ user=url_username,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="firmwareOSource")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_blacklist.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_blacklist.py
new file mode 100644
index 00000000..a883e5a7
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_blacklist.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_blacklist
+short_description: Enabling or Disabling physical interfaces.
+description:
+- Enables or Disables physical interfaces on Cisco ACI fabrics.
+options:
+ pod_id:
+ description:
+ - The pod number.
+ - C(pod_id) is usually an integer below C(12)
+ type: int
+ aliases: [ pod, pod_number ]
+ node_id:
+ description:
+ - The switch ID that the C(interface) belongs to.
+ - The C(node_id) value is usually something like '101'.
+ type: int
+ aliases: [ leaf, spine, node ]
+ interface:
+ description:
+ - The name of the C(interface) that is targeted.
+ - Usually an interface name with the following format C(1/7).
+ type: str
+ fex_id:
+ description:
+ - The fex ID that the C(interface) belongs to.
+ - The C(fex_id) value is usually something like '123'.
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+- Akini Ross (@akinross)
+"""
+
+EXAMPLES = r"""
+- name: Disable Interface
+ cisco.aci.aci_interface_blacklist:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ interface: 1/49
+ state: present
+ delegate_to: localhost
+
+- name: Enable Interface
+ cisco.aci.aci_interface_blacklist:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ interface: 1/49
+ state: absent
+ delegate_to: localhost
+
+- name: Disable Interface on Fex
+ cisco.aci.aci_interface_blacklist:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ fex_id: 123
+ interface: 1/49
+ state: present
+ delegate_to: localhost
+
+- name: Enable Interface on Fex
+ cisco.aci.aci_interface_blacklist:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ fex_id: 123
+ interface: 1/49
+ state: absent
+ delegate_to: localhost
+
+- name: Query Interface
+ cisco.aci.aci_interface_blacklist:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ interface: 1/49
+ state: query
+ delegate_to: localhost
+
+- name: Query All Interfaces
+ cisco.aci.aci_interface_blacklist:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: '?rsp-prop-include=config-only'
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ pod_id=dict(type="int", aliases=["pod", "pod_number"]),
+ node_id=dict(type="int", aliases=["leaf", "spine", "node"]),
+ fex_id=dict(type="int"),
+ interface=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["pod_id", "node_id", "interface"]],
+ ["state", "present", ["pod_id", "node_id", "interface"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ interface = module.params.get("interface")
+ fex_id = module.params.get("fex_id")
+ state = module.params.get("state")
+
+ root_module_object = None
+ subclass_1_module_object = None
+ tdn = None
+ rn = None
+
+ if pod_id and node_id and interface:
+ root_module_object = "fabric"
+ subclass_1_module_object = "outofsvc"
+ if fex_id:
+ tdn = "topology/pod-{0}/paths-{1}/extpaths-{2}/pathep-[eth{3}]".format(pod_id, node_id, fex_id, interface)
+ else:
+ tdn = "topology/pod-{0}/paths-{1}/pathep-[eth{2}]".format(pod_id, node_id, interface)
+ rn = "rsoosPath-[{0}]".format(tdn)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricInst",
+ aci_rn="fabric",
+ module_object=root_module_object,
+ target_filter={"name": "fabric"},
+ ),
+ subclass_1=dict(
+ aci_class="fabricOOServicePol",
+ aci_rn="outofsvc",
+ module_object=subclass_1_module_object,
+ target_filter={"name": "default"},
+ ),
+ subclass_2=dict(
+ aci_class="fabricRsOosPath",
+ aci_rn=rn,
+ target_filter={"tDN": tdn},
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricRsOosPath",
+ class_config=dict(
+ lc="blacklist",
+ ),
+ )
+
+ aci.get_diff(aci_class="fabricRsOosPath")
+
+ aci.post_config()
+
+ elif state == "absent":
+
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_description.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_description.py
new file mode 100644
index 00000000..e45de20d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_description.py
@@ -0,0 +1,347 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_description
+short_description: Setting and removing description on physical interfaces.
+description:
+- Setting and removing description on physical interfaces on Cisco ACI fabrics.
+options:
+ pod_id:
+ description:
+ - The pod number.
+ - C(pod_id) is usually an integer below C(12)
+ type: int
+ aliases: [ pod, pod_number ]
+ node_id:
+ description:
+ - The switch ID that the C(interface) belongs to.
+ - The C(node_id) value is usually something like '101'.
+ type: int
+ aliases: [ leaf, spine, node ]
+ node_type:
+ description:
+ - The type of node the C(interface) is configured on.
+ type: str
+ choices: [ leaf, spine ]
+ interface:
+ description:
+ - The name of the C(interface) that is targeted.
+ - Usually an interface name with the following format C(1/7).
+ type: str
+ fex_id:
+ description:
+ - The fex ID that the C(interface) belongs to.
+ - The C(fex_id) value is usually something like '123'.
+ type: int
+ description:
+ description:
+ - The C(description) that should be attached to the C(interface).
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+author:
+- Akini Ross (@akinross)
+"""
+
+EXAMPLES = r"""
+- name: Set Interface Description
+ cisco.aci.aci_interface_description:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ node_type: leaf
+ interface: 1/49
+ description: foobar
+ state: present
+ delegate_to: localhost
+
+- name: Remove Interface Description
+ cisco.aci.aci_interface_description:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ node_type: leaf
+ interface: 1/49
+ description: foobar
+ state: absent
+ delegate_to: localhost
+
+- name: Set Interface Description on Fex
+ cisco.aci.aci_interface_description:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ fex_id: 123
+ interface: 1/49
+ description: foobar
+ state: present
+ delegate_to: localhost
+
+- name: Remove Interface Description on Fex
+ cisco.aci.aci_interface_description:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ fex_id: 123
+ interface: 1/49
+ description: foobar
+ state: absent
+ delegate_to: localhost
+
+- name: Query Interface
+ cisco.aci.aci_interface_description:
+ host: "{{ inventory_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: no
+ pod_id: 1
+ node_id: 105
+ node_type: leaf
+ interface: 1/49
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: '?rsp-prop-include=config-only'
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ pod_id=dict(type="int", aliases=["pod", "pod_number"]),
+ node_id=dict(type="int", aliases=["leaf", "spine", "node"]),
+ fex_id=dict(type="int"),
+ node_type=dict(type="str", choices=["leaf", "spine"]),
+ interface=dict(type="str"),
+ description=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_one_of=[
+ ("node_type", "fex_id"),
+ ],
+ required_if=[
+ ["state", "absent", ["pod_id", "node_id", "interface"]],
+ ["state", "present", ["pod_id", "node_id", "interface", "description"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ interface = module.params.get("interface")
+ description = module.params.get("description")
+ fex_id = module.params.get("fex_id")
+ node_type = module.params.get("node_type")
+ state = module.params.get("state")
+
+ class_name = "infraHPathS"
+ children = ["infraRsHPathAtt"]
+ if node_type == "spine":
+ class_name = "infraSHPathS"
+ children = ["infraRsSHPathAtt"]
+ rn = None
+
+ if node_id and interface:
+ if fex_id:
+ rn = "hpaths-{0}_eth{1}_{2}".format(node_id, fex_id, interface.replace("/", "_"))
+ child_configs = [
+ dict(
+ infraRsHPathAtt=dict(
+ attributes=dict(tDn="topology/pod-{0}/paths-{1}/extpaths-{2}/pathep-[eth{3}]".format(pod_id, node_id, fex_id, interface))
+ )
+ ),
+ ]
+ elif node_type == "spine":
+ rn = "shpaths-{0}_eth{1}".format(node_id, interface.replace("/", "_"))
+ child_configs = [
+ dict(infraRsSHPathAtt=dict(attributes=dict(tDn="topology/pod-{0}/paths-{1}/pathep-[eth{2}]".format(pod_id, node_id, interface)))),
+ ]
+ elif node_type == "leaf":
+ rn = "hpaths-{0}_eth{1}".format(node_id, interface.replace("/", "_"))
+ child_configs = [
+ dict(infraRsHPathAtt=dict(attributes=dict(tDn="topology/pod-{0}/paths-{1}/pathep-[eth{2}]".format(pod_id, node_id, interface)))),
+ ]
+
+ dn = None
+ interface_name = None
+ infra_mo = None
+ if rn:
+ dn = "uni/infra/{0}".format(rn)
+ interface_name = rn.split("-")[1]
+ infra_mo = "infra"
+
+ aci.construct_url(
+ root_class=dict(aci_class="infraInfra", aci_rn="infra", module_object=infra_mo, target_filter=dict(name="infra")),
+ subclass_1=dict(aci_class=class_name, aci_rn=rn, module_object=dn, target_filter=dict(name=interface_name)),
+ child_classes=children,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=class_name,
+ class_config=dict(
+ descr=description,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class=class_name)
+
+ aci.post_config()
+
+ elif state == "absent":
+
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py
new file mode 100644
index 00000000..f610967d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_cdp.py
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+
+# Copyright: (c) 2019, Tim Knipper <tim.knipper@gmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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_interface_policy_cdp
+short_description: Manage CDP interface policies (cdp:IfPol)
+description:
+- Manage CDP interface policies on Cisco ACI fabrics.
+options:
+ cdp_policy:
+ description:
+ - The CDP interface policy name.
+ type: str
+ aliases: [ cdp_interface, name ]
+ description:
+ description:
+ - The description for the CDP interface policy name.
+ type: str
+ aliases: [ descr ]
+ admin_state:
+ description:
+ - Enable or Disable CDP state.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(cdp:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Knipper (@tknipper11)
+"""
+
+EXAMPLES = r"""
+- name: Create CDP Interface Policy to enable CDP
+ cisco.aci.aci_interface_policy_cdp:
+ name: Ansible_CDP_Interface_Policy
+ host: apic.example.com
+ username: admin
+ password: adminpass
+ admin_state: true
+ state: present
+
+- name: Create CDP Interface Policy to disable CDP
+ cisco.aci.aci_interface_policy_cdp:
+ name: Ansible_CDP_Interface_Policy
+ host: apic.example.com
+ username: admin
+ password: adminpass
+ admin_state: false
+ state: present
+
+- name: Remove CDP Interface Policy
+ cisco.aci.aci_interface_policy_cdp:
+ name: Ansible_CDP_Interface_Policy
+ host: apic.example.com
+ username: admin
+ password: adminpass
+ output_level: debug
+ state: absent
+
+- name: Query CDP Policy
+ cisco.aci.aci_interface_policy_cdp:
+ host: apic.example.com
+ username: admin
+ password: adminpass
+ state: query
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "cdpIfPol": {
+ "attributes": {
+ "adminSt": "disabled",
+ "annotation": "",
+ "descr": "Ansible Created CDP Test Policy",
+ "dn": "uni/infra/cdpIfP-Ansible_CDP_Test_Policy",
+ "name": "Ansible_CDP_Test_Policy",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ cdp_policy=dict(type="str", required=False, aliases=["cdp_interface", "name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ admin_state=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["cdp_policy"]],
+ ["state", "present", ["cdp_policy"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ cdp_policy = module.params.get("cdp_policy")
+ description = module.params.get("description")
+ admin_state = aci.boolean(module.params.get("admin_state"), "enabled", "disabled")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="cdpIfPol",
+ aci_rn="infra/cdpIfP-{0}".format(cdp_policy),
+ module_object=cdp_policy,
+ target_filter={"name": cdp_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="cdpIfPol",
+ class_config=dict(
+ name=cdp_policy,
+ descr=description,
+ adminSt=admin_state,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="cdpIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py
new file mode 100644
index 00000000..eaf8e124
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_fc.py
@@ -0,0 +1,268 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_fc
+short_description: Manage Fibre Channel interface policies (fc:IfPol)
+description:
+- Manage ACI Fiber Channel interface policies on Cisco ACI fabrics.
+options:
+ fc_policy:
+ description:
+ - The name of the Fiber Channel interface policy.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description of the Fiber Channel interface policy.
+ type: str
+ aliases: [ descr ]
+ port_mode:
+ description:
+ - The Port Mode to use.
+ - The APIC defaults to C(f) when unset during creation.
+ type: str
+ choices: [ f, np ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fc:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a Fibre Channel interface policy
+ cisco.aci.aci_interface_policy_fc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ fc_policy: fcpolicy1
+ state: present
+ delegate_to: localhost
+
+- name: Delete a Fibre Channel interface policy
+ cisco.aci.aci_interface_policy_fc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ fc_policy: fcpolicy1
+ state: absent
+ delegate_to: localhost
+
+- name: Query all Fibre Channel interface policies
+ cisco.aci.aci_interface_policy_fc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific Fibre Channel interface policy
+ cisco.aci.aci_interface_policy_fc:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ fc_policy: fcpolicy1
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ fc_policy=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ port_mode=dict(type="str", choices=["f", "np"]), # No default provided on purpose
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["fc_policy"]],
+ ["state", "present", ["fc_policy"]],
+ ],
+ )
+
+ fc_policy = module.params.get("fc_policy")
+ port_mode = module.params.get("port_mode")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fcIfPol",
+ aci_rn="infra/fcIfPol-{0}".format(fc_policy),
+ module_object=fc_policy,
+ target_filter={"name": fc_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fcIfPol",
+ class_config=dict(
+ name=fc_policy,
+ descr=description,
+ portMode=port_mode,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fcIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py
new file mode 100644
index 00000000..1707ca50
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_l2.py
@@ -0,0 +1,296 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_l2
+short_description: Manage Layer 2 interface policies (l2:IfPol)
+description:
+- Manage Layer 2 interface policies on Cisco ACI fabrics.
+options:
+ l2_policy:
+ description:
+ - The name of the Layer 2 interface policy.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description of the Layer 2 interface policy.
+ type: str
+ aliases: [ descr ]
+ qinq:
+ description:
+ - Determines if QinQ is disabled or if the port should be considered a core or edge port.
+ - The APIC defaults to C(disabled) when unset during creation.
+ type: str
+ choices: [ core, disabled, edge ]
+ vepa:
+ description:
+ - Determines if Virtual Ethernet Port Aggregator is disabled or enabled.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ vlan_scope:
+ description:
+ - The scope of the VLAN.
+ - The APIC defaults to C(global) when unset during creation.
+ type: str
+ choices: [ global, portlocal ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l2:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a Layer2 interface policy
+ cisco.aci.aci_interface_policy_l2:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ l2_policy: PORT_LOCAL
+ vlan_scope: portlocal
+ state: present
+ delegate_to: localhost
+
+- name: Delete a Layer2 interface policy
+ cisco.aci.aci_interface_policy_l2:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ l2_policy: PORT_LOCAL
+ state: absent
+ delegate_to: localhost
+
+- name: Query all Layer2 interface policies
+ cisco.aci.aci_interface_policy_l2:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific Layer2 interface policy
+ cisco.aci.aci_interface_policy_l2:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ l2_policy: PORT_LOCAL
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+# Mapping dicts are used to normalize the proposed data to what the APIC expects, which will keep diffs accurate
+QINQ_MAPPING = dict(
+ core="corePort",
+ disabled="disabled",
+ edge="edgePort",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ l2_policy=dict(type="str", aliases=["name"]), # Not required for querying all policies
+ description=dict(type="str", aliases=["descr"]),
+ vlan_scope=dict(type="str", choices=["global", "portlocal"]), # No default provided on purpose
+ qinq=dict(type="str", choices=["core", "disabled", "edge"]),
+ vepa=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["l2_policy"]],
+ ["state", "present", ["l2_policy"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ l2_policy = module.params.get("l2_policy")
+ vlan_scope = module.params.get("vlan_scope")
+ qinq = module.params.get("qinq")
+ if qinq is not None:
+ qinq = QINQ_MAPPING.get(qinq)
+ vepa = aci.boolean(module.params.get("vepa"), "enabled", "disabled")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="l2IfPol",
+ aci_rn="infra/l2IfP-{0}".format(l2_policy),
+ module_object=l2_policy,
+ target_filter={"name": l2_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l2IfPol",
+ class_config=dict(
+ name=l2_policy,
+ descr=description,
+ vlanScope=vlan_scope,
+ qinq=qinq,
+ vepa=vepa,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="l2IfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py
new file mode 100644
index 00000000..e0f26e7e
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_breakout_port_group.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Cindy Zhao <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_leaf_breakout_port_group
+short_description: Manage fabric interface policy leaf breakout port group (infra:BrkoutPortGrp)
+description:
+- Manage fabric interface policy leaf breakout port group on Cisco ACI fabrics.
+options:
+ breakout_port_group:
+ description:
+ - Name of the leaf breakout port group to be added/deleted.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - Description for the leaf breakout port group to be created.
+ type: str
+ aliases: [ descr ]
+ breakout_map:
+ description:
+ - The mapping of breakout port.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:BrkoutPortGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Cindy Zhao (@cizhao)
+"""
+
+EXAMPLES = r"""
+- name: Create a Leaf Breakout Port Group
+ cisco.aci.aci_interface_policy_leaf_breakout_port_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ breakout_port_group: BreakoutPortName
+ breakout_map: 10g-4x
+ state: present
+ delegate_to: localhost
+
+- name: Query all Leaf Breakout Port Groups of type link
+ cisco.aci.aci_interface_policy_leaf_breakout_port_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific Leaf Breakout Port Group
+ cisco.aci.aci_interface_policy_leaf_breakout_port_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ breakout_port_group: BreakoutPortName
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete an Leaf Breakout Port Group
+ cisco.aci.aci_interface_policy_leaf_breakout_port_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ breakout_port_group: BreakoutPortName
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ breakout_port_group=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ breakout_map=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["breakout_port_group"]],
+ ["state", "present", ["breakout_port_group"]],
+ ],
+ )
+
+ breakout_port_group = module.params.get("breakout_port_group")
+ description = module.params.get("description")
+ breakout_map = module.params.get("breakout_map")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraBrkoutPortGrp",
+ aci_rn="infra/funcprof/brkoutportgrp-{0}".format(breakout_port_group),
+ module_object=breakout_port_group,
+ target_filter={"name": breakout_port_group},
+ ),
+ child_classes=[],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraBrkoutPortGrp",
+ class_config=dict(
+ name=breakout_port_group,
+ descr=description,
+ brkoutMap=breakout_map,
+ ),
+ )
+
+ aci.get_diff(aci_class="infraBrkoutPortGrp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py
new file mode 100644
index 00000000..0eb070e2
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_policy_group.py
@@ -0,0 +1,587 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_leaf_policy_group
+short_description: Manage fabric interface policy leaf policy groups (infra:AccBndlGrp, infra:AccPortGrp)
+description:
+- Manage fabric interface policy leaf policy groups on Cisco ACI fabrics.
+options:
+ policy_group:
+ description:
+ - Name of the leaf policy group to be added/deleted.
+ type: str
+ aliases: [ name, policy_group_name ]
+ description:
+ description:
+ - Description for the leaf policy group to be created.
+ type: str
+ aliases: [ descr ]
+ 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)
+ type: str
+ required: yes
+ choices: [ leaf, link, node ]
+ aliases: [ lag_type_name ]
+ link_level_policy:
+ description:
+ - Choice of link_level_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ link_level_policy_name ]
+ cdp_policy:
+ description:
+ - Choice of cdp_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ cdp_policy_name ]
+ mcp_policy:
+ description:
+ - Choice of mcp_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ mcp_policy_name ]
+ lldp_policy:
+ description:
+ - Choice of lldp_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ lldp_policy_name ]
+ stp_interface_policy:
+ description:
+ - Choice of stp_interface_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ stp_interface_policy_name ]
+ egress_data_plane_policing_policy:
+ description:
+ - Choice of egress_data_plane_policing_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ egress_data_plane_policing_policy_name ]
+ ingress_data_plane_policing_policy:
+ description:
+ - Choice of ingress_data_plane_policing_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ ingress_data_plane_policing_policy_name ]
+ priority_flow_control_policy:
+ description:
+ - Choice of priority_flow_control_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ priority_flow_control_policy_name ]
+ fibre_channel_interface_policy:
+ description:
+ - Choice of fibre_channel_interface_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ fibre_channel_interface_policy_name ]
+ slow_drain_policy:
+ description:
+ - Choice of slow_drain_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ slow_drain_policy_name ]
+ port_channel_policy:
+ description:
+ - Choice of port_channel_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ port_channel_policy_name ]
+ monitoring_policy:
+ description:
+ - Choice of monitoring_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ monitoring_policy_name ]
+ storm_control_interface_policy:
+ description:
+ - Choice of storm_control_interface_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ storm_control_interface_policy_name ]
+ l2_interface_policy:
+ description:
+ - Choice of l2_interface_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ l2_interface_policy_name ]
+ port_security_policy:
+ description:
+ - Choice of port_security_policy to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ port_security_policy_name ]
+ aep:
+ description:
+ - Choice of attached_entity_profile (AEP) to be used as part of the leaf policy group to be created.
+ type: str
+ aliases: [ aep_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+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.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:AccBndlGrp) and B(infra:AccPortGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+"""
+
+EXAMPLES = r"""
+- name: Create a Port Channel (PC) Interface Policy Group
+ cisco.aci.aci_interface_policy_leaf_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lag_type: link
+ policy_group: policygroupname
+ description: policygroupname description
+ link_level_policy: whateverlinklevelpolicy
+ cdp_policy: whatevercdppolicy
+ lldp_policy: whateverlldppolicy
+ port_channel_policy: whateverlacppolicy
+ state: present
+ delegate_to: localhost
+
+- name: Create a Virtual Port Channel (VPC) Interface Policy Group
+ cisco.aci.aci_interface_policy_leaf_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lag_type: node
+ policy_group: policygroupname
+ link_level_policy: whateverlinklevelpolicy
+ cdp_policy: whatevercdppolicy
+ lldp_policy: whateverlldppolicy
+ port_channel_policy: whateverlacppolicy
+ state: present
+ delegate_to: localhost
+
+- name: Create a Leaf Access Port Policy Group
+ cisco.aci.aci_interface_policy_leaf_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lag_type: leaf
+ policy_group: policygroupname
+ link_level_policy: whateverlinklevelpolicy
+ cdp_policy: whatevercdppolicy
+ lldp_policy: whateverlldppolicy
+ state: present
+ delegate_to: localhost
+
+- name: Query all Leaf Access Port Policy Groups of type link
+ cisco.aci.aci_interface_policy_leaf_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lag_type: link
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific Lead Access Port Policy Group
+ cisco.aci.aci_interface_policy_leaf_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lag_type: leaf
+ policy_group: policygroupname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete an Interface policy Leaf Policy Group
+ cisco.aci.aci_interface_policy_leaf_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lag_type: leaf
+ policy_group: policygroupname
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ # 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", required=True, aliases=["lag_type_name"], choices=["leaf", "link", "node"]),
+ policy_group=dict(type="str", aliases=["name", "policy_group_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ 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"]),
+ lldp_policy=dict(type="str", aliases=["lldp_policy_name"]),
+ stp_interface_policy=dict(type="str", aliases=["stp_interface_policy_name"]),
+ egress_data_plane_policing_policy=dict(type="str", aliases=["egress_data_plane_policing_policy_name"]),
+ ingress_data_plane_policing_policy=dict(type="str", aliases=["ingress_data_plane_policing_policy_name"]),
+ priority_flow_control_policy=dict(type="str", aliases=["priority_flow_control_policy_name"]),
+ fibre_channel_interface_policy=dict(type="str", aliases=["fibre_channel_interface_policy_name"]),
+ slow_drain_policy=dict(type="str", aliases=["slow_drain_policy_name"]),
+ port_channel_policy=dict(type="str", aliases=["port_channel_policy_name"]),
+ monitoring_policy=dict(type="str", aliases=["monitoring_policy_name"]),
+ storm_control_interface_policy=dict(type="str", aliases=["storm_control_interface_policy_name"]),
+ 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"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["policy_group"]],
+ ["state", "present", ["policy_group"]],
+ ],
+ )
+
+ policy_group = module.params.get("policy_group")
+ description = module.params.get("description")
+ lag_type = module.params.get("lag_type")
+ link_level_policy = module.params.get("link_level_policy")
+ cdp_policy = module.params.get("cdp_policy")
+ mcp_policy = module.params.get("mcp_policy")
+ lldp_policy = module.params.get("lldp_policy")
+ stp_interface_policy = module.params.get("stp_interface_policy")
+ egress_data_plane_policing_policy = module.params.get("egress_data_plane_policing_policy")
+ ingress_data_plane_policing_policy = module.params.get("ingress_data_plane_policing_policy")
+ priority_flow_control_policy = module.params.get("priority_flow_control_policy")
+ fibre_channel_interface_policy = module.params.get("fibre_channel_interface_policy")
+ slow_drain_policy = module.params.get("slow_drain_policy")
+ port_channel_policy = module.params.get("port_channel_policy")
+ monitoring_policy = module.params.get("monitoring_policy")
+ storm_control_interface_policy = module.params.get("storm_control_interface_policy")
+ l2_interface_policy = module.params.get("l2_interface_policy")
+ port_security_policy = module.params.get("port_security_policy")
+ aep = module.params.get("aep")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ if lag_type == "leaf" and port_channel_policy is not None:
+ aci.fail_json(
+ "port_channel_policy is not a valid parameter for leaf\
+ (leaf access port policy group), if used\
+ assign null to it (port_channel_policy: null)."
+ )
+
+ if lag_type == "leaf":
+ aci_class_name = "infraAccPortGrp"
+ dn_name = "accportgrp"
+ class_config_dict = dict(
+ name=policy_group,
+ descr=description,
+ nameAlias=name_alias,
+ )
+ # Reset for target_filter
+ lag_type = None
+ elif lag_type in ("link", "node"):
+ aci_class_name = "infraAccBndlGrp"
+ dn_name = "accbundle"
+ class_config_dict = dict(
+ name=policy_group,
+ descr=description,
+ lagT=lag_type,
+ nameAlias=name_alias,
+ )
+
+ child_configs = [
+ dict(
+ infraRsCdpIfPol=dict(
+ attributes=dict(
+ tnCdpIfPolName=cdp_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsFcIfPol=dict(
+ attributes=dict(
+ tnFcIfPolName=fibre_channel_interface_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsHIfPol=dict(
+ attributes=dict(
+ tnFabricHIfPolName=link_level_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsL2IfPol=dict(
+ attributes=dict(
+ tnL2IfPolName=l2_interface_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsL2PortSecurityPol=dict(
+ attributes=dict(
+ tnL2PortSecurityPolName=port_security_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsLacpPol=dict(
+ attributes=dict(
+ tnLacpLagPolName=port_channel_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsLldpIfPol=dict(
+ attributes=dict(
+ tnLldpIfPolName=lldp_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsMcpIfPol=dict(
+ attributes=dict(
+ tnMcpIfPolName=mcp_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsMonIfInfraPol=dict(
+ attributes=dict(
+ tnMonInfraPolName=monitoring_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsQosEgressDppIfPol=dict(
+ attributes=dict(
+ tnQosDppPolName=egress_data_plane_policing_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsQosIngressDppIfPol=dict(
+ attributes=dict(
+ tnQosDppPolName=ingress_data_plane_policing_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsQosPfcIfPol=dict(
+ attributes=dict(
+ tnQosPfcIfPolName=priority_flow_control_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsQosSdIfPol=dict(
+ attributes=dict(
+ tnQosSdIfPolName=slow_drain_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsStormctrlIfPol=dict(
+ attributes=dict(
+ tnStormctrlIfPolName=storm_control_interface_policy,
+ ),
+ ),
+ ),
+ dict(
+ infraRsStpIfPol=dict(
+ attributes=dict(
+ tnStpIfPolName=stp_interface_policy,
+ ),
+ ),
+ ),
+ ]
+
+ # Add infraRsattEntP binding only when aep was defined
+ if aep is not None:
+ child_configs.append(
+ dict(
+ infraRsAttEntP=dict(
+ attributes=dict(
+ tDn="uni/infra/attentp-{0}".format(aep),
+ ),
+ ),
+ )
+ )
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_class_name,
+ aci_rn="infra/funcprof/{0}-{1}".format(dn_name, policy_group),
+ module_object=policy_group,
+ target_filter={"name": policy_group, "lagT": lag_type},
+ ),
+ child_classes=[
+ "infraRsAttEntP",
+ "infraRsCdpIfPol",
+ "infraRsFcIfPol",
+ "infraRsHIfPol",
+ "infraRsL2IfPol",
+ "infraRsL2PortSecurityPol",
+ "infraRsLacpPol",
+ "infraRsLldpIfPol",
+ "infraRsMcpIfPol",
+ "infraRsMonIfInfraPol",
+ "infraRsQosEgressDppIfPol",
+ "infraRsQosIngressDppIfPol",
+ "infraRsQosPfcIfPol",
+ "infraRsQosSdIfPol",
+ "infraRsStormctrlIfPol",
+ "infraRsStpIfPol",
+ ],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=aci_class_name,
+ class_config=class_config_dict,
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class=aci_class_name)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py
new file mode 100644
index 00000000..39c3e1ec
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile.py
@@ -0,0 +1,316 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_leaf_profile
+short_description: Manage fabric interface policy leaf profiles (infra:AccPortP)
+description:
+- Manage fabric interface policy leaf profiles on Cisco ACI fabrics.
+options:
+ interface_profile:
+ description:
+ - The name of the Fabric access policy leaf interface profile.
+ type: str
+ aliases: [ name, leaf_interface_profile_name, leaf_interface_profile, interface_profile_name ]
+ description:
+ description:
+ - Description for the Fabric access policy leaf interface profile.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ type:
+ description:
+ - The type of profile to be created.
+ type: str
+ choices: [ fex, leaf ]
+ default: leaf
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:AccPortP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new leaf_interface_profile
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ description: leafintprfname description
+ state: present
+ delegate_to: localhost
+
+- name: Add a new leaf_interface_profile of type fex
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname_fex
+ type: fex
+ description: leafintprfname description
+ state: present
+ delegate_to: localhost
+
+- name: Remove a leaf_interface_profile
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ state: absent
+ delegate_to: localhost
+
+- name: Remove a leaf_interface_profile of type fex
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname_fex
+ type: fex
+ state: absent
+ delegate_to: localhost
+
+- name: Query a leaf_interface_profile
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a leaf_interface_profile of type fex
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ interface_profile: leafintprfname_fex
+ type: fex
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all leaf_interface_profiles
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Query all leaf_interface_profiles of type fex
+ cisco.aci.aci_interface_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: fex
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ interface_profile=dict(type="str", aliases=["name", "leaf_interface_profile_name", "leaf_interface_profile", "interface_profile_name"]),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ type=dict(type="str", default="leaf", choices=["fex", "leaf"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["interface_profile"]],
+ ["state", "present", ["interface_profile"]],
+ ],
+ )
+
+ interface_profile = module.params.get("interface_profile")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+ type_profile = module.params.get("type")
+
+ aci = ACIModule(module)
+ aci_class = "infraAccPortP"
+ aci_rn = "accportprof"
+ if type_profile == "fex":
+ aci_class = "infraFexP"
+ aci_rn = "fexprof"
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_class,
+ aci_rn="infra/" + aci_rn + "-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(
+ name=interface_profile,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile_fex_policy_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile_fex_policy_group.py
new file mode 100644
index 00000000..8b020854
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_leaf_profile_fex_policy_group.py
@@ -0,0 +1,292 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Sabari Jaganathan <sajagana@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_leaf_profile_fex_policy_group
+short_description: Manage leaf interface profiles fex policy group (infra:FexBndlGrp)
+description:
+- Manage leaf interface profiles fex policy group on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the Fex Profile Policy Group.
+ type: str
+ aliases: [ policy_group ]
+ fex_profile:
+ description:
+ - The name of the Fex Profile.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:FexBndlGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sabari Jaganathan (@sajagana)
+"""
+
+EXAMPLES = r"""
+- name: Add a new fex policy group
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fex_policy_group
+ fex_profile: anstest_fex_profile
+ state: present
+ delegate_to: localhost
+
+- name: Add list of fex policy groups
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: "{{ item.name }}"
+ fex_profile: "{{ item.fex_profile }}"
+ state: present
+ delegate_to: localhost
+ with_items:
+ - name: fex_policy_group_1
+ fex_profile: anstest_fex_profile
+ - name: fex_policy_group_2
+ fex_profile: anstest_fex_profile
+
+- name: Query a fex policy group under fex profile
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fex_policy_group
+ fex_profile: anstest_fex_profile
+ state: query
+ delegate_to: localhost
+
+- name: Query all fex policy groups under fex profile
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ fex_profile: anstest_fex_profile
+ state: query
+ delegate_to: localhost
+
+- name: Query all fex policy groups with name
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fex_policy_group
+ state: query
+ delegate_to: localhost
+
+- name: Query all fex policy groups
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+
+- name: Remove fex policy group
+ cisco.aci.aci_interface_policy_leaf_profile_fex_policy_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: fex_policy_group
+ fex_profile: anstest_fex_profile
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["policy_group"]),
+ fex_profile=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name", "fex_profile"]],
+ ["state", "present", ["name", "fex_profile"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ fex_profile = module.params.get("fex_profile")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraFexP",
+ aci_rn="infra/fexprof-{0}".format(fex_profile),
+ module_object=fex_profile,
+ target_filter={"name": fex_profile},
+ ),
+ subclass_1=dict(
+ aci_class="infraFexBndlGrp",
+ aci_rn="fexbundle-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraFexBndlGrp",
+ class_config=dict(
+ name=name,
+ ),
+ )
+
+ aci.get_diff(aci_class="infraFexBndlGrp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py
new file mode 100644
index 00000000..5c0120d4
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_link_level.py
@@ -0,0 +1,298 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Vasily Prokopov (@vasilyprokopov)
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_link_level
+short_description: Manage Link Level interface policies (fabric:HIfPol)
+description:
+- The link level interface policy specifies the layer 1 parameters of switch interfaces.
+options:
+ link_level_policy:
+ description:
+ - The name of the Link Level interface policy.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description of the Link Level interface policy.
+ type: str
+ aliases: [ descr ]
+ auto_negotiation:
+ description:
+ - Auto-negotiation enables devices to automatically exchange information over a link about speed and duplex abilities.
+ - The APIC defaults to C(on) when unset during creation.
+ type: bool
+ default: true
+ speed:
+ description:
+ - Determines the interface policy administrative port speed.
+ - The APIC defaults to C(inherit) when unset during creation.
+ type: str
+ choices: [ 100M, 1G, 10G, 25G, 40G, 50G, 100G, 200G, 400G, inherit ]
+ default: inherit
+ link_debounce_interval:
+ description:
+ - Enables the debounce timer for physical interface ports and sets it for a specified amount of time in milliseconds.
+ - The APIC defaults to C(100) when unset during creation.
+ type: int
+ default: 100
+ forwarding_error_correction:
+ description:
+ - Determines the forwarding error correction (FEC) mode.
+ - The APIC defaults to C(inherit) when unset during creation.
+ type: str
+ choices: [ inherit, kp-fec, cl91-rs-fec, cl74-fc-fec, disable-fec, ieee-rs-fec, cons16-rs-fec ]
+ default: inherit
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fabric:HIfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Vasily Prokopov (@vasilyprokopov)
+"""
+
+EXAMPLES = r"""
+- name: Add a Link Level Policy
+ cisco.aci.aci_interface_policy_link_level:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ link_level_policy: link_level_policy_test
+ description: via Ansible
+ auto_negotiation: on
+ speed: 100M
+ link_debounce_interval: 100
+ forwarding_error_correction: cl91-rs-fec
+ state: present
+ delegate_to: localhost
+
+- name: Remove a Link Level Policy
+ cisco.aci.aci_interface_policy_link_level:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ link_level_policy: ansible_test
+ state: absent
+
+- name: Query a Link Level Policy
+ cisco.aci.aci_interface_policy_link_level:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ link_level_policy: link_level_policy_test
+ state: query
+ delegate_to: localhost
+
+- name: Query all Link Level Policies
+ cisco.aci.aci_interface_policy_link_level:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ link_level_policy=dict(type="str", aliases=["name"]),
+ description=dict(type="str", aliases=["descr"]),
+ auto_negotiation=dict(type="bool", default="true"),
+ speed=dict(type="str", default="inherit", choices=["100M", "1G", "10G", "25G", "40G", "50G", "100G", "200G", "400G", "inherit"]),
+ link_debounce_interval=dict(type="int", default="100"),
+ forwarding_error_correction=dict(
+ type="str", default="inherit", choices=["inherit", "kp-fec", "cl91-rs-fec", "cl74-fc-fec", "disable-fec", "ieee-rs-fec", "cons16-rs-fec"]
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["link_level_policy"]],
+ ["state", "present", ["link_level_policy"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ link_level_policy = module.params["link_level_policy"]
+ description = module.params["description"]
+ auto_negotiation = aci.boolean(module.params["auto_negotiation"], "on", "off")
+ speed = module.params["speed"]
+ link_debounce_interval = module.params["link_debounce_interval"]
+ if link_debounce_interval is not None and link_debounce_interval not in range(0, 5001):
+ module.fail_json(msg='The "link_debounce_interval" must be a value between 0 and 5000')
+ forwarding_error_correction = module.params["forwarding_error_correction"]
+ state = module.params["state"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricHIfPol",
+ aci_rn="infra/hintfpol-{0}".format(link_level_policy),
+ module_object=link_level_policy,
+ target_filter={"name": link_level_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricHIfPol",
+ class_config=dict(
+ name=link_level_policy,
+ descr=description,
+ autoNeg=auto_negotiation,
+ speed=speed,
+ linkDebounce=link_debounce_interval,
+ fecMode=forwarding_error_correction,
+ ),
+ )
+
+ aci.get_diff(aci_class="fabricHIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py
new file mode 100644
index 00000000..1281b844
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_lldp.py
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_lldp
+short_description: Manage LLDP interface policies (lldp:IfPol)
+description:
+- Manage LLDP interface policies on Cisco ACI fabrics.
+options:
+ lldp_policy:
+ description:
+ - The LLDP interface policy name.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description for the LLDP interface policy name.
+ type: str
+ aliases: [ descr ]
+ receive_state:
+ description:
+ - Enable or disable Receive state.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ transmit_state:
+ description:
+ - Enable or Disable Transmit state.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(lldp:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a LLDP interface policy
+ cisco.aci.aci_interface_policy_lldp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lldp_policy: LLDP_OFF
+ receive_state: false
+ transmit_state: false
+ state: present
+ delegate_to: localhost
+
+- name: Delete a LLDP interface policy
+ cisco.aci.aci_interface_policy_lldp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lldp_policy: LLDP_OFF
+ state: absent
+ delegate_to: localhost
+
+- name: Query all LLDP interface policies
+ cisco.aci.aci_interface_policy_lldp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific LLDP interface policy
+ cisco.aci.aci_interface_policy_lldp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lldp_policy: LLDP_OFF
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ lldp_policy=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ receive_state=dict(type="bool"),
+ transmit_state=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["lldp_policy"]],
+ ["state", "present", ["lldp_policy"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ lldp_policy = module.params.get("lldp_policy")
+ description = module.params.get("description")
+ receive_state = aci.boolean(module.params.get("receive_state"), "enabled", "disabled")
+ transmit_state = aci.boolean(module.params.get("transmit_state"), "enabled", "disabled")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="lldpIfPol",
+ aci_rn="infra/lldpIfP-{0}".format(lldp_policy),
+ module_object=lldp_policy,
+ target_filter={"name": lldp_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="lldpIfPol",
+ class_config=dict(
+ name=lldp_policy,
+ descr=description,
+ adminRxSt=receive_state,
+ adminTxSt=transmit_state,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="lldpIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py
new file mode 100644
index 00000000..6a1b6262
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_mcp.py
@@ -0,0 +1,269 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_mcp
+short_description: Manage MCP interface policies (mcp:IfPol)
+description:
+- Manage MCP interface policies on Cisco ACI fabrics.
+options:
+ mcp:
+ description:
+ - The name of the MCP interface.
+ type: str
+ aliases: [ mcp_interface, name ]
+ description:
+ description:
+ - The description for the MCP interface.
+ type: str
+ aliases: [ descr ]
+ admin_state:
+ description:
+ - Enable or disable admin state.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(mcp:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a MCP interface policy
+ cisco.aci.aci_interface_policy_mcp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ mcp: MCP_OFF
+ admin_state: false
+ state: present
+ delegate_to: localhost
+
+- name: Delete a MCP interface policy
+ cisco.aci.aci_interface_policy_mcp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ mcp: MCP_OFF
+ state: absent
+ delegate_to: localhost
+
+- name: Query all MCP interface policies
+ cisco.aci.aci_interface_policy_mcp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific MCP interface policy
+ cisco.aci.aci_interface_policy_mcp:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ mcp: MCP_OFF
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ mcp=dict(type="str", aliases=["mcp_interface", "name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ admin_state=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["mcp"]],
+ ["state", "present", ["mcp"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ mcp = module.params.get("mcp")
+ description = module.params.get("description")
+ admin_state = aci.boolean(module.params.get("admin_state"), "enabled", "disabled")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="mcpIfPol",
+ aci_rn="infra/mcpIfP-{0}".format(mcp),
+ module_object=mcp,
+ target_filter={"name": mcp},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="mcpIfPol",
+ class_config=dict(
+ name=mcp,
+ descr=description,
+ adminSt=admin_state,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="mcpIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py
new file mode 100644
index 00000000..040e1396
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_ospf.py
@@ -0,0 +1,408 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_ospf
+short_description: Manage OSPF interface policies (ospf:IfPol)
+description:
+- Manage OSPF interface policies on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of the Tenant the OSPF interface policy should belong to.
+ type: str
+ aliases: [ tenant_name ]
+ ospf:
+ description:
+ - The OSPF interface policy name.
+ - This name can be between 1 and 64 alphanumeric characters.
+ - Note that you cannot change this name after the object has been saved.
+ type: str
+ aliases: [ ospf_interface, name ]
+ description:
+ description:
+ - The description for the OSPF interface.
+ type: str
+ aliases: [ descr ]
+ network_type:
+ description:
+ - The OSPF interface policy network type.
+ - OSPF supports broadcast and point-to-point.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ bcast, p2p ]
+ cost:
+ description:
+ - The OSPF cost of the interface.
+ - The cost (also called metric) of an interface in OSPF is an indication of
+ the overhead required to send packets across a certain interface. The
+ cost of an interface is inversely proportional to the bandwidth of that
+ interface. A higher bandwidth indicates a lower cost. There is more
+ overhead (higher cost) and time delays involved in crossing a 56k serial
+ line than crossing a 10M ethernet line. The formula used to calculate the
+ cost is C(cost= 10000 0000/bandwith in bps) For example, it will cost
+ 10 EXP8/10 EXP7 = 10 to cross a 10M Ethernet line and will cost
+ 10 EXP8/1544000 = 64 to cross a T1 line.
+ - By default, the cost of an interface is calculated based on the bandwidth;
+ you can force the cost of an interface with the ip ospf cost value
+ interface subconfiguration mode command.
+ - Accepted values range between C(1) and C(450).
+ - The APIC defaults to C(0) when unset during creation.
+ type: int
+ controls:
+ description:
+ - The interface policy controls.
+ - 'This is a list of one or more of the following controls:'
+ - C(advert-subnet) -- Advertise IP subnet instead of a host mask in the router LSA.
+ - C(bfd) -- Bidirectional Forwarding Detection
+ - C(mtu-ignore) -- Disables MTU mismatch detection on an interface.
+ - C(passive) -- The interface does not participate in the OSPF protocol and
+ will not establish adjacencies or send routing updates. However the
+ interface is announced as part of the routing network.
+ type: list
+ elements: str
+ choices: [ advert-subnet, bfd, mtu-ignore, passive ]
+ dead_interval:
+ description:
+ - The interval between hello packets from a neighbor before the router
+ declares the neighbor as down.
+ - This value must be the same for all networking devices on a specific network.
+ - Specifying a smaller dead interval (seconds) will give faster detection
+ of a neighbor being down and improve convergence, but might cause more
+ routing instability.
+ - Accepted values range between C(1) and C(65535).
+ - The APIC defaults to C(40) when unset during creation.
+ type: int
+ hello_interval:
+ description:
+ - The interval between hello packets that OSPF sends on the interface.
+ - Note that the smaller the hello interval, the faster topological changes will be detected, but more routing traffic will ensue.
+ - This value must be the same for all routers and access servers on a specific network.
+ - Accepted values range between C(1) and C(65535).
+ - The APIC defaults to C(10) when unset during creation.
+ type: int
+ prefix_suppression:
+ description:
+ - Whether prefix suppressions is enabled or disabled.
+ - The APIC defaults to C(inherit) when unset during creation.
+ type: bool
+ priority:
+ description:
+ - The priority for the OSPF interface profile.
+ - Accepted values ranges between C(0) and C(255).
+ - The APIC defaults to C(1) when unset during creation.
+ type: int
+ retransmit_interval:
+ description:
+ - The interval between LSA retransmissions.
+ - The retransmit interval occurs while the router is waiting for an acknowledgement from the neighbor router that it received the LSA.
+ - If no acknowledgment is received at the end of the interval, then the LSA is resent.
+ - Accepted values range between C(1) and C(65535).
+ - The APIC defaults to C(5) when unset during creation.
+ type: int
+ transmit_delay:
+ description:
+ - The delay time needed to send an LSA update packet.
+ - OSPF increments the LSA age time by the transmit delay amount before transmitting the LSA update.
+ - You should take into account the transmission and propagation delays for the interface when you set this value.
+ - Accepted values range between C(1) and C(450).
+ - The APIC defaults to C(1) when unset during creation.
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(ospf:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Ensure ospf interface policy exists
+ cisco.aci.aci_interface_policy_ospf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ospf: ospf1
+ state: present
+ delegate_to: localhost
+
+- name: Ensure ospf interface policy does not exist
+ cisco.aci.aci_interface_policy_ospf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ospf: ospf1
+ state: present
+ delegate_to: localhost
+
+- name: Query an ospf interface policy
+ cisco.aci.aci_interface_policy_ospf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ ospf: ospf1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all ospf interface policies in tenant production
+ cisco.aci.aci_interface_policy_ospf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ ospf=dict(type="str", aliases=["ospf_interface", "name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ network_type=dict(type="str", choices=["bcast", "p2p"]),
+ cost=dict(type="int"),
+ controls=dict(type="list", elements="str", choices=["advert-subnet", "bfd", "mtu-ignore", "passive"]),
+ dead_interval=dict(type="int"),
+ hello_interval=dict(type="int"),
+ prefix_suppression=dict(type="bool"),
+ priority=dict(type="int"),
+ retransmit_interval=dict(type="int"),
+ transmit_delay=dict(type="int"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["ospf", "tenant"]],
+ ["state", "present", ["ospf", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ ospf = module.params.get("ospf")
+ description = module.params.get("description")
+ name_alias = module.params.get("name_alias")
+
+ if module.params.get("controls") is None:
+ controls = None
+ else:
+ controls = ",".join(module.params.get("controls"))
+
+ cost = module.params.get("cost")
+ if cost is not None and cost not in range(1, 451):
+ module.fail_json(msg="Parameter 'cost' is only valid in range between 1 and 450.")
+
+ dead_interval = module.params.get("dead_interval")
+ if dead_interval is not None and dead_interval not in range(1, 65536):
+ module.fail_json(msg="Parameter 'dead_interval' is only valid in range between 1 and 65536.")
+
+ hello_interval = module.params.get("hello_interval")
+ if hello_interval is not None and hello_interval not in range(1, 65536):
+ module.fail_json(msg="Parameter 'hello_interval' is only valid in range between 1 and 65536.")
+
+ network_type = module.params.get("network_type")
+ prefix_suppression = aci.boolean(module.params.get("prefix_suppression"), "enabled", "disabled")
+ priority = module.params.get("priority")
+ if priority is not None and priority not in range(0, 256):
+ module.fail_json(msg="Parameter 'priority' is only valid in range between 1 and 255.")
+
+ retransmit_interval = module.params.get("retransmit_interval")
+ if retransmit_interval is not None and retransmit_interval not in range(1, 65536):
+ module.fail_json(msg="Parameter 'retransmit_interval' is only valid in range between 1 and 65536.")
+
+ transmit_delay = module.params.get("transmit_delay")
+ if transmit_delay is not None and transmit_delay not in range(1, 451):
+ module.fail_json(msg="Parameter 'transmit_delay' is only valid in range between 1 and 450.")
+
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="ospfIfPol",
+ aci_rn="tn-{0}/ospfIfPol-{1}".format(tenant, ospf),
+ module_object=ospf,
+ target_filter={"name": ospf},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="ospfIfPol",
+ class_config=dict(
+ name=ospf,
+ descr=description,
+ cost=cost,
+ ctrl=controls,
+ deadIntvl=dead_interval,
+ helloIntvl=hello_interval,
+ nwT=network_type,
+ pfxSuppress=prefix_suppression,
+ prio=priority,
+ rexmitIntvl=retransmit_interval,
+ xmitDelay=transmit_delay,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="ospfIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py
new file mode 100644
index 00000000..d6b239d6
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_channel.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_port_channel
+short_description: Manage port channel interface policies (lacp:LagPol)
+description:
+- Manage port channel interface policies on Cisco ACI fabrics.
+options:
+ port_channel:
+ description:
+ - Name of the port channel.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description for the port channel.
+ type: str
+ aliases: [ descr ]
+ max_links:
+ description:
+ - Maximum links.
+ - Accepted values range between 1 and 16.
+ - The APIC defaults to C(16) when unset during creation.
+ type: int
+ min_links:
+ description:
+ - Minimum links.
+ - Accepted values range between 1 and 16.
+ - The APIC defaults to C(1) when unset during creation.
+ type: int
+ mode:
+ description:
+ - Port channel interface policy mode.
+ - Determines the LACP method to use for forming port-channels.
+ - The APIC defaults to C(off) when unset during creation.
+ type: str
+ choices: [ active, mac-pin, mac-pin-nicload, 'off', passive ]
+ fast_select:
+ description:
+ - Determines if Fast Select is enabled for Hot Standby Ports.
+ - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties
+ left undefined or set to false will not exist after the task is ran.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ graceful_convergence:
+ description:
+ - Determines if Graceful Convergence is enabled.
+ - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties
+ left undefined or set to false will not exist after the task is ran.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ load_defer:
+ description:
+ - Determines if Load Defer is enabled.
+ - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties
+ left undefined or set to false will not exist after the task is ran.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ suspend_individual:
+ description:
+ - Determines if Suspend Individual is enabled.
+ - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties
+ left undefined or set to false will not exist after the task is ran.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ symmetric_hash:
+ description:
+ - Determines if Symmetric Hashing is enabled.
+ - This makes up the LACP Policy Control Policy; if one setting is defined, then all other Control Properties
+ left undefined or set to false will not exist after the task is ran.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(lacp:LagPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a Port Channel interface policy
+ cisco.aci.aci_interface_policy_port_channel:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_channel: LACP_ACTIVE
+ mode: active
+ min_links: 1
+ max_links: 16
+ state: present
+ delegate_to: localhost
+
+- name: Delete a Port Channel interface policy
+ cisco.aci.aci_interface_policy_port_channel:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_channel: LACP_ACTIVE
+ state: absent
+ delegate_to: localhost
+
+- name: Query all Port Channel interface policies
+ cisco.aci.aci_interface_policy_port_channel:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific Port Channel interface policy
+ cisco.aci.aci_interface_policy_port_channel:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_channel: LACP_ACTIVE
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ port_channel=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ min_links=dict(type="int"),
+ max_links=dict(type="int"),
+ mode=dict(type="str", choices=["active", "mac-pin", "mac-pin-nicload", "off", "passive"]),
+ fast_select=dict(type="bool"),
+ graceful_convergence=dict(type="bool"),
+ load_defer=dict(type="bool"),
+ suspend_individual=dict(type="bool"),
+ symmetric_hash=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["port_channel"]],
+ ["state", "present", ["port_channel"]],
+ ],
+ )
+
+ port_channel = module.params.get("port_channel")
+ description = module.params.get("description")
+ min_links = module.params.get("min_links")
+ if min_links is not None and min_links not in range(1, 17):
+ module.fail_json(msg='The "min_links" must be a value between 1 and 16')
+ max_links = module.params.get("max_links")
+ if max_links is not None and max_links not in range(1, 17):
+ module.fail_json(msg='The "max_links" must be a value between 1 and 16')
+ mode = module.params.get("mode")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ # Build ctrl value for request
+ ctrl = []
+ if module.params.get("fast_select") is True:
+ ctrl.append("fast-sel-hot-stdby")
+ if module.params.get("graceful_convergence") is True:
+ ctrl.append("graceful-conv")
+ if module.params.get("load_defer") is True:
+ ctrl.append("load-defer")
+ if module.params.get("suspend_individual") is True:
+ ctrl.append("susp-individual")
+ if module.params.get("symmetric_hash") is True:
+ ctrl.append("symmetric-hash")
+ if not ctrl:
+ ctrl = None
+ else:
+ ctrl = ",".join(ctrl)
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="lacpLagPol",
+ aci_rn="infra/lacplagp-{0}".format(port_channel),
+ module_object=port_channel,
+ target_filter={"name": port_channel},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="lacpLagPol",
+ class_config=dict(
+ name=port_channel,
+ ctrl=ctrl,
+ descr=description,
+ minLinks=min_links,
+ maxLinks=max_links,
+ mode=mode,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="lacpLagPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py
new file mode 100644
index 00000000..b1df4191
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_policy_port_security.py
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_port_security
+short_description: Manage port security (l2:PortSecurityPol)
+description:
+- Manage port security on Cisco ACI fabrics.
+options:
+ port_security:
+ description:
+ - The name of the port security.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description for the contract.
+ type: str
+ aliases: [ descr ]
+ max_end_points:
+ description:
+ - Maximum number of end points.
+ - Accepted values range between C(0) and C(12000).
+ - The APIC defaults to C(0) when unset during creation.
+ type: int
+ port_security_timeout:
+ description:
+ - The delay time in seconds before MAC learning is re-enabled
+ - Accepted values range between C(60) and C(3600)
+ - The APIC defaults to C(60) when unset during creation
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l2:PortSecurityPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a Port Security interface policy
+ cisco.aci.aci_interface_policy_port_security:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_security: PS_1
+ max_end_points: 1
+ port_security_timeout: 60
+ state: present
+ delegate_to: localhost
+
+- name: Delete a Port Security interface policy
+ cisco.aci.aci_interface_policy_port_security:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_security: PS_1
+ state: absent
+ delegate_to: localhost
+
+- name: Query all Port Security interface policies
+ cisco.aci.aci_interface_policy_port_security:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific Port Security interface policy
+ cisco.aci.aci_interface_policy_port_security:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_security: PS_1
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ port_security=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ max_end_points=dict(type="int"),
+ port_security_timeout=dict(type="int"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["port_security"]],
+ ["state", "present", ["port_security"]],
+ ],
+ )
+
+ port_security = module.params.get("port_security")
+ description = module.params.get("description")
+ max_end_points = module.params.get("max_end_points")
+ port_security_timeout = module.params.get("port_security_timeout")
+ name_alias = module.params.get("name_alias")
+ if max_end_points is not None and max_end_points not in range(12001):
+ module.fail_json(msg="The max_end_points must be between 0 and 12000")
+ if port_security_timeout is not None and port_security_timeout not in range(60, 3601):
+ module.fail_json(msg="The port_security_timeout must be between 60 and 3600")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="l2PortSecurityPol",
+ aci_rn="infra/portsecurityP-{0}".format(port_security),
+ module_object=port_security,
+ target_filter={"name": port_security},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l2PortSecurityPol",
+ class_config=dict(
+ name=port_security,
+ descr=description,
+ maximum=max_end_points,
+ nameAlias=name_alias,
+ timeout=port_security_timeout,
+ ),
+ )
+
+ aci.get_diff(aci_class="l2PortSecurityPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py
new file mode 100644
index 00000000..2adfb191
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_interface_selector_to_switch_policy_leaf_profile.py
@@ -0,0 +1,252 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_selector_to_switch_policy_leaf_profile
+short_description: Bind interface selector profiles to switch policy leaf profiles (infra:RsAccPortP)
+description:
+- Bind interface selector profiles to switch policy leaf profiles on Cisco ACI fabrics.
+options:
+ leaf_profile:
+ description:
+ - Name of the Leaf Profile to which we add a Selector.
+ type: str
+ aliases: [ leaf_profile_name ]
+ interface_selector:
+ description:
+ - Name of Interface Profile Selector to be added and associated with the Leaf Profile.
+ type: str
+ aliases: [ name, interface_selector_name, interface_profile_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- This module requires an existing leaf profile, the module M(cisco.aci.aci_switch_policy_leaf_profile) can be used for this.
+seealso:
+- module: cisco.aci.aci_switch_policy_leaf_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:RsAccPortP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+"""
+
+EXAMPLES = r"""
+- name: Associating an interface selector profile to a switch policy leaf profile
+ cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ interface_selector: interface_profile_name
+ state: present
+ delegate_to: localhost
+
+- name: Remove an interface selector profile associated with a switch policy leaf profile
+ cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ interface_selector: interface_profile_name
+ state: absent
+ delegate_to: localhost
+
+- name: Query an interface selector profile associated with a switch policy leaf profile
+ cisco.aci.aci_interface_selector_to_switch_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ interface_selector: interface_profile_name
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[["state", "absent", ["leaf_profile", "interface_selector"]], ["state", "present", ["leaf_profile", "interface_selector"]]],
+ )
+
+ leaf_profile = module.params.get("leaf_profile")
+ # WARNING: interface_selector accepts non existing interface_profile names and they appear on APIC gui with a state of "missing-target"
+ interface_selector = module.params.get("interface_selector")
+ state = module.params.get("state")
+
+ # Defining the interface profile tDn for clarity
+ interface_selector_tDn = "uni/infra/accportprof-{0}".format(interface_selector)
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraNodeP",
+ aci_rn="infra/nprof-{0}".format(leaf_profile),
+ module_object=leaf_profile,
+ target_filter={"name": leaf_profile},
+ ),
+ subclass_1=dict(
+ aci_class="infraRsAccPortP",
+ aci_rn="rsaccPortP-[{0}]".format(interface_selector_tDn),
+ module_object=interface_selector,
+ target_filter={"tDn": interface_selector_tDn},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraRsAccPortP",
+ class_config=dict(tDn=interface_selector_tDn),
+ )
+
+ aci.get_diff(aci_class="infraRsAccPortP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l2out.py b/ansible_collections/cisco/aci/plugins/modules/aci_l2out.py
new file mode 100644
index 00000000..974c3c8e
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l2out.py
@@ -0,0 +1,306 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1)
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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_l2out
+short_description: Manage Layer2 Out (L2Out) objects.
+description:
+- Manage Layer2 Out configuration on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ l2out:
+ description:
+ - The name of outer layer2.
+ type: str
+ aliases: [ 'name' ]
+ description:
+ description:
+ - Description for the L2Out.
+ type: str
+ bd:
+ description:
+ - Name of the Bridge domain which is associted with the L2Out.
+ type: str
+ domain:
+ description:
+ - Name of the external L2 Domain that is being associated with L2Out.
+ type: str
+ vlan:
+ description:
+ - The VLAN which is being associated with the L2Out.
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fvTenant).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sudhakar Shet Kudtarkar (@kudtarkar1)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new L2Out
+ cisco.aci.aci_l2out:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ description: via Ansible
+ bd: bd1
+ domain: l2Dom
+ vlan: 3200
+ state: present
+ delegate_to: localhost
+
+- name: Remove an L2Out
+ cisco.aci.aci_l2out:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ state: absent
+ delegate_to: localhost
+
+- name: Query an L2Out
+ cisco.aci.aci_l2out:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all L2Outs in a specific tenant
+ cisco.aci.aci_l2out:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ bd=dict(type="str"),
+ l2out=dict(type="str", aliases=["name"]),
+ domain=dict(type="str"),
+ vlan=dict(type="int"),
+ description=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ tenant=dict(type="str"),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["l2out", "tenant"]],
+ ["state", "present", ["bd", "l2out", "tenant", "domain", "vlan"]],
+ ],
+ )
+
+ bd = module.params.get("bd")
+ l2out = module.params.get("l2out")
+ description = module.params.get("description")
+ domain = module.params.get("domain")
+ vlan = module.params.get("vlan")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+ child_classes = ["l2extRsEBd", "l2extRsL2DomAtt", "l2extLNodeP"]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l2extOut",
+ aci_rn="l2out-{0}".format(l2out),
+ module_object=l2out,
+ target_filter={"name": l2out},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = [
+ dict(l2extRsL2DomAtt=dict(attributes=dict(tDn="uni/l2dom-{0}".format(domain)))),
+ dict(l2extRsEBd=dict(attributes=dict(tnFvBDName=bd, encap="vlan-{0}".format(vlan)))),
+ ]
+
+ aci.payload(
+ aci_class="l2extOut",
+ class_config=dict(name=l2out, descr=description, dn="uni/tn-{0}/l2out-{1}".format(tenant, l2out), nameAlias=name_alias),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="l2extOut")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py
new file mode 100644
index 00000000..f896e942
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg.py
@@ -0,0 +1,305 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1)
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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_l2out_extepg
+short_description: Manage External Network Instance (L2Out External EPG) objects (l2extInstP).
+description:
+- Manage External Network Instance (L2Out External EPG) objects (l2extInstP) on ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of existing tenant.
+ type: str
+ l2out:
+ description:
+ - Name of the l2out.
+ type: str
+ extepg:
+ description:
+ - Name of the external end point group.
+ type: str
+ aliases: [ external_epg, extepg_name, name ]
+ description:
+ description:
+ - Description for the l2out.
+ type: str
+ preferred_group:
+ description:
+ - This depicts whether this External EPG is part of the Preferred Group and can communicate without contracts.
+ - This is convenient for migration scenarios, or when ACI is used for network automation but not for policy.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ qos_class:
+ description:
+ - The bandwidth level for Quality of service.
+ type: str
+ choices: [ level1, level2, level3, level4, level5, level6, Unspecified ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(l2out) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l2out) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fvtenant) and B(l2extOut).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sudhakar Shet Kudtarkar (@kudtarkar1)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new L2 external end point group
+ cisco.aci.aci_l2out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ extepg: NewExt
+ description: external epg
+ preferred_group: False
+ state: present
+ delegate_to: localhost
+
+- name: Remove an L2 external end point group
+ cisco.aci.aci_l2out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ extepg: NewExt
+ state: absent
+ delegate_to: localhost
+
+- name: Query the L2 external end point group
+ cisco.aci.aci_l2out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ extepg: NewExt
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all L2 external end point groups in a tenant
+ cisco.aci.aci_l2out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ l2out=dict(type="str"),
+ description=dict(type="str"),
+ extepg=dict(type="str", aliases=["external_epg", "extepg_name", "name"]),
+ preferred_group=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ tenant=dict(type="str"),
+ qos_class=dict(type="str", choices=["level1", "level2", "level3", "level4", "level5", "level6", "Unspecified"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["l2out", "tenant", "extepg"]],
+ ["state", "present", ["l2out", "tenant", "extepg"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ l2out = module.params.get("l2out")
+ description = module.params.get("description")
+ preferred_group = aci.boolean(module.params.get("preferred_group"), "include", "exclude")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ extepg = module.params.get("extepg")
+ qos_class = module.params.get("qos_class")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l2extOut",
+ aci_rn="l2out-{0}".format(l2out),
+ module_object=l2out,
+ target_filter={"name": l2out},
+ ),
+ subclass_2=dict(
+ aci_class="l2extInstP",
+ aci_rn="instP-{0}".format(extepg),
+ module_object=extepg,
+ target_filter={"name": extepg},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ config = dict(name=extepg, descr=description, dn="uni/tn-{0}/l2out-{1}/instP-{2}".format(tenant, l2out, extepg), prefGrMemb=preferred_group)
+ if qos_class:
+ config.update(prio=qos_class)
+ aci.payload(
+ class_config=config,
+ aci_class="l2extInstP",
+ )
+
+ aci.get_diff(aci_class="l2extInstP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg_to_contract.py b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg_to_contract.py
new file mode 100644
index 00000000..ba536b54
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_extepg_to_contract.py
@@ -0,0 +1,349 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1)
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# Copyright: (c) 2021, Oleksandr Kreshchenko (@alexkross)
+# GNU General Public License v3.0+ (see LICENSE 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_l2out_extepg_to_contract
+short_description: Bind Contracts to L2 External End Point Groups (EPGs)
+description:
+- Bind Contracts to L2 External End Point Groups (EPGs) on ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of existing tenant.
+ type: str
+ l2out:
+ description:
+ - Name of the l2out.
+ type: str
+ aliases: ['l2out_name']
+ extepg:
+ description:
+ - Name of the external end point group.
+ type: str
+ aliases: ['extepg_name', 'external_epg']
+ contract:
+ description:
+ - Name of the contract.
+ type: str
+ contract_type:
+ description:
+ - The type of contract.
+ type: str
+ required: yes
+ choices: ['consumer', 'provider']
+ priority:
+ description:
+ - This has four levels of priority.
+ type: str
+ choices: ['level1', 'level2', 'level3', 'unspecified']
+ provider_match:
+ description:
+ - This is configurable for provided contracts.
+ type: str
+ choices: ['all', 'at_least_one', 'at_most_one', 'none']
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(l2out) and C(extepg) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l2out) and M(cisco.aci.aci_l2out_extepg) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fvtenant), B(l2extInstP) and B(l2extOut).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sudhakar Shet Kudtarkar (@kudtarkar1)
+- Shreyas Srish (@shrsr)
+- Oleksandr Kreshchenko (@alexkross)
+"""
+
+EXAMPLES = r"""
+- name: Bind a contract to an L2 external EPG
+ cisco.aci.aci_l2out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ extepg : testEpg
+ contract: contract1
+ contract_type: provider
+ state: present
+ delegate_to: localhost
+
+- name: Remove existing contract from an L2 external EPG
+ cisco.aco.aci_l2out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l2out: l2out
+ extepg : testEpg
+ contract: contract1
+ contract_type: provider
+ state: absent
+ delegate_to: localhost
+
+- name: Query a contract bound to an L2 external EPG
+ cisco.aci.aci_l2out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: ansible_tenant
+ l2out: ansible_l2out
+ extepg: ansible_extEpg
+ contract: ansible_contract
+ contract_type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all contracts relationships
+ cisco.aci.aci_l2out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ contract_type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+ACI_CLASS_MAPPING = dict(
+ consumer={
+ "class": "fvRsCons",
+ "rn": "rscons-",
+ },
+ provider={
+ "class": "fvRsProv",
+ "rn": "rsprov-",
+ },
+)
+
+PROVIDER_MATCH_MAPPING = dict(
+ all="All",
+ at_least_one="AtleastOne",
+ at_most_one="tmostOne",
+ none="None",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ contract_type=dict(type="str", required=True, choices=["consumer", "provider"]),
+ l2out=dict(type="str", aliases=["l2out_name"]),
+ contract=dict(type="str"),
+ 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"),
+ extepg=dict(type="str", aliases=["extepg_name", "external_epg"]),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["extepg", "contract", "l2out", "tenant"]],
+ ["state", "present", ["extepg", "contract", "l2out", "tenant"]],
+ ],
+ )
+
+ l2out = module.params.get("l2out")
+ contract = module.params.get("contract")
+ contract_type = module.params.get("contract_type")
+ extepg = module.params.get("extepg")
+ priority = module.params.get("priority")
+ provider_match = module.params.get("provider_match")
+ if provider_match is not None:
+ provider_match = PROVIDER_MATCH_MAPPING.get(provider_match)
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+
+ aci_class = ACI_CLASS_MAPPING.get(contract_type)["class"]
+ aci_rn = ACI_CLASS_MAPPING.get(contract_type)["rn"]
+
+ if contract_type == "consumer" and provider_match is not None:
+ module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l2extOut",
+ aci_rn="l2out-{0}".format(l2out),
+ module_object=l2out,
+ target_filter={"name": l2out},
+ ),
+ subclass_2=dict(
+ aci_class="l2extInstP",
+ aci_rn="instP-{0}".format(extepg),
+ module_object=extepg,
+ target_filter={"name": extepg},
+ ),
+ subclass_3=dict(
+ aci_class=aci_class,
+ aci_rn="{0}{1}".format(aci_rn, contract),
+ module_object=contract,
+ target_filter={"tnVzBrCPName": contract},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(
+ matchT=provider_match,
+ prio=priority,
+ tnVzBrCPName=contract,
+ ),
+ )
+
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_path.py b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_path.py
new file mode 100644
index 00000000..6be588bc
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_path.py
@@ -0,0 +1,372 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l2out_logical_interface_path
+short_description: Manage Layer 2 Outside (L2Out) logical interface path (l2extRsPathL2OutAtt)
+description:
+- Manage interface path entry of L2 outside node (BD extension) on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l2out:
+ description:
+ - Name of an existing L2Out.
+ type: str
+ aliases: [ l2out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ name, interface_profile_name, logical_interface ]
+ interface_type:
+ description:
+ - The type of interface for the static EPG deployment.
+ type: str
+ choices: [ switch_port, port_channel, vpc ]
+ default: switch_port
+ pod_id:
+ description:
+ - The pod number part of the tDn.
+ - C(pod_id) is usually an integer below C(10).
+ type: int
+ aliases: [ pod, pod_number ]
+ leaves:
+ description:
+ - The switch ID(s) that the C(interface) belongs to.
+ - When C(interface_type) is C(switch_port) or C(port_channel), then C(leaves) is a string of the leaf ID.
+ - When C(interface_type) is C(vpc), then C(leaves) is a list with both leaf IDs.
+ - The C(leaves) value is usually something like '101' or '101-102' depending on C(connection_type).
+ type: list
+ elements: str
+ aliases: [ leafs, nodes, paths, switches ]
+ interface:
+ description:
+ - The C(interface) string value part of the tDn.
+ - Usually a policy group like C(test-IntPolGrp) or an interface of the following format C(1/7) depending on C(interface_type).
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: aci_l2out
+- module: aci_l2out_logical_node_profile
+- module: aci_l2out_logical_interface_profile
+- module: aci_l2out_extepg
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Oleksandr Kreshchenko (@alexkross)
+"""
+
+EXAMPLES = r"""
+- name: Add new path to interface profile
+ cisco.aci.aci_l2out_logical_interface_path:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ leaves: 101-102
+ interface: L2o1
+ state: present
+ delegate_to: localhost
+
+- name: Delete path to interface profile
+ cisco.aci.aci_l2out_logical_interface_path:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ leaves: 101-102
+ interface: L2o1
+ state: absent
+ delegate_to: localhost
+
+- name: Query a path to interface profile
+ cisco.aci.aci_l2out_logical_interface_path:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ leaves: 101-102
+ interface: L2o1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all paths to interface profiles
+ cisco.aci.aci_l2out_logical_interface_path:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+INTERFACE_TYPE_MAPPING = dict(
+ switch_port="topology/pod-{pod_id}/paths-{leaves}/pathep-[eth{interface}]",
+ port_channel="topology/pod-{pod_id}/paths-{leaves}/pathep-[{interface}]",
+ vpc="topology/pod-{pod_id}/protpaths-{leaves}/pathep-[{interface}]",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update( # See comments in aci_static_binding_to_epg module.
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l2out=dict(type="str", aliases=["l2out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ interface_profile=dict(type="str", aliases=["name", "interface_profile_name", "logical_interface"]),
+ interface_type=dict(type="str", default="switch_port", choices=["switch_port", "port_channel", "vpc"]),
+ pod_id=dict(type="int", aliases=["pod", "pod_number"]),
+ leaves=dict(type="list", elements="str", aliases=["leafs", "nodes", "paths", "switches"]),
+ interface=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l2out", "node_profile", "interface_profile", "pod_id", "leaves", "interface"]],
+ ["state", "present", ["tenant", "l2out", "node_profile", "interface_profile", "pod_id", "leaves", "interface"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l2out = module.params.get("l2out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ interface_type = module.params.get("interface_type")
+ pod_id = module.params.get("pod_id")
+ leaves = module.params.get("leaves")
+ if leaves is not None: # Process leaves, and support dash-delimited leaves
+ leaves = []
+ for leaf in module.params.get("leaves"): # Users are likely to use integers for leaf IDs, which would raise an exception when using the join method
+ leaves.extend(str(leaf).split("-"))
+ if len(leaves) == 1:
+ if interface_type == "vpc":
+ module.fail_json(msg='A interface_type of "vpc" requires 2 leaves')
+ leaves = leaves[0]
+ elif len(leaves) == 2:
+ if interface_type != "vpc":
+ module.fail_json(msg='The interface_types "switch_port" and "port_channel" do not support using multiple leaves for a single binding')
+ leaves = "-".join(leaves)
+ else:
+ module.fail_json(msg='The "leaves" parameter must not have more than 2 entries')
+ interface = module.params.get("interface")
+ state = module.params.get("state")
+
+ path = INTERFACE_TYPE_MAPPING[interface_type].format(pod_id=pod_id, leaves=leaves, interface=interface)
+ if not pod_id or not leaves or not interface:
+ path = None
+
+ path_target_filter = {}
+ if any((pod_id, leaves, interface)):
+ path_target_filter = {"tDn": path}
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l2extOut",
+ aci_rn="l2out-{0}".format(l2out),
+ module_object=l2out,
+ target_filter={"name": l2out},
+ ),
+ subclass_2=dict(
+ aci_class="l2extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l2extLIfP",
+ aci_rn="lifp-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_4=dict(
+ aci_class="l2extRsPathL2OutAtt",
+ aci_rn="rspathL2OutAtt-[{0}]".format(path),
+ # rspathL2OutAtt-[topology/pod-1/protpaths-101-102/pathep-[L2o2_n7]]
+ module_object=path,
+ target_filter=path_target_filter,
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l2extRsPathL2OutAtt",
+ class_config=dict(tDn=path),
+ )
+
+ aci.get_diff(aci_class="l2extRsPathL2OutAtt")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_profile.py
new file mode 100644
index 00000000..93e8fd19
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_interface_profile.py
@@ -0,0 +1,299 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l2out_logical_interface_profile
+short_description: Manage Layer 2 Outside (L2Out) interface profiles (l2ext:LIfP)
+description:
+- Manage interface profiles of L2 outside (BD extension) on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l2out:
+ description:
+ - Name of an existing L2Out.
+ type: str
+ aliases: [ l2out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ default: default
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ name, interface_profile_name, logical_interface ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: aci_l2out
+- module: aci_l2out_logical_node_profile
+- module: aci_l2out_logical_interface_path
+- module: aci_l2out_extepg
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Oleksandr Kreshchenko (@alexkross)
+"""
+
+EXAMPLES = r"""
+- name: Add new interface profile
+ cisco.aci.aci_l2out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ state: present
+ delegate_to: localhost
+
+- name: Delete interface profile
+ cisco.aci.aci_l2out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ state: absent
+ delegate_to: localhost
+
+- name: Query an interface profile
+ cisco.aci.aci_l2out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all interface profiles
+ cisco.aci.aci_l2out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update( # See comments in aci_static_binding_to_epg module.
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l2out=dict(type="str", aliases=["l2out_name"]),
+ node_profile=dict(type="str", default="default", aliases=["node_profile_name", "logical_node"]),
+ interface_profile=dict(type="str", aliases=["name", "interface_profile_name", "logical_interface"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l2out", "node_profile", "interface_profile"]],
+ ["state", "present", ["tenant", "l2out", "node_profile", "interface_profile"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l2out = module.params.get("l2out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l2extOut",
+ aci_rn="l2out-{0}".format(l2out),
+ module_object=l2out,
+ target_filter={"name": l2out},
+ ),
+ subclass_2=dict(
+ aci_class="l2extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l2extLIfP",
+ aci_rn="lifp-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ # child_configs = []
+ aci.payload(
+ aci_class="l2extLIfP",
+ class_config=dict(name=interface_profile),
+ # child_configs=child_configs
+ )
+
+ aci.get_diff(aci_class="l2extLIfP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_node_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_node_profile.py
new file mode 100644
index 00000000..12a1aa6f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l2out_logical_node_profile.py
@@ -0,0 +1,279 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l2out_logical_node_profile
+short_description: Manage Layer 2 Outside (L2Out) logical node profiles (l2ext:LNodeP)
+description:
+- Manage node profiles of L2 outside (BD extension) on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l2out:
+ description:
+ - Name of an existing L2Out.
+ type: str
+ aliases: [ l2out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: aci_l2out
+- module: aci_l2out_logical_interface_profile
+- module: aci_l2out_logical_interface_path
+- module: aci_l2out_extepg
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Oleksandr Kreshchenko (@alexkross)
+"""
+
+EXAMPLES = r"""
+- name: Add new node profile
+ cisco.aci.aci_l2out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ state: present
+ delegate_to: localhost
+
+- name: Delete node profile
+ cisco.aci.aci_l2out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ state: absent
+ delegate_to: localhost
+
+- name: Query an node profile
+ cisco.aci.aci_l2out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l2out: my_l2out
+ node_profile: my_node_profile
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all node profiles
+ cisco.aci.aci_l2out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update( # See comments in aci_static_binding_to_epg module.
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l2out=dict(type="str", aliases=["l2out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[["state", "absent", ["tenant", "l2out", "node_profile"]], ["state", "present", ["tenant", "l2out", "node_profile"]]],
+ )
+
+ tenant = module.params.get("tenant")
+ l2out = module.params.get("l2out")
+ node_profile = module.params.get("node_profile")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l2extOut",
+ aci_rn="l2out-{0}".format(l2out),
+ module_object=l2out,
+ target_filter={"name": l2out},
+ ),
+ subclass_2=dict(
+ aci_class="l2extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ # child_configs = []
+ aci.payload(
+ aci_class="l2extLNodeP",
+ class_config=dict(name=node_profile),
+ # child_configs=child_configs
+ )
+
+ aci.get_diff(aci_class="l2extLNodeP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out.py
new file mode 100644
index 00000000..70cd11f8
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out.py
@@ -0,0 +1,389 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_l3out
+short_description: Manage Layer 3 Outside (L3Out) objects (l3ext:Out)
+description:
+- Manage Layer 3 Outside (L3Out) on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of L3Out being created.
+ type: str
+ aliases: [ l3out_name, name ]
+ vrf:
+ description:
+ - Name of the VRF being associated with the L3Out.
+ type: str
+ aliases: [ vrf_name ]
+ domain:
+ description:
+ - Name of the external L3 domain being associated with the L3Out.
+ type: str
+ aliases: [ ext_routed_domain_name, routed_domain ]
+ dscp:
+ description:
+ - The target Differentiated Service (DSCP) value.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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 ]
+ route_control:
+ description:
+ - Route Control enforcement direction. The only allowed values are export or import,export.
+ type: list
+ elements: str
+ choices: [ export, import ]
+ aliases: [ route_control_enforcement ]
+ l3protocol:
+ description:
+ - Routing protocol for the L3Out
+ type: list
+ elements: str
+ choices: [ bgp, eigrp, ospf, pim, static ]
+ asn:
+ description:
+ - The AS number for the L3Out.
+ - Only applicable when using 'eigrp' as the l3protocol
+ type: int
+ aliases: [ as_number ]
+ description:
+ description:
+ - Description for the L3Out.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) and C(domain) and C(vrf) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_domain) and M(cisco.aci.aci_vrf) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vrf
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:Out).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Rostyslav Davydenko (@rost-d)
+"""
+
+EXAMPLES = r"""
+- name: Add a new L3Out
+ cisco.aci.aci_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ name: prod_l3out
+ description: L3Out for Production tenant
+ domain: l3dom_prod
+ vrf: prod
+ l3protocol: ospf
+ state: present
+ delegate_to: localhost
+
+- name: Delete L3Out
+ cisco.aci.aci_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ name: prod_l3out
+ state: absent
+ delegate_to: localhost
+
+- name: Query L3Out information
+ cisco.aci.aci_l3out:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ name: prod_l3out
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ l3out=dict(type="str", aliases=["l3out_name", "name"]), # Not required for querying all objects
+ domain=dict(type="str", aliases=["ext_routed_domain_name", "routed_domain"]),
+ vrf=dict(type="str", aliases=["vrf_name"]),
+ description=dict(type="str", aliases=["descr"]),
+ route_control=dict(type="list", elements="str", choices=["export", "import"], aliases=["route_control_enforcement"]),
+ dscp=dict(
+ type="str",
+ 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"],
+ ),
+ l3protocol=dict(type="list", elements="str", choices=["bgp", "eigrp", "ospf", "pim", "static"]),
+ asn=dict(type="int", aliases=["as_number"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["l3out", "tenant"]],
+ ["state", "present", ["l3out", "tenant", "domain", "vrf"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ l3out = module.params.get("l3out")
+ domain = module.params.get("domain")
+ dscp = module.params.get("dscp")
+ description = module.params.get("description")
+ enforceRtctrl = module.params.get("route_control")
+ vrf = module.params.get("vrf")
+ l3protocol = module.params.get("l3protocol")
+ asn = module.params.get("asn")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ if l3protocol:
+ if "eigrp" in l3protocol and asn is None:
+ module.fail_json(msg="Parameter 'asn' is required when l3protocol is 'eigrp'")
+ if "eigrp" not in l3protocol and asn is not None:
+ module.warn("Parameter 'asn' is only applicable when l3protocol is 'eigrp'. The ASN will be ignored")
+
+ enforce_ctrl = ""
+ if enforceRtctrl is not None:
+ if len(enforceRtctrl) == 1 and enforceRtctrl[0] == "import":
+ aci.fail_json("The route_control parameter is invalid: allowed options are export or import,export only")
+ elif len(enforceRtctrl) == 1 and enforceRtctrl[0] == "export":
+ enforce_ctrl = "export"
+ else:
+ enforce_ctrl = "export,import"
+ child_classes = ["l3extRsL3DomAtt", "l3extRsEctx", "bgpExtP", "ospfExtP", "eigrpExtP", "pimExtP"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ child_configs = [
+ dict(l3extRsL3DomAtt=dict(attributes=dict(tDn="uni/l3dom-{0}".format(domain)))),
+ dict(l3extRsEctx=dict(attributes=dict(tnFvCtxName=vrf))),
+ ]
+ if l3protocol is not None:
+ for protocol in l3protocol:
+ if protocol == "bgp":
+ child_configs.append(dict(bgpExtP=dict(attributes=dict(descr="", nameAlias=""))))
+ elif protocol == "eigrp":
+ child_configs.append(dict(eigrpExtP=dict(attributes=dict(descr="", nameAlias="", asn=asn))))
+ elif protocol == "ospf":
+ child_configs.append(dict(ospfExtP=dict(attributes=dict(descr="", nameAlias=""))))
+ elif protocol == "pim":
+ child_configs.append(dict(pimExtP=dict(attributes=dict(descr="", nameAlias=""))))
+
+ if state == "present":
+ aci.payload(
+ aci_class="l3extOut",
+ class_config=dict(
+ name=l3out,
+ descr=description,
+ dn="uni/tn-{0}/out-{1}".format(tenant, l3out),
+ enforceRtctrl=enforce_ctrl,
+ targetDscp=dscp,
+ nameAlias=name_alias,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="l3extOut")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_bgp_peer.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_bgp_peer.py
new file mode 100644
index 00000000..23e90f46
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_bgp_peer.py
@@ -0,0 +1,616 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_bgp_peer
+short_description: Manage Layer 3 Outside (L3Out) BGP Peers (bgp:PeerP)
+description:
+- Manage L3Out BGP Peers on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ interface_profile_name, logical_interface ]
+ pod_id:
+ description:
+ - Pod to build the interface on.
+ type: str
+ node_id:
+ description:
+ - Node to build the interface on for Port-channels and single ports.
+ - Hyphen separated pair of nodes (e.g. "201-202") for vPCs.
+ type: str
+ path_ep:
+ description:
+ - Path to interface
+ - Interface Port Group name for Port-channels and vPCs
+ - Port number for single ports (e.g. "eth1/12")
+ type: str
+ peer_ip:
+ description:
+ - IP address of the BGP peer.
+ type: str
+ remote_asn:
+ description:
+ - Autonomous System Number of the BGP peer.
+ type: int
+ bgp_controls:
+ description:
+ - BGP Controls
+ type: list
+ elements: str
+ choices: [ send-com, send-ext-com, allow-self-as, as-override, dis-peer-as-check, nh-self ]
+ peer_controls:
+ description:
+ - Peer Controls
+ type: list
+ elements: str
+ choices: [ bfd, dis-conn-check ]
+ address_type_controls:
+ description:
+ - Address Type Controls
+ type: list
+ elements: str
+ choices: [ af-ucast, af-mcast ]
+ private_asn_controls:
+ description:
+ - Private AS Controls
+ type: list
+ elements: str
+ choices: [ remove-exclusive, remove-all, replace-as ]
+ ttl:
+ description:
+ - eBGP Multihop Time To Live
+ type: int
+ weight:
+ description:
+ - Weight for BGP routes from this neighbor
+ type: int
+ admin_state:
+ description:
+ - Admin state for the BGP session
+ type: str
+ choices: [ enabled, disabled ]
+ allow_self_as_count:
+ description:
+ - Number of allowed self AS.
+ - Only used if C(allow-self-as) is enabled under C(bgp_controls).
+ type: int
+ route_control_profiles:
+ description:
+ - List of dictionaries objects, which is used to bind the BGP Peer Connectivity Profile to Route Control Profile.
+ type: list
+ elements: dict
+ suboptions:
+ tenant:
+ description:
+ - Name of the tenant.
+ type: str
+ required: yes
+ profile:
+ description:
+ - Name of the Route Control Profile.
+ type: str
+ required: yes
+ l3out:
+ description:
+ - Name of the L3 Out.
+ type: str
+ direction:
+ description:
+ - Name of the Route Control Profile direction.
+ type: str
+ required: yes
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(bgp:peerP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new BGP peer on a physical interface
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ peer_ip: 192.168.10.2
+ remote_asn: 65456
+ bgp_controls:
+ - nh-self
+ - send-com
+ - send-ext-com
+ peer_controls:
+ - bfd
+ route_control_profiles:
+ - tenant: "ansible_tenant"
+ profile: "anstest_import"
+ direction: "import"
+ - tenant: "ansible_tenant"
+ profile: "anstest_export"
+ direction: "export"
+ l3out: "anstest_l3out"
+ state: present
+ delegate_to: localhost
+
+- name: Add a new BGP peer on a vPC
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201-202
+ path_ep: my_vpc_ipg
+ peer_ip: 192.168.20.2
+ remote_asn: 65457
+ ttl: 4
+ weight: 50
+ state: present
+ delegate_to: localhost
+
+- name: Shutdown a BGP peer
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ peer_ip: 192.168.10.2
+ admin_state: disabled
+ state: present
+ delegate_to: localhost
+
+- name: Delete a BGP peer
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ peer_ip: 192.168.10.2
+ state: absent
+ delegate_to: localhost
+
+- name: Add BGP Peer to the Node Profile level
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_tenant
+ l3out: ansible_l3out
+ node_profile: ansible_node_profile
+ peer_ip: 192.168.50.3
+ route_control_profiles:
+ - tenant: "ansible_tenant"
+ profile: "anstest_import"
+ direction: "import"
+ - tenant: "ansible_tenant"
+ profile: "anstest_export"
+ direction: "export"
+ l3out: "anstest_l3out"
+ state: present
+
+- name: Query a BGP peer
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ peer_ip: 192.168.10.2
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all BGP peer
+ cisco.aci.aci_l3out_bgp_peer:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_all
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import (
+ ACIModule,
+ aci_argument_spec,
+ aci_annotation_spec,
+ route_control_profile_spec,
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ interface_profile=dict(type="str", aliases=["interface_profile_name", "logical_interface"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ pod_id=dict(type="str"),
+ node_id=dict(type="str"),
+ path_ep=dict(type="str"),
+ peer_ip=dict(type="str"),
+ remote_asn=dict(type="int"),
+ bgp_controls=dict(
+ type="list",
+ elements="str",
+ choices=[
+ "send-com",
+ "send-ext-com",
+ "allow-self-as",
+ "as-override",
+ "dis-peer-as-check",
+ "nh-self",
+ ],
+ ),
+ peer_controls=dict(type="list", elements="str", choices=["bfd", "dis-conn-check"]),
+ address_type_controls=dict(type="list", elements="str", choices=["af-ucast", "af-mcast"]),
+ private_asn_controls=dict(
+ type="list",
+ elements="str",
+ choices=["remove-exclusive", "remove-all", "replace-as"],
+ ),
+ ttl=dict(type="int"),
+ weight=dict(type="int"),
+ admin_state=dict(type="str", choices=["enabled", "disabled"]),
+ allow_self_as_count=dict(type="int"),
+ route_control_profiles=dict(
+ type="list",
+ elements="dict",
+ options=route_control_profile_spec(),
+ ),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l3out", "node_profile", "peer_ip"]],
+ ["state", "present", ["tenant", "l3out", "node_profile", "peer_ip"]],
+ ],
+ required_together=[["interface_profile", "pod_id", "node_id", "path_ep"]],
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ state = module.params.get("state")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ path_ep = module.params.get("path_ep")
+ peer_ip = module.params.get("peer_ip")
+ remote_asn = module.params.get("remote_asn")
+ bgp_controls = module.params.get("bgp_controls")
+ peer_controls = module.params.get("peer_controls")
+ address_type_controls = sorted(module.params.get("address_type_controls") or [])
+ private_asn_controls = module.params.get("private_asn_controls")
+ ttl = module.params.get("ttl")
+ weight = module.params.get("weight")
+ admin_state = module.params.get("admin_state")
+ allow_self_as_count = module.params.get("allow_self_as_count")
+ route_control_profiles = module.params.get("route_control_profiles")
+
+ aci = ACIModule(module)
+ if node_id:
+ if "-" in node_id:
+ path_type = "protpaths"
+ else:
+ path_type = "paths"
+
+ path_dn = "topology/pod-{0}/{1}-{2}/pathep-[{3}]".format(pod_id, path_type, node_id, path_ep)
+
+ child_configs = []
+ child_classes = [
+ "bgpRsPeerPfxPol",
+ "bgpAsP",
+ "bgpLocalAsnP"
+ ]
+
+ if remote_asn:
+ child_configs.append(
+ dict(
+ bgpAsP=dict(
+ attributes=dict(asn=remote_asn),
+ ),
+ )
+ )
+
+ if route_control_profiles:
+ child_classes.append("bgpRsPeerToProfile")
+ for profile in route_control_profiles:
+ if profile.get("l3out"):
+ route_control_profile_dn = "uni/tn-{0}/out-{1}/prof-{2}".format(
+ profile.get("tenant"),
+ profile.get("l3out"),
+ profile.get("profile"),
+ )
+ else:
+ route_control_profile_dn = "uni/tn-{0}/prof-{1}".format(profile.get("tenant"), profile.get("profile"))
+ child_configs.append(
+ dict(
+ bgpRsPeerToProfile=dict(
+ attributes=dict(
+ direction=profile.get("direction"),
+ tDn=route_control_profile_dn,
+ )
+ )
+ )
+ )
+
+ subclass_3 = None
+ subclass_4 = None
+ subclass_5 = None
+
+ bgp_peer_profile_dict = None
+
+ if peer_ip or state == "query":
+ bgp_peer_profile_dict = dict(
+ aci_class="bgpPeerP",
+ aci_rn="peerP-[{0}]".format(peer_ip),
+ module_object=peer_ip,
+ target_filter={"addr": peer_ip},
+ )
+
+ if interface_profile is None:
+ subclass_3 = bgp_peer_profile_dict
+ else:
+ subclass_3 = dict(
+ aci_class="l3extLIfP",
+ aci_rn="lifp-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ )
+ subclass_4 = dict(
+ aci_class="l3extRsPathL3OutAtt",
+ aci_rn="rspathL3OutAtt-[{0}]".format(path_dn),
+ module_object=path_dn,
+ target_filter={"tDn": path_dn},
+ )
+ subclass_5 = bgp_peer_profile_dict
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=subclass_3,
+ subclass_4=subclass_4,
+ subclass_5=subclass_5,
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ ctrl, peerCtrl, addrTCtrl, privateASctrl = None, None, None, None
+ if bgp_controls:
+ ctrl = ",".join(bgp_controls)
+ if peer_controls:
+ peerCtrl = ",".join(peer_controls)
+ if address_type_controls:
+ addrTCtrl = ",".join(address_type_controls)
+ if private_asn_controls:
+ privateASctrl = ",".join(private_asn_controls)
+ aci.payload(
+ aci_class="bgpPeerP",
+ class_config=dict(
+ addr=peer_ip,
+ ctrl=ctrl,
+ peerCtrl=peerCtrl,
+ addrTCtrl=addrTCtrl,
+ privateASctrl=privateASctrl,
+ ttl=ttl,
+ weight=weight,
+ adminSt=admin_state,
+ allowedSelfAsCnt=allow_self_as_count,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="bgpPeerP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py
new file mode 100644
index 00000000..f785937d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg.py
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_l3out_extepg
+short_description: Manage External Network Instance Profile (ExtEpg) objects (l3extInstP:instP)
+description:
+- Manage External Network Instance Profile (ExtEpg) objects (l3extInstP:instP)
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ extepg:
+ description:
+ - Name of ExtEpg being created.
+ type: str
+ aliases: [ extepg_name, name ]
+ description:
+ description:
+ - Description for the ExtEpg.
+ type: str
+ aliases: [ descr ]
+ preferred_group:
+ description:
+ - Whether ot not the EPG is part of the Preferred Group and can communicate without contracts.
+ - This is very convenient for migration scenarios, or when ACI is used for network automation but not for policy.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ dscp:
+ description:
+ - The target Differentiated Service (DSCP) value.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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 ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(domain) and C(vrf) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_domain) and M(cisco.aci.aci_vrf) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vrf
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:Out).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Rostyslav Davydenko (@rost-d)
+"""
+
+EXAMPLES = r"""
+- name: Add a new ExtEpg
+ cisco.aci.aci_l3out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ l3out: prod_l3out
+ name: prod_extepg
+ description: ExtEpg for Production L3Out
+ state: present
+ delegate_to: localhost
+
+- name: Delete ExtEpg
+ cisco.aci.aci_l3out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ l3out: prod_l3out
+ name: prod_extepg
+ state: absent
+ delegate_to: localhost
+
+- name: Query ExtEpg information
+ cisco.aci.aci_l3out_extepg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ l3out: prod_l3out
+ name: prod_extepg
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ l3out=dict(type="str", aliases=["l3out_name"]), # Not required for querying all objects
+ extepg=dict(type="str", aliases=["extepg_name", "name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ preferred_group=dict(type="bool"),
+ dscp=dict(
+ type="str",
+ 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"],
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["extepg", "l3out", "tenant"]],
+ ["state", "absent", ["extepg", "l3out", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ extepg = module.params.get("extepg")
+ description = module.params.get("description")
+ preferred_group = aci.boolean(module.params.get("preferred_group"), "include", "exclude")
+ dscp = module.params.get("dscp")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extInstP",
+ aci_rn="instP-{0}".format(extepg),
+ module_object=extepg,
+ target_filter={"name": extepg},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l3extInstP",
+ class_config=dict(
+ name=extepg,
+ descr=description,
+ prefGrMemb=preferred_group,
+ targetDscp=dscp,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="l3extInstP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py
new file mode 100644
index 00000000..5d516f2a
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extepg_to_contract.py
@@ -0,0 +1,347 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1)
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_extepg_to_contract
+short_description: Bind Contracts to External End Point Groups (EPGs)
+description:
+- Bind Contracts to External End Point Groups (EPGs) on ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of existing tenant.
+ type: str
+ l3out:
+ description:
+ - Name of the l3out.
+ type: str
+ aliases: ['l3out_name']
+ extepg:
+ description:
+ - Name of the external end point group.
+ type: str
+ aliases: ['extepg_name', 'external_epg']
+ contract:
+ description:
+ - Name of the contract.
+ type: str
+ contract_type:
+ description:
+ - The type of contract.
+ type: str
+ required: yes
+ choices: ['consumer', 'provider']
+ priority:
+ description:
+ - This has four levels of priority.
+ type: str
+ choices: ['level1', 'level2', 'level3', 'unspecified']
+ provider_match:
+ description:
+ - This is configurable for provided contracts.
+ type: str
+ choices: ['all', 'at_least_one', 'at_most_one', 'none']
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(l3out) and C(extepg) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l3out) and M(cisco.aci.aci_l3out_extepg) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fvtenant), B(l3extInstP) and B(l3extOut).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Sudhakar Shet Kudtarkar (@kudtarkar1)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Bind a contract to an external EPG
+ cisco.aci.aci_l3out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l3out: l3out
+ extepg : testEpg
+ contract: contract1
+ contract_type: provider
+ state: present
+ delegate_to: localhost
+
+- name: Remove existing contract from an external EPG
+ cisco.aci.aci_l3out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: Auto-Demo
+ l3out: l3out
+ extepg : testEpg
+ contract: contract1
+ contract_type: provider
+ state: absent
+ delegate_to: localhost
+
+- name: Query a contract bound to an external EPG
+ cisco.aci.aci_l3out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ tenant: ansible_tenant
+ l3out: ansible_l3out
+ extepg: ansible_extEpg
+ contract: ansible_contract
+ contract_type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all contracts relationships
+ cisco.aci.aci_l3out_extepg_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretePassword
+ contract_type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+ACI_CLASS_MAPPING = dict(
+ consumer={
+ "class": "fvRsCons",
+ "rn": "rscons-",
+ },
+ provider={
+ "class": "fvRsProv",
+ "rn": "rsprov-",
+ },
+)
+
+PROVIDER_MATCH_MAPPING = dict(
+ all="All",
+ at_least_one="AtleastOne",
+ at_most_one="tmostOne",
+ none="None",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ contract_type=dict(type="str", required=True, choices=["consumer", "provider"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ contract=dict(type="str"),
+ 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"),
+ extepg=dict(type="str", aliases=["extepg_name", "external_epg"]),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["extepg", "contract", "l3out", "tenant"]],
+ ["state", "present", ["extepg", "contract", "l3out", "tenant"]],
+ ],
+ )
+
+ l3out = module.params.get("l3out")
+ contract = module.params.get("contract")
+ contract_type = module.params.get("contract_type")
+ extepg = module.params.get("extepg")
+ priority = module.params.get("priority")
+ provider_match = module.params.get("provider_match")
+ if provider_match is not None:
+ provider_match = PROVIDER_MATCH_MAPPING.get(provider_match)
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+
+ aci_class = ACI_CLASS_MAPPING.get(contract_type)["class"]
+ aci_rn = ACI_CLASS_MAPPING.get(contract_type)["rn"]
+
+ if contract_type == "consumer" and provider_match is not None:
+ module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extInstP",
+ aci_rn="instP-{0}".format(extepg),
+ module_object=extepg,
+ target_filter={"name": extepg},
+ ),
+ subclass_3=dict(
+ aci_class=aci_class,
+ aci_rn="{0}{1}".format(aci_rn, contract),
+ module_object=contract,
+ target_filter={"tnVzBrCPName": contract},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(
+ matchT=provider_match,
+ prio=priority,
+ tnVzBrCPName=contract,
+ ),
+ )
+
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py
new file mode 100644
index 00000000..e42bf035
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_extsubnet.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_l3out_extsubnet
+short_description: Manage External Subnet objects (l3extSubnet:extsubnet)
+description:
+- Manage External Subnet objects (l3extSubnet:extsubnet)
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ required: yes
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ required: yes
+ extepg:
+ description:
+ - Name of an existing ExtEpg.
+ type: str
+ aliases: [ extepg_name ]
+ required: yes
+ network:
+ description:
+ - The network address for the Subnet.
+ type: str
+ aliases: [ address, ip ]
+ subnet_name:
+ description:
+ - Name of External Subnet being created.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - Description for the External Subnet.
+ type: str
+ aliases: [ descr ]
+ scope:
+ description:
+ - Determines the scope of the Subnet.
+ - The C(export-rtctrl) option controls which external networks are advertised out of the fabric using route-maps and IP prefix-lists.
+ - The C(import-rtctrl) option controls which external networks are advertised in to the fabric using route-maps and IP prefix-lists.
+ - The C(import-security) option classifies for the external EPG.
+ The rules and contracts defined in this external EPG apply to networks matching this subnet.
+ - The C(shared-rtctrl) option controls which external prefixes are advertised to other tenants for shared services.
+ - The C(shared-security) option configures the classifier for the subnets in the VRF where the routes are leaked.
+ - The APIC defaults to C(import-security) when unset during creation.
+ - The C(import-rtctrl) is only supported for BGP and OSPF.
+ type: list
+ elements: str
+ choices: [ export-rtctrl, import-rtctrl, import-security, shared-rtctrl, shared-security ]
+ aggregate:
+ description:
+ - Determines the Aggregate Routes for the Subnet.
+ - The C(export-rtctrl) option to export all transit routes of a VRF (0/0 subnets).
+ - The C(import-rtctrl) option to import all incoming routes of given L3 peers (0/0 subnets).
+ - The C(shared-rtctrl) option to share routes learned in one VRF which needs to be advertised to another VRF.
+ 0/0 can be used to share all subnet routes across multiple VRFs.
+ - The C(import-rtctrl) is only supported for BGP and OSPF.
+ - Aggregate import route control is only available if the L3Out has 'Import Route Control Enforcement' enabled.
+ Default this is disabled, M(cisco.aci.aci_l3out) with C(route_control) can be used to enable.
+ type: list
+ elements: str
+ choices: [ export-rtctrl, import-rtctrl, shared-rtctrl]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) and C(domain) and C(vrf) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_domain) and M(cisco.aci.aci_vrf) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vrf
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:Out).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Rostyslav Davydenko (@rost-d)
+- Cindy Zhao (@cizhao)
+"""
+
+EXAMPLES = r"""
+- name: Add a new External Subnet
+ cisco.aci.aci_l3out_extsubnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ l3out: prod_l3out
+ extepg: prod_extepg
+ description: External Subnet for Production ExtEpg
+ network: 192.0.2.0/24
+ scope: export-rtctrl
+ aggregate: export-rtctrl
+ state: present
+ delegate_to: localhost
+
+- name: Delete External Subnet
+ cisco.aci.aci_l3out_extsubnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ l3out: prod_l3out
+ extepg: prod_extepg
+ network: 192.0.2.0/24
+ state: absent
+ delegate_to: localhost
+
+- name: Query ExtEpg Subnet information
+ cisco.aci.aci_l3out_extsubnet:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ l3out: prod_l3out
+ extepg: prod_extepg
+ network: 192.0.2.0/24
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", required=True, aliases=["tenant_name"]),
+ l3out=dict(type="str", required=True, aliases=["l3out_name"]),
+ extepg=dict(type="str", required=True, aliases=["extepg_name", "name"]),
+ network=dict(type="str", aliases=["address", "ip"]),
+ description=dict(type="str", aliases=["descr"]),
+ subnet_name=dict(type="str", aliases=["name"]),
+ scope=dict(type="list", elements="str", choices=["import-security", "export-rtctrl", "import-rtctrl", "shared-rtctrl", "shared-security"]),
+ aggregate=dict(type="list", elements="str", choices=["export-rtctrl", "import-rtctrl", "shared-rtctrl"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["network"]],
+ ["state", "absent", ["network"]],
+ ],
+ required_by={"aggregate": "scope"}
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ extepg = module.params.get("extepg")
+ network = module.params.get("network")
+ description = module.params.get("description")
+ subnet_name = module.params.get("subnet_name")
+ scope = module.params.get("scope")
+ aggregate = module.params.get("aggregate")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ # Validation rule to only allow aggregate choice when there is a match in scope choice
+ if aggregate and not set(aggregate).issubset(scope):
+ aci.module.fail_json(msg="All aggregate values {0} need to be defined in scope {1}.".format(aggregate, scope))
+
+ class_config = dict(ip=network, descr=description, name=subnet_name, nameAlias=name_alias)
+ if scope:
+ class_config['scope'] = ",".join(sorted(scope))
+ if aggregate:
+ class_config['aggregate'] = ",".join(sorted(aggregate))
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extInstP",
+ aci_rn="instP-{0}".format(extepg),
+ module_object=extepg,
+ target_filter={"name": extepg},
+ ),
+ subclass_3=dict(
+ aci_class="l3extSubnet",
+ aci_rn="extsubnet-[{0}]".format(network),
+ module_object=network,
+ target_filter={"name": network},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="l3extSubnet", class_config=class_config)
+
+ aci.get_diff(aci_class="l3extSubnet")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface.py
new file mode 100644
index 00000000..c0ffa675
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface.py
@@ -0,0 +1,409 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_interface
+short_description: Manage Layer 3 Outside (L3Out) interfaces (l3ext:RsPathL3OutAtt)
+description:
+- Manage L3Out interfaces on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ required: yes
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ required: yes
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ required: yes
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ interface_profile_name, logical_interface ]
+ required: yes
+ pod_id:
+ description:
+ - Pod to build the interface on.
+ type: str
+ node_id:
+ description:
+ - Node to build the interface on for Port-channels and single ports.
+ - Hyphen separated pair of nodes (e.g. "201-202") for vPCs.
+ type: str
+ path_ep:
+ description:
+ - Path to interface
+ - Interface Policy Group name for Port-channels and vPCs
+ - Port number for single ports (e.g. "eth1/12")
+ type: str
+ encap:
+ description:
+ - encapsulation on the interface (e.g. "vlan-500")
+ type: str
+ address:
+ description:
+ - IP address.
+ type: str
+ aliases: [ addr, ip_address]
+ mtu:
+ description:
+ - Interface MTU.
+ type: str
+ ipv6_dad:
+ description:
+ - IPv6 DAD feature.
+ type: str
+ choices: [ enabled, disabled]
+ interface_type:
+ description:
+ - Type of interface to build.
+ type: str
+ choices: [ l3-port, sub-interface, ext-svi ]
+ mode:
+ description:
+ - Interface mode, only used if instance_type is ext-svi
+ type: str
+ choices: [ regular, native, untagged ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ auto_state:
+ description:
+ - SVI auto state.
+ type: str
+ choices: [ enabled, disabled ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:RsPathL3OutAtt)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new routed interface
+ cisco.aci.aci_l3out_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ interface_type: l3-port
+ address: 192.168.10.1/27
+ state: present
+ delegate_to: localhost
+
+- name: Add a new SVI vPC
+ cisco.aci.aci_l3out_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201-202
+ path_ep: my_vpc_ipg
+ interface_type: ext-svi
+ encap: vlan-800
+ mode: regular
+ state: present
+ delegate_to: localhost
+
+- name: Delete an interface
+ cisco.aci.aci_l3out_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ state: absent
+ delegate_to: localhost
+
+- name: Query an interface
+ cisco.aci.aci_l3out_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"], required=True),
+ l3out=dict(type="str", aliases=["l3out_name"], required=True),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"], required=True),
+ interface_profile=dict(type="str", aliases=["interface_profile_name", "logical_interface"], required=True),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ pod_id=dict(type="str"),
+ node_id=dict(type="str"),
+ path_ep=dict(type="str"),
+ address=dict(type="str", aliases=["addr", "ip_address"]),
+ mtu=dict(type='str'),
+ ipv6_dad=dict(type="str", choices=["enabled", "disabled"]),
+ interface_type=dict(type="str", choices=["l3-port", "sub-interface", "ext-svi"]),
+ mode=dict(type="str", choices=["regular", "native", "untagged"]),
+ encap=dict(type="str"),
+ auto_state=dict(type="str", choices=["enabled", "disabled"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["interface_type", "pod_id", "node_id", "path_ep"]],
+ ["state", "absent", ["pod_id", "node_id", "path_ep"]]
+ ]
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ state = module.params.get("state")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ path_ep = module.params.get("path_ep")
+ address = module.params.get("address")
+ mtu = module.params.get('mtu')
+ ipv6_dad = module.params.get("ipv6_dad")
+ interface_type = module.params.get("interface_type")
+ mode = module.params.get("mode")
+ encap = module.params.get("encap")
+ auto_state = module.params.get("auto_state")
+
+ aci = ACIModule(module)
+ if node_id and "-" in node_id:
+ path_type = "protpaths"
+ else:
+ path_type = "paths"
+
+ path_dn = None
+ if pod_id and node_id and path_ep:
+ path_dn = "topology/pod-{0}/{1}-{2}/pathep-[{3}]".format(pod_id, path_type, node_id, path_ep)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l3extLIfP",
+ aci_rn="lifp-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_4=dict(
+ aci_class='l3extRsPathL3OutAtt',
+ aci_rn='rspathL3OutAtt-[{0}]'.format(path_dn),
+ module_object=path_dn,
+ target_filter={'tDn': path_dn}
+ )
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l3extRsPathL3OutAtt",
+ class_config=dict(
+ tDn=path_dn,
+ addr=address,
+ ipv6Dad=ipv6_dad,
+ mtu=mtu,
+ ifInstT=interface_type,
+ mode=mode,
+ encap=encap,
+ autostate=auto_state
+ ),
+ )
+
+ aci.get_diff(aci_class="l3extRsPathL3OutAtt")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface_secondary_ip.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface_secondary_ip.py
new file mode 100644
index 00000000..d8311dad
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_interface_secondary_ip.py
@@ -0,0 +1,405 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_interface_secondary_ip
+short_description: Manage Layer 3 Outside (L3Out) interface secondary IP addresses (l3ext:Ip).
+description:
+- Manage Layer 3 Outside (L3Out) interface secondary IP addresses (l3ext:Ip).
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ interface_profile_name, logical_interface ]
+ pod_id:
+ description:
+ - Pod to build the interface on.
+ type: str
+ node_id:
+ description:
+ - Node to build the interface on for Port-channels and single ports.
+ - Hyphen separated pair of nodes (e.g. "201-202") for vPCs.
+ type: str
+ path_ep:
+ description:
+ - Path to interface
+ - Interface Policy Group name for Port-channels and vPCs
+ - Port number for single ports (e.g. "eth1/12")
+ type: str
+ side:
+ description:
+ - Provides the side for vPC member interfaces.
+ type: str
+ choices: [ A, B ]
+ address:
+ description:
+ - Secondary IP address.
+ type: str
+ aliases: [ addr, ip_address]
+ ipv6_dad:
+ description:
+ - IPv6 DAD feature.
+ type: str
+ choices: [ enabled, disabled]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- This is a test
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- module: aci_l3out_logical_interface_profile
+- module: aci_l3out_logical_interface
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:RsPathL3OutAtt)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new secondary IP to a routed interface
+ cisco.aci.aci_l3out_interface_secondary_ip:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ address: 192.168.10.2/27
+ state: present
+ delegate_to: localhost
+
+- name: Add a new secondary IP to a vPC member
+ cisco.aci.aci_l3out_interface_secondary_ip:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201-202
+ path_ep: my_vpc_ipg
+ side: A
+ address: 192.168.10.2/27
+ state: present
+ delegate_to: localhost
+
+- name: Delete a secondary IP
+ cisco.aci.aci_l3out_interface_secondary_ip:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ address: 192.168.10.2/27
+ state: absent
+ delegate_to: localhost
+
+- name: Query a secondary IP
+ cisco.aci.aci_l3out_interface_secondary_ip:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ address: 192.168.10.2/27
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ interface_profile=dict(type="str", aliases=["interface_profile_name", "logical_interface"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ pod_id=dict(type="str"),
+ node_id=dict(type="str"),
+ path_ep=dict(type="str"),
+ side=dict(type="str", choices=["A", "B"]),
+ address=dict(type="str", aliases=["addr", "ip_address"]),
+ ipv6_dad=dict(type="str", choices=["enabled", "disabled"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ [
+ "state",
+ "absent",
+ [
+ "tenant",
+ "l3out",
+ "node_profile",
+ "interface_profile",
+ "pod_id",
+ "node_id",
+ "path_ep",
+ ],
+ ],
+ [
+ "state",
+ "present",
+ [
+ "tenant",
+ "l3out",
+ "node_profile",
+ "interface_profile",
+ "pod_id",
+ "node_id",
+ "path_ep",
+ ],
+ ],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ path_ep = module.params.get("path_ep")
+ side = module.params.get("side")
+ address = module.params.get("address")
+ ipv6_dad = module.params.get("ipv6_dad")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ path_type = "paths"
+ rn_prefix = ""
+
+ if node_id:
+ if "-" in node_id:
+ path_type = "protpaths"
+ rn_prefix = "mem-{0}/".format(side)
+
+ path_dn = None
+ if pod_id and node_id and path_ep:
+ path_dn = "topology/pod-{0}/{1}-{2}/pathep-[{3}]".format(pod_id, path_type, node_id, path_ep)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l3extLIfP",
+ aci_rn="lifp-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_4=dict(
+ aci_class="l3extRsPathL3OutAtt",
+ aci_rn="rspathL3OutAtt-[{0}]".format(path_dn),
+ module_object=path_dn,
+ target_filter={"tDn": path_dn},
+ ),
+ subclass_5=dict(
+ aci_class="l3extIp",
+ aci_rn="{0}addr-[{1}]".format(rn_prefix, address),
+ module_object=address,
+ target_filter={"addr": address},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="l3extIp", class_config=dict(addr=address, ipv6Dad=ipv6_dad))
+
+ aci.get_diff(aci_class="l3extIp")
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile.py
new file mode 100644
index 00000000..a87cb664
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile.py
@@ -0,0 +1,315 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_logical_interface_profile
+short_description: Manage Layer 3 Outside (L3Out) logical interface profiles (l3ext:LIfP)
+description:
+- Manage L3Out interface profiles on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ name, interface_profile_name, logical_interface ]
+ nd_policy:
+ description:
+ - Name of the neighbor discovery interface policy.
+ type: str
+ egress_dpp_policy:
+ description:
+ - Name of the egress data plane policing policy.
+ type: str
+ ingress_dpp_policy:
+ description:
+ - Name of the ingress data plane policing policy.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new interface profile
+ cisco.aci.aci_l3out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ state: present
+ delegate_to: localhost
+
+- name: Delete an interface profile
+ cisco.aci.aci_l3out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ state: absent
+ delegate_to: localhost
+
+- name: Query an interface profile
+ cisco.aci.aci_l3out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all interface profiles
+ cisco.aci.aci_l3out_logical_interface_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ interface_profile=dict(type="str", aliases=["name", "interface_profile_name", "logical_interface"]),
+ nd_policy=dict(type="str", default=""),
+ egress_dpp_policy=dict(type="str", default=""),
+ ingress_dpp_policy=dict(type="str", default=""),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l3out", "node_profile", "interface_profile"]],
+ ["state", "present", ["tenant", "l3out", "node_profile", "interface_profile"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ nd_policy = module.params.get("nd_policy")
+ egress_dpp_policy = module.params.get("egress_dpp_policy")
+ ingress_dpp_policy = module.params.get("ingress_dpp_policy")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l3extLIfP",
+ aci_rn="lifp-[{0}]".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ child_classes=["l3extRsNdIfPol", "l3extRsIngressQosDppPol", "l3extRsEgressQosDppPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = [
+ dict(l3extRsNdIfPol=dict(attributes=dict(tnNdIfPolName=nd_policy))),
+ dict(l3extRsIngressQosDppPol=dict(attributes=dict(tnQosDppPolName=ingress_dpp_policy))),
+ dict(l3extRsEgressQosDppPol=dict(attributes=dict(tnQosDppPolName=egress_dpp_policy))),
+ ]
+ aci.payload(aci_class="l3extLIfP", class_config=dict(name=interface_profile), child_configs=child_configs)
+
+ aci.get_diff(aci_class="l3extLIfP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile_ospf_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile_ospf_policy.py
new file mode 100644
index 00000000..2282e942
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_profile_ospf_policy.py
@@ -0,0 +1,344 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Jason Juenger (@jasonjuenger) <jasonjuenger@gmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_logical_interface_profile_ospf_policy
+short_description: Manage Layer 3 Outside (L3Out) logical interface profile (l3ext:LIfP) OSPF policy (ospfIfP)
+description:
+- Manage L3Out interface profile OSPF policies on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of an existing interface profile.
+ type: str
+ aliases: [ name, interface_profile_name, logical_interface ]
+ ospf_policy:
+ description:
+ - Name of an existing OSPF interface policy.
+ type: str
+ aliases: [ name, ospf_policy_name ]
+ ospf_auth_type:
+ description:
+ - OSPF authentication type. The value C(default) represents the "No Authentication" setting.
+ type: str
+ choices: [ default, simple, md5 ]
+ ospf_auth_key:
+ description:
+ - OSPF authentication key.
+ - When using C(ospf_auth_key) this module will always show as C(changed) as the module cannot know what the currently configured key is.
+ type: str
+ default: ""
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jason Juenger (@jasonjuenger)
+"""
+
+EXAMPLES = r"""
+- name: Add a new interface profile OSPF policy
+ cisco.aci.aci_l3out_logical_interface_profile_ospf_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ ospf_policy: my_ospf_interface_policy
+ state: present
+ delegate_to: localhost
+
+- name: Add a new interface profile OSPF policy with authentication
+ cisco.aci.aci_l3out_logical_interface_profile_ospf_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ ospf_policy: my_ospf_interface_policy
+ ospf_auth_type: simple
+ ospf_auth_key: my_auth_key
+ state: present
+ delegate_to: localhost
+
+- name: Delete an interface profile OSPF policy
+ cisco.aci.aci_l3out_logical_interface_profile_ospf_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ ospf_policy: my_ospf_interface_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query an interface profile OSPF policy
+ cisco.aci.aci_l3out_logical_interface_profile_ospf_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ interface_profile: my_interface_profile
+ ospf_policy: my_ospf_interface_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ interface_profile=dict(type="str", aliases=["interface_profile_name", "logical_interface"]),
+ ospf_policy=dict(type="str", aliases=["name", "ospf_policy_name"]),
+ ospf_auth_type=dict(type="str", choices=["default", "simple", "md5"]),
+ ospf_auth_key=dict(type="str", no_log=True),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l3out", "node_profile", "interface_profile"]],
+ ["state", "present", ["tenant", "l3out", "node_profile", "interface_profile", "ospf_policy"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ ospf_policy = module.params.get("ospf_policy")
+ ospf_auth_type = module.params.get("ospf_auth_type")
+ ospf_auth_key = module.params.get("ospf_auth_key")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l3extLIfP",
+ aci_rn="lifp-[{0}]".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_4=dict(
+ aci_class="ospfIfP",
+ aci_rn="ospfIfP",
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ child_classes=["ospfRsIfPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = [
+ dict(ospfRsIfPol=dict(attributes=dict(
+ tnOspfIfPolName=ospf_policy
+ )))
+ ]
+
+ config = dict(authType=ospf_auth_type)
+ if ospf_auth_key is not None:
+ config.update(authKey=ospf_auth_key)
+
+ aci.payload(
+ aci_class="ospfIfP",
+ class_config=config,
+ child_configs=child_configs
+ )
+
+ aci.get_diff(aci_class="ospfIfP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py
new file mode 100644
index 00000000..1779d34e
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_interface_vpc_member.py
@@ -0,0 +1,411 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Anvitha Jain(@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_l3out_logical_interface_vpc_member
+short_description: Manage Member Node objects (l3ext:Member)
+description:
+- Manage Member Node objects (l3ext:Member)
+options:
+ description:
+ description:
+ - The description for the logical interface VPC member.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ interface_profile:
+ description:
+ - Name of the interface profile.
+ type: str
+ aliases: [ interface_profile_name, logical_interface ]
+ pod_id:
+ description:
+ - Pod to of the interface.
+ type: str
+ node_id:
+ description:
+ - Hyphen separated pair of nodes (e.g. "201-202")
+ type: str
+ path_ep:
+ description:
+ - vPC Interface Policy Group name
+ type: str
+ path_dn:
+ description:
+ - DN of existing path endpoint (fabricPathEp).
+ type: str
+ side:
+ description:
+ - Provides the side of member.
+ type: str
+ choices: [ A, B ]
+ address:
+ description:
+ - IP address.
+ type: str
+ aliases: [ addr, ip_address]
+ ipv6_dad:
+ description:
+ - IPv6 DAD feature.
+ type: str
+ choices: [ enabled, disabled]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The L3Out vPC inteface used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_l3out_logical_interface_profile) module can be used for this.
+seealso:
+- module: cisco.aci.aci_l3out_logical_interface_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:Out).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Anvitha Jain (@anvitha-jain)
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Create a VPC member based on the path_dn
+ cisco.aci.aci_l3out_logical_interface_vpc_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ node_profile: nodeName
+ interface_profile: interfaceName
+ path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_name]
+ side: A
+ state: present
+ delegate_to: localhost
+
+- name: Create a VPC member based pod, node and path
+ cisco.aci.aci_l3out_logical_interface_vpc_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ node_profile: nodeName
+ interface_profile: interfaceName
+ pod_id: 1
+ node_id: 101-102
+ path_ep: policy_group_name
+ side: A
+ address: 192.168.1.252/24
+ state: present
+ delegate_to: localhost
+
+- name: Delete a VPC member
+ cisco.aci.aci_l3out_logical_interface_vpc_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ node_profile: nodeName
+ interface_profile: interfaceName
+ path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_name]
+ side: A
+ state: absent
+ delegate_to: localhost
+
+- name: Query all VPC members
+ cisco.aci.aci_l3out_logical_interface_vpc_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific VPC member under l3out
+ cisco.aci.aci_l3out_logical_interface_vpc_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ node_profile: nodeName
+ interface_profile: interfaceName
+ path_dn: topology/pod-1/protpaths-101-102/pathep-[policy_group_name]
+ side: A
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ l3out=dict(type="str", aliases=["l3out_name"]), # Not required for querying all objects
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]), # Not required for querying all objects
+ interface_profile=dict(type="str", aliases=["interface_profile_name", "logical_interface"]),
+ path_dn=dict(type="str"),
+ pod_id=dict(type="str"),
+ node_id=dict(type="str"),
+ path_ep=dict(type="str"),
+ side=dict(type="str", choices=["A", "B"]),
+ address=dict(type="str", aliases=["addr", "ip_address"]),
+ ipv6_dad=dict(type="str", choices=["enabled", "disabled"]),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["side", "interface_profile", "node_profile", "l3out", "tenant"]],
+ ["state", "absent", ["side", "interface_profile", "node_profile", "l3out", "tenant"]],
+ ],
+ mutually_exclusive=[
+ ["path_dn", "pod_id"],
+ ["path_dn", "node_id"],
+ ["path_dn", "path_ep"],
+ ],
+ required_together=[
+ ["pod_id", "node_id", "path_ep"],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ interface_profile = module.params.get("interface_profile")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ path_ep = module.params.get("path_ep")
+ path_dn = module.params.get("path_dn")
+ side = module.params.get("side")
+ address = module.params.get("address")
+ ipv6_dad = module.params.get("ipv6_dad")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ if not path_dn:
+ if pod_id and node_id and path_ep:
+ path_dn = ("topology/pod-{0}/protpaths-{1}/pathep-[{2}]".format(pod_id,
+ node_id,
+ path_ep))
+ else:
+ path_dn = None
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l3extLIfP",
+ aci_rn="lifp-{0}".format(interface_profile),
+ module_object=interface_profile,
+ target_filter={"name": interface_profile},
+ ),
+ subclass_4=dict(
+ aci_class="l3extRsPathL3OutAtt",
+ aci_rn="rspathL3OutAtt-[{0}]".format(path_dn),
+ module_object=path_dn,
+ target_filter={"name": path_dn},
+ ),
+ subclass_5=dict(
+ aci_class="l3extMember",
+ aci_rn="mem-{0}".format(side),
+ module_object=side,
+ target_filter={"name": side},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l3extMember",
+ class_config=dict(
+ name=side,
+ addr=address,
+ ipv6Dad=ipv6_dad,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="l3extMember")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node.py
new file mode 100644
index 00000000..2bfc876a
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node.py
@@ -0,0 +1,316 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_logical_node
+short_description: Manage Layer 3 Outside (L3Out) logical node profile nodes (l3ext:RsNodeL3OutAtt)
+description:
+- Bind nodes to node profiles on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ pod_id:
+ description:
+ - Existing podId.
+ type: int
+ node_id:
+ description:
+ - Existing nodeId.
+ type: int
+ router_id:
+ description:
+ - Router ID in dotted decimal notation.
+ type: str
+ router_id_as_loopback:
+ description:
+ - Configure the router ID as a loopback IP.
+ type: str
+ choices: [ 'yes', 'no' ]
+ default: 'yes'
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vmm:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new node to a node profile
+ cisco.aci.aci_l3out_logical_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ pod_id: 1
+ node_id: 111
+ router_id: 111.111.111.111
+ state: present
+ delegate_to: localhost
+
+- name: Delete a node from a node profile
+ cisco.aci.aci_l3out_logical_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ pod_id: 1
+ node_id: 111
+ state: absent
+ delegate_to: localhost
+
+- name: Query a node
+ cisco.aci.aci_l3out_logical_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ pod_id: 1
+ node_id: 111
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all nodes
+ cisco.aci.aci_l3out_logical_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ pod_id=dict(type="int"),
+ node_id=dict(type="int"),
+ router_id=dict(type="str"),
+ router_id_as_loopback=dict(type="str", default="yes", choices=["yes", "no"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l3out", "node_profile", "pod_id", "node_id"]],
+ ["state", "present", ["tenant", "l3out", "node_profile", "pod_id", "node_id"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ router_id = module.params.get("router_id")
+ router_id_as_loopback = module.params.get("router_id_as_loopback")
+ state = module.params.get("state")
+
+ tdn = None
+ if pod_id is not None and node_id is not None:
+ tdn = "topology/pod-{0}/node-{1}".format(pod_id, node_id)
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ subclass_3=dict(
+ aci_class="l3extRsNodeL3OutAtt",
+ aci_rn="rsnodeL3OutAtt-[{0}]".format(tdn),
+ module_object=tdn,
+ target_filter={"name": tdn},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="l3extRsNodeL3OutAtt", class_config=dict(rtrId=router_id, rtrIdLoopBack=router_id_as_loopback, tDn=tdn))
+
+ aci.get_diff(aci_class="l3extRsNodeL3OutAtt")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node_profile.py
new file mode 100644
index 00000000..d3aa5e8f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_logical_node_profile.py
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_logical_node_profile
+short_description: Manage Layer 3 Outside (L3Out) logical node profiles (l3extLNodeP:lnodep)
+description:
+- Manage Layer 3 Outside (L3Out) logical node profiles on Cisco ACI fabrics.
+options:
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, name, logical_node ]
+ description:
+ description:
+ - Description for the node profile.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ dscp:
+ description:
+ - The target Differentiated Service (DSCP) value.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ 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_dscp ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: aci_l3out
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vmm:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jason Juenger (@jasonjuenger)
+"""
+
+EXAMPLES = r"""
+- name: Add a new node profile
+ cisco.aci.aci_l3out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ node_profile: my_node_profile
+ description: node profile for my_l3out
+ l3out: my_l3out
+ tenant: my_tenant
+ dscp: CS0
+ state: present
+ delegate_to: localhost
+
+- name: Delete a node profile
+ cisco.aci.aci_l3out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ node_profile: my_node_profile
+ l3out: my_l3out
+ tenant: my_tenant
+ state: absent
+ delegate_to: localhost
+
+- name: Query a node profile
+ cisco.aci.aci_l3out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ node_profile: my_node_profile
+ l3out: my_l3out
+ tenant: my_tenant
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all node profile for L3out
+ cisco.aci.aci_l3out_logical_node_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ l3out: my_l3out
+ tenant: my_tenant
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ node_profile=dict(type="str", aliases=["name", "node_profile_name", "logical_node"]),
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ description=dict(type="str", aliases=["descr"]),
+ dscp=dict(
+ type="str",
+ 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_dscp"],
+ ),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l3out", "node_profile"]],
+ ["state", "present", ["tenant", "l3out", "node_profile"]],
+ ],
+ )
+
+ node_profile = module.params.get("node_profile")
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ description = module.params.get("description")
+ dscp = module.params.get("dscp")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(node_profile),
+ module_object=node_profile,
+ target_filter={"name": node_profile},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l3extLNodeP",
+ class_config=dict(
+ descr=description,
+ name=node_profile,
+ targetDscp=dscp,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="l3extLNodeP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py
new file mode 100644
index 00000000..d975ddcf
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_route_tag_policy.py
@@ -0,0 +1,289 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_l3out_route_tag_policy
+short_description: Manage route tag policies (l3ext:RouteTagPol)
+description:
+- Manage route tag policies on Cisco ACI fabrics.
+options:
+ rtp:
+ description:
+ - The name of the route tag policy.
+ type: str
+ aliases: [ name, rtp_name ]
+ description:
+ description:
+ - The description for the route tag policy.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ tag:
+ description:
+ - The value of the route tag.
+ - Accepted values range between C(0) and C(4294967295).
+ - The APIC defaults to C(4294967295) when unset during creation.
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:RouteTagPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a l3out route tag policy
+ cisco.aci.aci_l3out_route_tag_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tag: 1000
+ rtp: my_route_tag_policy
+ tenant: production
+ state: present
+ delegate_to: localhost
+
+- name: Delete a l3out route tag policy
+ cisco.aci.aci_l3out_route_tag_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ rtp: my_route_tag_policy
+ tenant: production
+ state: absent
+ delegate_to: localhost
+
+- name: Query all l3out route tag policies
+ cisco.aci.aci_l3out_route_tag_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific l3out route tag policy
+ cisco.aci.aci_l3out_route_tag_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ rtp: my_route_tag_policy
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ rtp=dict(type="str", aliases=["name", "rtp_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ tag=dict(type="int"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["rtp", "tenant"]],
+ ["state", "present", ["rtp", "tenant"]],
+ ],
+ )
+
+ rtp = module.params.get("rtp")
+ description = module.params.get("description")
+ tag = module.params.get("tag")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extRouteTagPol",
+ aci_rn="rttag-{0}".format(rtp),
+ module_object=rtp,
+ target_filter={"name": rtp},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="l3extRouteTagPol",
+ class_config=dict(
+ name=rtp,
+ descr=description,
+ tag=tag,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="l3extRouteTagPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py
new file mode 100644
index 00000000..09a67b8e
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes.py
@@ -0,0 +1,366 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Anvitha Jain(@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_l3out_static_routes
+short_description: Manage Static routes object (l3ext:ipRouteP)
+description:
+- Manage External Subnet objects (l3ext:ipRouteP)
+options:
+ description:
+ description:
+ - The description for the static routes.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ logical_node:
+ description:
+ - Name of an existing logical node profile.
+ type: str
+ aliases: [ node_profile, node_profile_name ]
+ pod_id:
+ description:
+ - Existing podId.
+ type: int
+ node_id:
+ description:
+ - Existing nodeId.
+ type: int
+ prefix:
+ description:
+ - Configure IP and next hop IP for the routed outside network.
+ type: str
+ aliases: [ route ]
+ track_policy:
+ description:
+ - Relation definition for static route to TrackList.
+ type: str
+ preference:
+ description:
+ - Administrative preference value for the route.
+ type: int
+ bfd:
+ description:
+ - Determines if bfd is required for route control.
+ - The APIC defaults to C(None) when unset during creation.
+ type: str
+ choices: [ bfd, None ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(l3out), C(logical_node), C(fabric_node) and C(prefix) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l3out) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l3out
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(l3ext:Out).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Anvitha Jain(@anvitha-jain)
+"""
+
+EXAMPLES = r"""
+- name: Create static routes
+ cisco.aci.aci_l3out_static_routes:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ logical_node: nodeName
+ node_id: 101
+ pod_id: 1
+ prefix: 10.10.0.0/16
+ delegate_to: localhost
+
+- name: Delete static routes
+ cisco.aci.aci_l3out_static_routes:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ logical_node: nodeName
+ node_id: 101
+ pod_id: 1
+ prefix: 10.10.0.0/16
+ delegate_to: localhost
+
+- name: Query for a specific MO under l3out
+ cisco.aci.aci_l3out_static_routes:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: tenantName
+ l3out: l3out
+ logical_node: nodeName
+ node_id: 101
+ pod_id: 1
+ prefix: 10.10.0.0/16
+ delegate_to: localhost
+
+- name: Query for all static routes
+ cisco.aci.aci_l3out_static_routes:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ l3out=dict(type="str", aliases=["l3out_name"]), # Not required for querying all objects
+ logical_node=dict(type="str", aliases=["node_profile", "node_profile_name"]), # Not required for querying all objects
+ pod_id=dict(type="int"),
+ node_id=dict(type="int"),
+ prefix=dict(type="str", aliases=["route"]),
+ track_policy=dict(type="str"),
+ preference=dict(type="int"),
+ bfd=dict(type="str", choices=["bfd", None]),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["prefix", "node_id", "pod_id", "logical_node", "l3out", "tenant"]],
+ ["state", "absent", ["prefix", "node_id", "pod_id", "logical_node", "l3out", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ logical_node = module.params.get("logical_node")
+ node_id = module.params.get("node_id")
+ pod_id = module.params.get("pod_id")
+ prefix = module.params.get("prefix")
+ track_policy = module.params.get("track_policy")
+ preference = module.params.get("preference")
+ bfd = module.params.get("bfd")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ fabric_node = "topology/pod-{0}/node-{1}".format(pod_id, node_id)
+ child_classes = ["ipNexthopP"]
+ if track_policy is not None:
+ child_classes.append("ipRsRouteTrack")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="l3extOut",
+ aci_rn="out-{0}".format(l3out),
+ module_object=l3out,
+ target_filter={"name": l3out},
+ ),
+ subclass_2=dict(
+ aci_class="l3extLNodeP",
+ aci_rn="lnodep-{0}".format(logical_node),
+ module_object=logical_node,
+ target_filter={"name": logical_node},
+ ),
+ subclass_3=dict(
+ aci_class="l3extRsNodeL3OutAtt",
+ aci_rn="rsnodeL3OutAtt-[{0}]".format(fabric_node),
+ module_object=fabric_node,
+ target_filter={"name": fabric_node},
+ ),
+ subclass_4=dict(
+ aci_class="ipRouteP",
+ aci_rn="rt-[{0}]".format(prefix),
+ module_object=prefix,
+ target_filter={"name": prefix},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ class_config = dict(
+ descr=description,
+ ip=prefix,
+ pref=preference,
+ nameAlias=name_alias,
+ )
+ if bfd is not None:
+ class_config["rtCtrl"] = bfd
+
+ if track_policy is not None:
+ tDn = "uni/tn-{0}/tracklist-{1}".format(tenant, track_policy)
+ child_configs.append({"ipRsRouteTrack": {"attributes": {"tDn": tDn}}})
+
+ aci.payload(aci_class="ipRouteP", class_config=class_config, child_configs=child_configs),
+
+ aci.get_diff(aci_class="ipRouteP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes_nexthop.py b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes_nexthop.py
new file mode 100644
index 00000000..36c3afa3
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_l3out_static_routes_nexthop.py
@@ -0,0 +1,306 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_l3out_static_routes_nexthop
+short_description: Manage nexthops for static routes (ip:NexthopP)
+description:
+- Manage nexthops for static routes.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ l3out:
+ description:
+ - Name of an existing L3Out.
+ type: str
+ aliases: [ l3out_name ]
+ node_profile:
+ description:
+ - Name of the node profile.
+ type: str
+ aliases: [ node_profile_name, logical_node ]
+ pod_id:
+ description:
+ - Existing podId.
+ type: int
+ node_id:
+ description:
+ - Existing nodeId.
+ type: int
+ prefix:
+ description:
+ - The IP prefix
+ type: str
+ aliases: [ route ]
+ nexthop:
+ description:
+ - The nexthop for the prefix
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: aci_l3out
+- module: aci_l3out_logical_node_profile
+- module: aci_l3out_logical_node_profile_to_node
+- module: aci_l3out_static_routes
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vmm:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new nexthop to a prefix
+ cisco.aci.aci_l3out_static_routes_nexthop:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ pod_id: 1
+ node_id: 111
+ prefix: 10.84.90.0/24
+ nexthop: 10.1.1.1
+ state: present
+ delegate_to: localhost
+
+- name: Delete a nexthop from a prefix
+ cisco.aci.aci_l3out_static_routes_nexthop:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ pod_id: 1
+ node_id: 111
+ prefix: 10.84.90.0/24
+ nexthop: 10.1.1.1
+ state: absent
+ delegate_to: localhost
+
+- name: Query a nexthop
+ cisco.aci.aci_l3out_static_routes_nexthop:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ l3out: my_l3out
+ node_profile: my_node_profile
+ pod_id: 1
+ node_id: 111
+ prefix: 10.84.90.0/24
+ nexthop: 10.1.1.1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all nexthops
+ cisco.aci.aci_l3out_static_routes_nexthop:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ l3out=dict(type="str", aliases=["l3out_name"]),
+ node_profile=dict(type="str", aliases=["node_profile_name", "logical_node"]),
+ pod_id=dict(type="int"),
+ node_id=dict(type="int"),
+ prefix=dict(type="str", aliases=["route"]),
+ nexthop=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "l3out", "node_profile", "pod_id", "node_id", "prefix", "nexthop"]],
+ ["state", "present", ["tenant", "l3out", "node_profile", "pod_id", "node_id", "prefix", "nexthop"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ l3out = module.params.get("l3out")
+ node_profile = module.params.get("node_profile")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ prefix = module.params.get("prefix")
+ nexthop = module.params.get("nexthop")
+ state = module.params.get("state")
+
+ node_tdn = None
+ if pod_id is not None and node_id is not None:
+ node_tdn = "topology/pod-{0}/node-{1}".format(pod_id, node_id)
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(aci_class="fvTenant", aci_rn="tn-{0}".format(tenant), module_object=tenant, target_filter={"name": tenant}),
+ subclass_1=dict(aci_class="l3extOut", aci_rn="out-{0}".format(l3out), module_object=l3out, target_filter={"name": l3out}),
+ subclass_2=dict(aci_class="l3extLNodeP", aci_rn="lnodep-{0}".format(node_profile), module_object=node_profile, target_filter={"name": node_profile}),
+ subclass_3=dict(
+ aci_class="l3extRsNodeL3OutAtt", aci_rn="rsnodeL3OutAtt-[{0}]".format(node_tdn), module_object=node_tdn, target_filter={"name": node_tdn}
+ ),
+ subclass_4=dict(aci_class="ipRouteP", aci_rn="rt-[{0}]".format(prefix), module_object=prefix, target_filter={"name": prefix}),
+ subclass_5=dict(aci_class="ipNexthopP", aci_rn="nh-[{0}]".format(nexthop), module_object=nexthop, target_filter={"name": nexthop}),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class="ipNexthopP", class_config=dict(nhAddr=nexthop))
+
+ aci.get_diff(aci_class="ipNexthopP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py
new file mode 100644
index 00000000..98b0df9f
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group.py
@@ -0,0 +1,263 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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 = """
+module: aci_maintenance_group
+short_description: This creates an ACI maintenance group
+notes:
+ - a maintenance policy (aci_maintenance_policy must be created prior to creating an aci maintenance group
+description:
+ - This modules creates an ACI maintenance group
+options:
+ group:
+ description:
+ - This is the name of the group
+ type: str
+ policy:
+ description:
+ - This is the name of the policy that was created using aci_maintenance_policy
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [absent, present, query]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+author:
+ - Steven Gerhart (@sgerhart)
+"""
+
+EXAMPLES = r"""
+- name: Create a maintenance group
+ cisco.aci.aci_maintenance_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_maintenance_group
+ policy: my_maintenance_policy
+ state: present
+ delegate_to: localhost
+
+- name: Delete a maintenance group
+ cisco.aci.aci_maintenance_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_maintenance_group
+ state: absent
+ delegate_to: localhost
+
+- name: Query all maintenance groups
+ cisco.aci.aci_maintenance_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific maintenance group
+ cisco.aci.aci_maintenance_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_maintenance_group
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = """
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ group=dict(type="str"), # Not required for querying all objects
+ policy=dict(type="str"), # Not required for querying all objects
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["group"]],
+ ["state", "present", ["group"]],
+ ],
+ )
+
+ state = module.params.get("state")
+ group = module.params.get("group")
+ policy = module.params.get("policy")
+ name_alias = module.params.get("name_alias")
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="maintMaintGrp",
+ aci_rn="fabric/maintgrp-{0}".format(group),
+ target_filter={"name": group},
+ module_object=group,
+ ),
+ child_classes=["maintRsMgrpp"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="maintMaintGrp",
+ class_config=dict(
+ name=group,
+ nameAlias=name_alias,
+ ),
+ child_configs=[
+ dict(
+ maintRsMgrpp=dict(
+ attributes=dict(
+ tnMaintMaintPName=policy,
+ ),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="maintMaintGrp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py b/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py
new file mode 100644
index 00000000..27214341
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_group_node.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python
+
+# GNU General Public License v3.0+ (see LICENSE 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_maintenance_group_node
+short_description: Manage maintenance group nodes
+description:
+- Manage maintenance group nodes
+options:
+ group:
+ description:
+ - The maintenance group name that you want to add the node to.
+ type: str
+ node:
+ description:
+ - The node to be added to the maintenance group.
+ - The value equals the nodeid.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+- Steven Gerhart (@sgerhart)
+"""
+
+EXAMPLES = r"""
+- name: Create a maintenance group node
+ cisco.aci.aci_maintenance_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_maintenance_group
+ node: 1001
+ state: present
+ delegate_to: localhost
+
+- name: Delete a maintenance group node
+ cisco.aci.aci_maintenance_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_maintenance_group
+ node: 1001
+ state: absent
+ delegate_to: localhost
+
+- name: Query all maintenance group nodes
+ cisco.aci.aci_maintenance_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a maintenance group node
+ cisco.aci.aci_maintenance_group_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_maintenance_group
+ node: 1001
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ group=dict(type="str"), # Not required for querying all objects
+ node=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["node", "group"]],
+ ["state", "present", ["node", "group"]],
+ ],
+ )
+
+ state = module.params.get("state")
+ group = module.params.get("group")
+ node = module.params.get("node")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="maintMaintGrp",
+ aci_rn="fabric/maintgrp-{0}".format(group),
+ target_filter={"name": group},
+ module_object=group,
+ ),
+ subclass_1=dict(
+ aci_class="fabricNodeBlk",
+ aci_rn="nodeblk-blk{0}-{0}".format(node),
+ target_filter={"name": "blk{0}-{0}".format(node)},
+ module_object=node,
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricNodeBlk",
+ class_config=dict(
+ from_=node,
+ to_=node,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fabricNodeBlk")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py
new file mode 100644
index 00000000..47cd49ca
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_maintenance_policy.py
@@ -0,0 +1,300 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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_maintenance_policy
+short_description: Manage firmware maintenance policies
+description:
+- Manage maintenance policies that defines behavior during an ACI upgrade.
+options:
+ name:
+ description:
+ - The name for the maintenance policy.
+ type: str
+ aliases: [ maintenance_policy ]
+ runmode:
+ description:
+ - Whether the system pauses on error or just continues through it.
+ type: str
+ choices: [ pauseOnlyOnFailures, pauseNever ]
+ default: pauseOnlyOnFailures
+ graceful:
+ description:
+ - Whether the system will bring down the nodes gracefully during an upgrade, which reduces traffic lost.
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ scheduler:
+ description:
+ - The name of scheduler that is applied to the policy.
+ type: str
+ adminst:
+ description:
+ - Will trigger an immediate upgrade for nodes if adminst is set to triggered.
+ type: str
+ choices: [ triggered, untriggered ]
+ default: untriggered
+ ignoreCompat:
+ description:
+ - To check whether compatibility checks should be ignored
+ - The APIC defaults to C(no) when unset during creation.
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- A scheduler is required for this module, which could have been created using the M(cisco.aci.aci_fabric_scheduler) module or via the UI.
+author:
+- Steven Gerhart (@sgerhart)
+"""
+
+EXAMPLES = r"""
+- name: Create a maintenance policy
+ cisco.aci.aci_maintenance_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_maintenance_policy
+ scheduler: simpleScheduler
+ state: present
+ delegate_to: localhost
+
+- name: Delete a maintenance policy
+ cisco.aci.aci_maintenance_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_maintenance_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query all maintenance policies
+ cisco.aci.aci_maintenance_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific maintenance policy
+ cisco.aci.aci_maintenance_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_maintenance_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["maintenance_policy"]), # Not required for querying all objects
+ runmode=dict(type="str", default="pauseOnlyOnFailures", choices=["pauseOnlyOnFailures", "pauseNever"]),
+ graceful=dict(type="bool"),
+ scheduler=dict(type="str"),
+ ignoreCompat=dict(type="bool"),
+ adminst=dict(type="str", default="untriggered", choices=["triggered", "untriggered"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name", "scheduler"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ state = module.params.get("state")
+ name = module.params.get("name")
+ runmode = module.params.get("runmode")
+ scheduler = module.params.get("scheduler")
+ adminst = module.params.get("adminst")
+ graceful = aci.boolean(module.params.get("graceful"))
+ ignoreCompat = aci.boolean(module.params.get("ignoreCompat"))
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="maintMaintP",
+ aci_rn="fabric/maintpol-{0}".format(name),
+ target_filter={"name": name},
+ module_object=name,
+ ),
+ child_classes=["maintRsPolScheduler"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="maintMaintP",
+ class_config=dict(
+ name=name,
+ runMode=runmode,
+ graceful=graceful,
+ adminSt=adminst,
+ ignoreCompat=ignoreCompat,
+ nameAlias=name_alias,
+ ),
+ child_configs=[
+ dict(
+ maintRsPolScheduler=dict(
+ attributes=dict(
+ tnTrigSchedPName=scheduler,
+ ),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="maintMaintP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_node_mgmt_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_node_mgmt_epg.py
new file mode 100644
index 00000000..be8fe183
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_node_mgmt_epg.py
@@ -0,0 +1,317 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_node_mgmt_epg
+short_description: In band or Out of band management EPGs
+description:
+- Cisco ACI Fabric Node EPGs
+options:
+ epg:
+ description:
+ - The name of the end point group
+ type: str
+ aliases: [ name ]
+ type:
+ description:
+ - type of management interface
+ type: str
+ choices: [ in_band, out_of_band ]
+ required: true
+ bd:
+ description:
+ - The in-band bridge domain which is used when type is in_band
+ type: str
+ encap:
+ description:
+ - The in-band access encapsulation which is used when type is in_band
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add in band mgmt epg
+ cisco.aci.aci_node_mgmt_epg:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ type: in_band
+ encap: vlan-1
+ bd: bd1
+ state: present
+ delegate_to: localhost
+
+- name: Add out of band mgmt epg
+ cisco.aci.aci_node_mgmt_epg:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ type: out_of_band
+ state: present
+ delegate_to: localhost
+
+- name: Query in band mgmt epg
+ cisco.aci.aci_node_mgmt_epg:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ type: in_band
+ encap: vlan-1
+ bd: bd1
+ state: query
+ delegate_to: localhost
+
+- name: Query all in band mgmt epg
+ cisco.aci.aci_node_mgmt_epg:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ type: in_band
+ state: query
+ delegate_to: localhost
+
+- name: Query all out of band mgmt epg
+ cisco.aci.aci_node_mgmt_epg:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ type: out_of_band
+ state: query
+ delegate_to: localhost
+
+- name: Remove in band mgmt epg
+ cisco.aci.aci_node_mgmt_epg:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ type: in_band
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: class_map (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ type=dict(type="str", choices=["in_band", "out_of_band"], required=True),
+ epg=dict(type="str", aliases=["name"]),
+ bd=dict(type="str"),
+ encap=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, required_if=[["state", "absent", ["epg"]], ["state", "present", ["epg"]]])
+
+ type = module.params.get("type")
+ epg = module.params.get("epg")
+ bd = module.params.get("bd")
+ encap = module.params.get("encap")
+ state = module.params.get("state")
+
+ child_configs = []
+ child_class = []
+ if type == "in_band":
+ child_configs = [
+ dict(
+ mgmtRsMgmtBD=dict(
+ attributes=dict(
+ tnFvBDName=bd,
+ ),
+ ),
+ )
+ ]
+
+ child_class = ["mgmtRsMgmtBD"]
+
+ class_map = dict(
+ in_band=list(
+ [
+ dict(aci_class="mgmtInB", aci_rn="inb-{0}"),
+ ]
+ ),
+ out_of_band=list(
+ [
+ dict(aci_class="mgmtOoB", aci_rn="oob-{0}"),
+ ]
+ ),
+ )
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-mgmt",
+ module_object="mgmt",
+ target_filter={"name": "mgmt"},
+ ),
+ subclass_1=dict(
+ aci_class="mgmtMgmtP",
+ aci_rn="mgmtp-default",
+ module_object="default",
+ target_filter={"name": "default"},
+ ),
+ subclass_2=dict(
+ aci_class=class_map.get(type)[0]["aci_class"],
+ aci_rn=class_map.get(type)[0]["aci_rn"].format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ child_classes=child_class,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=class_map.get(type)[0]["aci_class"],
+ class_config=dict(
+ name=epg,
+ encap=encap,
+ ),
+ child_configs=child_configs,
+ )
+ aci.get_diff(aci_class=class_map.get(type)[0]["aci_class"])
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_ntp_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_ntp_policy.py
new file mode 100644
index 00000000..478a1891
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_ntp_policy.py
@@ -0,0 +1,291 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_ntp_policy
+short_description: Manage NTP policies.
+description:
+- Manage NTP policy (datetimePol) configuration on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - Name of the NTP policy
+ type: str
+ aliases: [ ntp_policy ]
+ description:
+ description:
+ - Description of the NTP policy
+ type: str
+ admin_state:
+ description:
+ - Admin state of the policy
+ type: str
+ choices: [ disabled, enabled ]
+ server_state:
+ description:
+ - Allow switches to act as NTP servers
+ type: str
+ choices: [ disabled, enabled ]
+ auth_state:
+ description:
+ - Enable authentication
+ type: str
+ choices: [ disabled, enabled ]
+ master_mode:
+ description:
+ - Enable master mode. Only applicable if server_state is enabled
+ type: str
+ choices: [ disabled, enabled ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(datetimePol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new NTP policy
+ cisco.aci.aci_ntp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_ntp_policy
+ description: via Ansible
+ admin_state: enabled
+ state: present
+ delegate_to: localhost
+
+- name: Remove a NTP policy
+ cisco.aci.aci_ntp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_ntp_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query a NTP policy
+ cisco.aci.aci_ntp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_ntp_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all NTP policies
+ cisco.aci.aci_ntp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import (
+ ACIModule,
+ aci_argument_spec,
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ name=dict(type="str", aliases=["ntp_policy"]),
+ description=dict(type="str"),
+ admin_state=dict(type="str", choices=["disabled", "enabled"]),
+ server_state=dict(type="str", choices=["disabled", "enabled"]),
+ auth_state=dict(type="str", choices=["disabled", "enabled"]),
+ master_mode=dict(type="str", choices=["disabled", "enabled"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ admin_state = module.params.get("admin_state")
+ server_state = module.params.get("server_state")
+ auth_state = module.params.get("auth_state")
+ master_mode = module.params.get("master_mode")
+ state = module.params.get("state")
+ child_classes = ["datetimeNtpProv"]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="datetimePol",
+ aci_rn="fabric/time-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="datetimePol",
+ class_config=dict(
+ name=name,
+ descr=description,
+ adminSt=admin_state,
+ serverState=server_state,
+ authSt=auth_state,
+ masterMode=master_mode,
+ ),
+ )
+
+ aci.get_diff(aci_class="datetimePol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_ntp_server.py b/ansible_collections/cisco/aci/plugins/modules/aci_ntp_server.py
new file mode 100644
index 00000000..ed30880e
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_ntp_server.py
@@ -0,0 +1,323 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2022, Tim Cragg (@timcragg)
+# GNU General Public License v3.0+ (see LICENSE 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_ntp_server
+short_description: Manage NTP servers.
+description:
+- Manage NTP server (datetimeNtpProv) configuration on Cisco ACI fabrics.
+options:
+ ntp_policy:
+ description:
+ - Name of an existing NTP policy
+ type: str
+ required: yes
+ aliases: [ policy_name ]
+ ntp_server:
+ description:
+ - Name of the NTP server
+ type: str
+ aliases: [ server_name ]
+ description:
+ description:
+ - Description of the NTP server
+ type: str
+ min_poll:
+ description:
+ - Minimum polling interval
+ type: int
+ max_poll:
+ description:
+ - Maximum polling interval
+ type: int
+ preferred:
+ description:
+ - Is this the preferred NTP server
+ type: bool
+ epg_type:
+ description:
+ - Type of management EPG to use to reach the NTP server, inb or oob
+ type: str
+ choices: [ inb, oob ]
+ epg_name:
+ description:
+ - Name of the management EPG to reach the NTP server
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+
+notes:
+- The used C(ntp_policy) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_ntp_policy) module can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(datetimeNtpProv).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new NTP server
+ cisco.aci.aci_ntp_server:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ ntp_policy: my_ntp_policy
+ ntp_server: 10.20.30.40
+ min_poll: 3
+ max_poll: 8
+ preferred: yes
+ state: present
+ delegate_to: localhost
+
+- name: Remove a NTP server
+ cisco.aci.aci_ntp_server:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ ntp_policy: my_ntp_policy
+ ntp_server: 10.20.30.40
+ state: absent
+ delegate_to: localhost
+
+- name: Query a NTP server
+ cisco.aci.aci_ntp_server:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ ntp_policy: my_ntp_policy
+ ntp_server: 10.20.30.40
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all NTP servers within a policy
+ cisco.aci.aci_ntp_server:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ ntp_policy: my_ntp_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import (
+ ACIModule,
+ aci_argument_spec,
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ ntp_policy=dict(type="str", aliases=["policy_name"], required=True),
+ ntp_server=dict(type="str", aliases=["server_name"]),
+ description=dict(type="str"),
+ min_poll=dict(type="int"),
+ max_poll=dict(type="int"),
+ preferred=dict(type="bool"),
+ epg_type=dict(type="str", choices=["inb", "oob"]),
+ epg_name=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["ntp_server"]],
+ ["state", "present", ["ntp_server"]],
+ ],
+ required_together=[
+ ["epg_type", "epg_name"],
+ ],
+ )
+ aci = ACIModule(module)
+
+ ntp_policy = module.params.get("ntp_policy")
+ ntp_server = module.params.get("ntp_server")
+ description = module.params.get("description")
+ min_poll = module.params.get("min_poll")
+ max_poll = module.params.get("max_poll")
+ preferred = aci.boolean(module.params.get("preferred"))
+ epg_type = module.params.get("epg_type")
+ epg_name = module.params.get("epg_name")
+ state = module.params.get("state")
+ child_classes = ["datetimeRsNtpProvToEpg"]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="datetimePol",
+ aci_rn="fabric/time-{0}".format(ntp_policy),
+ module_object=ntp_policy,
+ target_filter={"name": ntp_policy},
+ ),
+ subclass_1=dict(
+ aci_class="datetimeNtpProv",
+ aci_rn="ntpprov-{0}".format(ntp_server),
+ module_object=ntp_server,
+ target_filter={"name": ntp_server},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if epg_type is not None:
+ tdn = "uni/tn-mgmt/mgmtp-default/{0}-{1}".format(epg_type, epg_name)
+ child_configs.append(dict(datetimeRsNtpProvToEpg=dict(attributes=dict(tDn=tdn))))
+ aci.payload(
+ aci_class="datetimeNtpProv",
+ class_config=dict(
+ name=ntp_server,
+ descr=description,
+ maxPoll=max_poll,
+ minPoll=min_poll,
+ preferred=preferred,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="datetimeNtpProv")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_rest.py b/ansible_collections/cisco/aci/plugins/modules/aci_rest.py
new file mode 100644
index 00000000..0e80a874
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_rest.py
@@ -0,0 +1,451 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
+# Copyright: (c) 2020, Cindy Zhao (@cizhao) <cizhao@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+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.
+requirements:
+- lxml (when using XML payload)
+- xmljson >= 0.1.8 (when using XML payload)
+- python 2.7+ (when using xmljson)
+options:
+ method:
+ description:
+ - The HTTP method of the request.
+ - Using C(delete) is typically used for deleting objects.
+ - Using C(get) is typically used for querying objects.
+ - Using C(post) is typically used for modifying objects.
+ type: str
+ choices: [ delete, get, post ]
+ default: get
+ aliases: [ action ]
+ path:
+ description:
+ - URI being used to execute API calls.
+ - Must end in C(.xml) or C(.json).
+ type: str
+ required: yes
+ aliases: [ uri ]
+ content:
+ description:
+ - When used instead of C(src), sets the payload of the API request directly.
+ - This may be convenient to template simple requests.
+ - For anything complex use the C(template) lookup plugin (see examples)
+ or the C(template) module with parameter C(src).
+ type: raw
+ src:
+ description:
+ - Name of the absolute path of the filename that includes the body
+ of the HTTP request being sent to the ACI fabric.
+ - If you require a templated payload, use the C(content) parameter
+ together with the C(template) lookup plugin, or use C(template).
+ type: path
+ aliases: [ config_file ]
+extends_documentation_fragment:
+- cisco.aci.aci
+
+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.
+- If you do not have any attributes, it may be necessary to add the "attributes" key with an empty dictionnary "{}" for value
+ as the APIC does expect the entry to precede any children.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: Cisco APIC REST API Configuration Guide
+ description: More information about the APIC REST API.
+ link: 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:
+- Dag Wieers (@dagwieers)
+- Cindy Zhao (@cizhao)
+"""
+
+EXAMPLES = r"""
+- name: Add a tenant using certificate authentication
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/admin.key
+ method: post
+ path: /api/mo/uni.xml
+ src: /home/cisco/ansible/aci/configs/aci_config.xml
+ delegate_to: localhost
+
+- name: Add a tenant from a templated payload file from templates/
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/admin.key
+ method: post
+ path: /api/mo/uni.xml
+ content: "{{ lookup('template', 'aci/tenant.xml.j2') }}"
+ delegate_to: localhost
+
+- name: Add a tenant using inline YAML
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/admin.key
+ validate_certs: no
+ path: /api/mo/uni.json
+ method: post
+ content:
+ fvTenant:
+ attributes:
+ name: Sales
+ descr: Sales department
+ delegate_to: localhost
+
+- name: Add a tenant using a JSON string
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/admin.key
+ validate_certs: no
+ path: /api/mo/uni.json
+ method: post
+ content:
+ {
+ "fvTenant": {
+ "attributes": {
+ "name": "Sales",
+ "descr": "Sales department"
+ }
+ }
+ }
+ delegate_to: localhost
+
+- name: Add a tenant using an XML string
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/{{ aci_username }}.key
+ validate_certs: no
+ path: /api/mo/uni.xml
+ method: post
+ content: '<fvTenant name="Sales" descr="Sales departement"/>'
+ delegate_to: localhost
+
+- name: Get tenants using password authentication
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ method: get
+ path: /api/node/class/fvTenant.json
+ delegate_to: localhost
+ register: query_result
+
+- name: Configure contracts
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/admin.key
+ method: post
+ path: /api/mo/uni.xml
+ src: /home/cisco/ansible/aci/configs/contract_config.xml
+ delegate_to: localhost
+
+- name: Register leaves and spines
+ cisco.aci.aci_rest:
+ host: apic
+ username: admin
+ private_key: pki/admin.key
+ validate_certs: no
+ method: post
+ path: /api/mo/uni/controller/nodeidentpol.xml
+ content:
+ <fabricNodeIdentPol>
+ <fabricNodeIdentP name="{{ item.name }}" nodeId="{{ item.nodeid }}" status="{{ item.status }}" serial="{{ item.serial }}"/>
+ </fabricNodeIdentPol>
+ with_items:
+ - '{{ apic_leavesspines }}'
+ delegate_to: localhost
+
+- name: Wait for all controllers to become ready
+ cisco.aci.aci_rest:
+ 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")
+ register: apics
+ until: "'totalCount' in apics and apics.totalCount|int >= groups['apic']|count"
+ retries: 120
+ delay: 30
+ delegate_to: localhost
+ run_once: yes
+"""
+
+RETURN = r"""
+error_code:
+ description: The REST ACI return code, useful for troubleshooting on failure
+ returned: always
+ type: int
+ sample: 122
+error_text:
+ description: The REST ACI descriptive text, useful for troubleshooting on failure
+ returned: always
+ type: str
+ sample: unknown managed object class foo
+imdata:
+ description: Converted output returned by the APIC REST (register this for post-processing)
+ returned: always
+ type: str
+ sample: [{"error": {"attributes": {"code": "122", "text": "unknown managed object class foo"}}}]
+payload:
+ description: The (templated) payload send to the APIC REST API (xml or json)
+ returned: always
+ type: str
+ sample: '<foo bar="boo"/>'
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class foo"/></imdata>'
+response:
+ description: HTTP response string
+ returned: always
+ type: str
+ sample: 'HTTP Error 400: Bad Request'
+status:
+ description: HTTP status code
+ returned: always
+ type: int
+ sample: 400
+totalCount:
+ description: Number of items in the imdata array
+ returned: always
+ type: str
+ sample: '0'
+url:
+ description: URL used for APIC REST call
+ returned: success
+ type: str
+ sample: https://1.2.3.4/api/mo/uni/tn-[Dag].json?rsp-subtree=modified
+"""
+
+import json
+import os
+
+try:
+ from ansible.module_utils.six.moves.urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
+
+ HAS_URLPARSE = True
+except Exception:
+ HAS_URLPARSE = False
+
+# Optional, only used for XML payload
+try:
+ from lxml import etree # noqa
+
+ HAS_LXML_ETREE = True
+except ImportError:
+ HAS_LXML_ETREE = False
+
+# Optional, only used for XML payload
+try:
+ from xmljson import cobra # noqa
+
+ HAS_XMLJSON_COBRA = True
+except ImportError:
+ HAS_XMLJSON_COBRA = False
+
+# Optional, only used for YAML validation
+try:
+ import yaml
+
+ HAS_YAML = True
+except Exception:
+ HAS_YAML = False
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
+from ansible.module_utils.urls import fetch_url
+from ansible.module_utils._text import to_text
+
+
+def update_qsl(url, params):
+ """Add or update a URL query string"""
+
+ if HAS_URLPARSE:
+ url_parts = list(urlparse(url))
+ query = dict(parse_qsl(url_parts[4]))
+ query.update(params)
+ url_parts[4] = urlencode(query)
+ return urlunparse(url_parts)
+ elif "?" in url:
+ return url + "&" + "&".join(["%s=%s" % (k, v) for k, v in params.items()])
+ else:
+ return url + "?" + "&".join(["%s=%s" % (k, v) for k, v in params.items()])
+
+
+class ACIRESTModule(ACIModule):
+ def changed(self, d):
+ """Check ACI response for changes"""
+
+ if isinstance(d, dict):
+ for k, v in d.items():
+ if k == "status" and v in ("created", "modified", "deleted"):
+ return True
+ elif self.changed(v) is True:
+ return True
+ elif isinstance(d, list):
+ for i in d:
+ if self.changed(i) is True:
+ return True
+
+ return False
+
+ def response_type(self, rawoutput, rest_type="xml"):
+ """Handle APIC response output"""
+
+ if rest_type == "json":
+ self.response_json(rawoutput)
+ else:
+ self.response_xml(rawoutput)
+
+ # Use APICs built-in idempotency
+ if HAS_URLPARSE:
+ self.result["changed"] = self.changed(self.imdata)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ path=dict(type="str", required=True, aliases=["uri"]),
+ method=dict(type="str", default="get", choices=["delete", "get", "post"], aliases=["action"]),
+ src=dict(type="path", aliases=["config_file"]),
+ content=dict(type="raw"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[["content", "src"]],
+ )
+
+ content = module.params.get("content")
+ path = module.params.get("path")
+ src = module.params.get("src")
+
+ # Report missing file
+ file_exists = False
+ if src:
+ if os.path.isfile(src):
+ file_exists = True
+ else:
+ module.fail_json(msg="Cannot find/access src '%s'" % src)
+
+ # Find request type
+ if path.find(".xml") != -1:
+ rest_type = "xml"
+ if not HAS_LXML_ETREE:
+ module.fail_json(msg="The lxml python library is missing, or lacks etree support.")
+ if not HAS_XMLJSON_COBRA:
+ module.fail_json(msg="The xmljson python library is missing, or lacks cobra support.")
+ elif path.find(".json") != -1:
+ rest_type = "json"
+ else:
+ module.fail_json(msg="Failed to find REST API payload type (neither .xml nor .json).")
+
+ aci = ACIRESTModule(module)
+ aci.result["status"] = -1 # Ensure we always return a status
+
+ # We include the payload as it may be templated
+ payload = content
+ if file_exists:
+ with open(src, "r") as config_object:
+ # TODO: Would be nice to template this, requires action-plugin
+ payload = config_object.read()
+
+ # Validate payload
+ if rest_type == "json":
+ if content and isinstance(content, dict):
+ # Validate inline YAML/JSON
+ payload = json.dumps(payload)
+ elif payload and isinstance(payload, str) and HAS_YAML:
+ try:
+ # Validate YAML/JSON string
+ payload = json.dumps(yaml.safe_load(payload))
+ except Exception as e:
+ module.fail_json(msg="Failed to parse provided JSON/YAML payload: %s" % to_text(e), exception=to_text(e), payload=payload)
+ elif rest_type == "xml" and HAS_LXML_ETREE:
+ if content and isinstance(content, dict) and HAS_XMLJSON_COBRA:
+ # Validate inline YAML/JSON
+ payload = etree.tostring(cobra.etree(payload)[0])
+ elif payload and isinstance(payload, str):
+ try:
+ # Validate XML string
+ payload = etree.tostring(etree.fromstring(payload))
+ except Exception as e:
+ module.fail_json(msg="Failed to parse provided XML payload: %s" % to_text(e), payload=payload)
+
+ # Perform actual request using auth cookie (Same as aci.request(), but also supports XML)
+ if "port" in aci.params and aci.params.get("port") is not None:
+ aci.url = "%(protocol)s://%(host)s:%(port)s/" % aci.params + path.lstrip("/")
+ else:
+ aci.url = "%(protocol)s://%(host)s/" % aci.params + path.lstrip("/")
+ if aci.params.get("method") != "get":
+ path += "?rsp-subtree=modified"
+ aci.url = update_qsl(aci.url, {"rsp-subtree": "modified"})
+
+ # Sign and encode request as to APIC's wishes
+ if aci.params.get("private_key") is not None:
+ aci.cert_auth(path=path, payload=payload)
+
+ aci.method = aci.params.get("method").upper()
+
+ # Perform request
+ resp, info = fetch_url(
+ module, aci.url, data=payload, headers=aci.headers, method=aci.method, timeout=aci.params.get("timeout"), use_proxy=aci.params.get("use_proxy")
+ )
+
+ aci.response = info.get("msg")
+ aci.status = info.get("status")
+
+ # Report failure
+ if info.get("status") != 200:
+ try:
+ # APIC error
+ aci.response_type(info.get("body"), rest_type)
+ aci.fail_json(msg="APIC Error %(code)s: %(text)s" % aci.error)
+ except KeyError:
+ # Connection error
+ aci.fail_json(msg="Connection failed for %(url)s. %(msg)s" % info)
+
+ aci.response_type(resp.read(), rest_type)
+
+ aci.result["status"] = aci.status
+ aci.result["imdata"] = aci.imdata
+ aci.result["totalCount"] = aci.totalCount
+
+ if aci.params.get("method") != "get":
+ output_path = aci.params.get("output_path")
+ if output_path is not None:
+ with open(output_path, "a") as output_file:
+ output_file.write(str(payload))
+
+ # Report success
+ aci.exit_json(**aci.result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_snmp_client.py b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_client.py
new file mode 100644
index 00000000..a7c01dc8
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_client.py
@@ -0,0 +1,280 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_snmp_client
+short_description: Manage SNMP clients (snmp:ClientP).
+description:
+- Manage SNMP clients
+options:
+ address:
+ description:
+ - IP subnet to accept SNMP requests from
+ type: str
+ client_group:
+ description:
+ - Name of an existing SNMP client group
+ type: str
+ aliases: [ client_group_name, client_group_profile ]
+ client_name:
+ description:
+ - Name of the SNMP client
+ type: str
+ policy:
+ description:
+ - Name of an existing SNMP policy
+ type: str
+ aliases: [ snmp_policy, snmp_policy_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(snmp:ClientP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create an SNMP client
+ cisco.aci.aci_snmp_client:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ client_group: my_snmp_client_group
+ address: 10.20.30.0/24
+ client_name: my_client_name
+ state: present
+ delegate_to: localhost
+
+- name: Remove an SNMP client
+ cisco.aci.aci_snmp_client_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ client_group: my_snmp_client_group
+ address: 10.20.30.0/24
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SNMP client
+ cisco.aci.aci_snmp_client:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ client_group: my_snmp_client_group
+ address: 10.20.30.0/24
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SNMP clients
+ cisco.aci.aci_snmp_client:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ address=dict(type="str"),
+ client_group=dict(type="str", aliases=["client_group_name", "client_group_profile"]),
+ policy=dict(type="str", aliases=["snmp_policy", "snmp_policy_name"]),
+ client_name=dict(type="str"),
+ 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", "client_group", "address"]],
+ ["state", "present", ["policy", "client_group", "address"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ client_group = module.params.get("client_group")
+ policy = module.params.get("policy")
+ client_name = module.params.get("client_name")
+ address = module.params.get("address")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="snmpPol",
+ aci_rn="fabric/snmppol-{0}".format(policy),
+ module_object=policy,
+ target_filter={"name": policy},
+ ),
+ subclass_1=dict(
+ aci_class="snmpClientGrpP",
+ aci_rn="clgrp-{0}".format(client_group),
+ module_object=client_group,
+ target_filter={"name": client_group},
+ ),
+ subclass_2=dict(
+ aci_class="snmpClientP",
+ aci_rn="client-[{0}]".format(address),
+ module_object=address,
+ target_filter={"addr": address},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="snmpClientP",
+ class_config=dict(addr=address, name=client_name),
+ )
+
+ aci.get_diff(aci_class="snmpClientP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_snmp_client_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_client_group.py
new file mode 100644
index 00000000..7f7b1250
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_client_group.py
@@ -0,0 +1,284 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_snmp_client_group
+short_description: Manage SNMP client groups (snmp:ClientGrpP).
+description:
+- Manage SNMP client groups
+options:
+ client_group:
+ description:
+ - Name of the SNMP client group
+ type: str
+ aliases: [ client_group_name, client_group_profile ]
+ description:
+ description:
+ - Description of the SNMP policy
+ type: str
+ mgmt_epg:
+ description:
+ - Associated management EPG
+ type: str
+ aliases: [ management_epg_name, management_epg ]
+ policy:
+ description:
+ - Name of an existing SNMP policy
+ type: str
+ aliases: [ snmp_policy, snmp_policy_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(snmp:ClientGrpP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create an SNMP client group
+ cisco.aci.aci_snmp_client_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ client_group: my_snmp_client_group
+ mgmt_epg: oob-default
+ state: present
+ delegate_to: localhost
+
+- name: Remove an SNMP client group
+ cisco.aci.aci_snmp_client_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ client_group: my_snmp_client_group
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SNMP client group
+ cisco.aci.aci_snmp_client_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ client_group: my_snmp_client_group
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SNMP client group
+ cisco.aci.aci_snmp_community_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ client_group=dict(type="str", aliases=["client_group_name", "client_group_profile"]),
+ mgmt_epg=dict(type="str", aliases=["management_epg_name", "management_epg"]),
+ policy=dict(type="str", aliases=["snmp_policy", "snmp_policy_name"]),
+ description=dict(type="str"),
+ 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", "client_group"]],
+ ["state", "present", ["policy", "client_group"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ client_group = module.params.get("client_group")
+ policy = module.params.get("policy")
+ mgmt_epg = module.params.get("mgmt_epg")
+ description = module.params.get("description")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="snmpPol",
+ aci_rn="fabric/snmppol-{0}".format(policy),
+ module_object=policy,
+ target_filter={"name": policy},
+ ),
+ subclass_1=dict(
+ aci_class="snmpClientGrpP",
+ aci_rn="clgrp-{0}".format(client_group),
+ module_object=client_group,
+ target_filter={"name": client_group},
+ ),
+ child_classes=["snmpRsEpg"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ if mgmt_epg:
+ tdn = "uni/tn-mgmt/mgmtp-default/{0}".format(mgmt_epg)
+ else:
+ tdn = None
+ aci.payload(
+ aci_class="snmpClientGrpP",
+ class_config=dict(name=client_group, descr=description),
+ child_configs=[
+ dict(
+ snmpRsEpg=dict(
+ attributes=dict(tDn=tdn),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="snmpClientGrpP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_snmp_community_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_community_policy.py
new file mode 100644
index 00000000..44696216
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_community_policy.py
@@ -0,0 +1,263 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_snmp_community_policy
+short_description: Manage SNMP community policies (snmp:CommunityP).
+description:
+- Manage SNMP community policies
+options:
+ community:
+ description:
+ - Name of the SNMP community policy
+ type: str
+ description:
+ description:
+ - Description of the SNMP policy
+ type: str
+ policy:
+ description:
+ - Name of an existing SNMP policy
+ type: str
+ aliases: [ snmp_policy, snmp_policy_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(snmp:CommunityP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create an SNMP community policy
+ cisco.aci.aci_snmp_community_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ community: my_snmp_community
+ state: present
+ delegate_to: localhost
+
+- name: Remove an SNMP community policy
+ cisco.aci.aci_snmp_community_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ community: my_snmp_community
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SNMP community policy
+ cisco.aci.aci_snmp_community_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ community: my_snmp_community
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SNMP community policies
+ cisco.aci.aci_snmp_community_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ community=dict(type="str"),
+ policy=dict(type="str", aliases=["snmp_policy", "snmp_policy_name"]),
+ description=dict(type="str"),
+ 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", "community"]],
+ ["state", "present", ["policy", "community"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ community = module.params.get("community")
+ policy = module.params.get("policy")
+ description = module.params.get("description")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="snmpPol",
+ aci_rn="fabric/snmppol-{0}".format(policy),
+ module_object=policy,
+ target_filter={"name": policy},
+ ),
+ subclass_1=dict(
+ aci_class="snmpCommunityP",
+ aci_rn="community-{0}".format(community),
+ module_object=community,
+ target_filter={"name": community},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="snmpCommunityP",
+ class_config=dict(name=community, descr=description),
+ )
+
+ aci.get_diff(aci_class="snmpCommunityP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_snmp_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_policy.py
new file mode 100644
index 00000000..c7a89874
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_policy.py
@@ -0,0 +1,272 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_snmp_policy
+short_description: Manage Syslog groups (snmp:Pol).
+description:
+- Manage syslog policies
+options:
+ admin_state:
+ description:
+ - Administrative State of the policy
+ type: str
+ choices: [ enabled, disabled ]
+ contact:
+ description:
+ - SNMP contact
+ type: str
+ description:
+ description:
+ - Description of the SNMP policy
+ type: str
+ location:
+ description:
+ - SNMP location
+ type: str
+ name:
+ description:
+ - Name of the SNMP policy
+ type: str
+ aliases: [ snmp_policy, snmp_policy_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(snmp:Pol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create an SNMP policy and Set Admin State to Enable
+ cisco.aci.aci_snmp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ validate_certs: no
+ admin_state: enabled
+ name: my_snmp_policy
+ state: present
+ delegate_to: localhost
+
+- name: Remove an SNMP policy
+ cisco.aci.aci_snmp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_snmp_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SNMP policy
+ cisco.aci.aci_snmp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_snmp_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SNMP policies
+ cisco.aci.aci_snmp_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["snmp_policy", "snmp_policy_name"]),
+ admin_state=dict(type="str", choices=["enabled", "disabled"]),
+ contact=dict(type="str"),
+ description=dict(type="str"),
+ location=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ admin_state = module.params.get("admin_state")
+ contact = module.params.get("contact")
+ description = module.params.get("description")
+ location = module.params.get("location")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="snmpPol",
+ aci_rn="fabric/snmppol-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["snmpCommunityP", "snmpClientGrpP"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="snmpPol",
+ class_config=dict(name=name, adminSt=admin_state, contact=contact, descr=description, loc=location),
+ )
+
+ aci.get_diff(aci_class="snmpPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_snmp_user.py b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_user.py
new file mode 100644
index 00000000..49a9f5ae
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_snmp_user.py
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_snmp_user
+short_description: Manage SNMP v3 Users (snmp:UserP).
+description:
+- Manage SNMP v3 Users
+- Note that all properties within the snmpUserP class are Create-only. To modify any property of an existing user, you must delete and re-create it.
+options:
+ auth_type:
+ description:
+ - SNMP authentication method
+ type: str
+ choices: [ hmac-md5-96, hmac-sha1-96]
+ auth_key:
+ description:
+ - SNMP authentication key
+ type: str
+ name:
+ description:
+ - Name of the SNMP user policy
+ type: str
+ aliases: [ snmp_user_policy ]
+ policy:
+ description:
+ - Name of an existing SNMP policy
+ type: str
+ aliases: [ snmp_policy, snmp_policy_name ]
+ privacy_type:
+ description:
+ - SNMP privacy type
+ type: str
+ choices: [ aes-128, des, none ]
+ privacy_key:
+ description:
+ - SNMP privacy key
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(snmp:UserP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create an SNMP user
+ cisco.aci.aci_snmp_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ name: my_snmp_user
+ auth_type: hmac-sha1-96
+ auth_key: "{{ hmac_key }}"
+ state: present
+ delegate_to: localhost
+
+- name: Create an SNMP user with both authentication and privacy
+ cisco.aci.aci_snmp_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ name: my_snmp_user
+ auth_type: hmac-sha1-96
+ auth_key: "{{ hmac_key }}"
+ privacy_type: aes-128
+ privacy_key: "{{ aes_key }}"
+ state: present
+ delegate_to: localhost
+
+- name: Remove an SNMP user
+ cisco.aci.aci_snmp_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ name: my_snmp_user
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SNMP user
+ cisco.aci.aci_snmp_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ policy: my_snmp_policy
+ name: my_snmp_user
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SNMP users
+ cisco.aci.aci_snmp_user:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ policy=dict(type="str", aliases=["snmp_policy", "snmp_policy_name"]),
+ name=dict(type="str", aliases=["snmp_user_policy"]),
+ auth_type=dict(type="str", choices=["hmac-md5-96", "hmac-sha1-96"]),
+ auth_key=dict(type="str", no_log=True),
+ privacy_type=dict(type="str", choices=["aes-128", "des", "none"]),
+ privacy_key=dict(type="str", no_log=True),
+ 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", "name"]],
+ ["state", "present", ["policy", "name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ policy = module.params.get("policy")
+ name = module.params.get("name")
+ auth_type = module.params.get("auth_type")
+ auth_key = module.params.get("auth_key")
+ privacy_type = module.params.get("privacy_type")
+ privacy_key = module.params.get("privacy_key")
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="snmpPol",
+ aci_rn="fabric/snmppol-{0}".format(policy),
+ module_object=policy,
+ target_filter={"name": policy},
+ ),
+ subclass_1=dict(
+ aci_class="snmpUserP",
+ aci_rn="user-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="snmpUserP",
+ class_config=dict(privType=privacy_type, privKey=privacy_key, authType=auth_type, authKey=auth_key, name=name),
+ )
+
+ aci.get_diff(aci_class="snmpUserP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py b/ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py
new file mode 100644
index 00000000..b5428449
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_static_binding_to_epg.py
@@ -0,0 +1,481 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_static_binding_to_epg
+short_description: Bind static paths to EPGs (fv:RsPathAtt)
+description:
+- Bind static paths to EPGs on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - Name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ ap:
+ description:
+ - Name of an existing application network profile, that will contain the EPGs.
+ type: str
+ aliases: [ app_profile, app_profile_name ]
+ epg:
+ description:
+ - The name of the end point group.
+ type: str
+ aliases: [ epg_name ]
+ description:
+ description:
+ - Description for the static path to EPG binding.
+ type: str
+ aliases: [ descr ]
+ encap_id:
+ description:
+ - The encapsulation ID associating the C(epg) with the interface path.
+ - This acts as the secondary C(encap_id) when using micro-segmentation.
+ - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096).
+ type: int
+ aliases: [ vlan, vlan_id ]
+ primary_encap_id:
+ description:
+ - Determines the primary encapsulation ID associating the C(epg)
+ with the interface path when using micro-segmentation.
+ - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown.
+ - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation.
+ type: str
+ aliases: [ primary_vlan, primary_vlan_id ]
+ deploy_immediacy:
+ description:
+ - The Deployment Immediacy of Static EPG on PC, VPC or Interface.
+ - The APIC defaults to C(lazy) when unset during creation.
+ type: str
+ choices: [ immediate, lazy ]
+ 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 to C(trunk) when unset during creation.
+ type: str
+ choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ]
+ aliases: [ interface_mode_name, mode ]
+ interface_type:
+ description:
+ - The type of interface for the static EPG deployment.
+ type: str
+ choices: [ fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc ]
+ default: switch_port
+ pod_id:
+ description:
+ - The pod number part of the tDn.
+ - C(pod_id) is usually an integer below C(10).
+ type: int
+ 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.
+ - The C(leafs) value is usually something like '101' or '101-102' depending on C(connection_type).
+ type: list
+ elements: str
+ aliases: [ leaves, nodes, paths, switches ]
+ interface:
+ description:
+ - The C(interface) string value part of the tDn.
+ - Usually a policy group like C(test-IntPolGrp) or an interface of the following format C(1/7) depending on C(interface_type).
+ type: str
+ extpaths:
+ description:
+ - The C(extpaths) integer value part of the tDn.
+ - C(extpaths) is only used if C(interface_type) is C(fex), C(fex_vpc) or C(fex_port_channel).
+ - When C(interface_type) is C(fex_vpc), then C(extpaths) is a list with both fex IDs.
+ - Usually something like C(1011).
+ type: list
+ elements: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant), C(ap), C(epg) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_ap
+- module: cisco.aci.aci_epg
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:RsPathAtt).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Deploy Static Path binding for given EPG
+ cisco.aci.aci_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ encap_id: 222
+ deploy_immediacy: lazy
+ interface_mode: untagged
+ interface_type: switch_port
+ pod_id: 1
+ leafs: 101
+ interface: '1/7'
+ state: present
+ delegate_to: localhost
+
+- name: Remove Static Path binding for given EPG
+ cisco.aci.aci_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ interface_type: switch_port
+ pod: 1
+ leafs: 101
+ interface: '1/7'
+ state: absent
+ delegate_to: localhost
+
+- name: Get specific Static Path binding for given EPG
+ cisco.aci.aci_static_binding_to_epg:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: accessport-code-cert
+ ap: accessport_code_app
+ epg: accessport_epg1
+ interface_type: switch_port
+ pod: 1
+ leafs: 101
+ interface: '1/7'
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+INTERFACE_MODE_MAPPING = {
+ "802.1p": "native",
+ "access": "untagged",
+ "native": "native",
+ "regular": "regular",
+ "tagged": "regular",
+ "trunk": "regular",
+ "untagged": "untagged",
+}
+
+INTERFACE_TYPE_MAPPING = dict(
+ fex="topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[eth{interface}]",
+ fex_port_channel="topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[{interface}]",
+ fex_vpc="topology/pod-{pod_id}/protpaths-{leafs}/extprotpaths-{extpaths}/pathep-[{interface}]",
+ port_channel="topology/pod-{pod_id}/paths-{leafs}/pathep-[{interface}]",
+ switch_port="topology/pod-{pod_id}/paths-{leafs}/pathep-[eth{interface}]",
+ vpc="topology/pod-{pod_id}/protpaths-{leafs}/pathep-[{interface}]",
+)
+
+# TODO: change 'deploy_immediacy' to 'resolution_immediacy' (as seen in aci_epg_to_domain)?
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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
+ description=dict(type="str", aliases=["descr"]),
+ encap_id=dict(type="int", aliases=["vlan", "vlan_id"]),
+ primary_encap_id=dict(type="str", aliases=["primary_vlan", "primary_vlan_id"]),
+ deploy_immediacy=dict(type="str", choices=["immediate", "lazy"]),
+ 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", "fex_port_channel", "fex_vpc"]),
+ pod_id=dict(type="int", aliases=["pod", "pod_number"]), # Not required for querying all objects
+ leafs=dict(type="list", elements="str", aliases=["leaves", "nodes", "paths", "switches"]), # Not required for querying all objects
+ interface=dict(type="str"), # Not required for querying all objects
+ extpaths=dict(type="list", elements="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["interface_type", "fex", ["extpaths"]],
+ ["interface_type", "fex_vpc", ["extpaths"]],
+ ["interface_type", "fex_port_channel", ["extpaths"]],
+ ["state", "absent", ["ap", "epg", "interface", "leafs", "pod_id", "tenant"]],
+ ["state", "present", ["ap", "encap_id", "epg", "interface", "leafs", "pod_id", "tenant"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ ap = module.params.get("ap")
+ epg = module.params.get("epg")
+ description = module.params.get("description")
+ encap_id = module.params.get("encap_id")
+ primary_encap_id = module.params.get("primary_encap_id")
+ deploy_immediacy = module.params.get("deploy_immediacy")
+ interface_mode = module.params.get("interface_mode")
+ interface_type = module.params.get("interface_type")
+ pod_id = module.params.get("pod_id")
+ leafs = module.params.get("leafs")
+ interface = module.params.get("interface")
+ extpaths = module.params.get("extpaths")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+
+ if leafs is not None:
+ # Process leafs, and support dash-delimited leafs
+ leafs = []
+ for leaf in module.params.get("leafs"):
+ # Users are likely to use integers for leaf IDs, which would raise an exception when using the join method
+ leafs.extend(str(leaf).split("-"))
+ if len(leafs) == 1:
+ if interface_type in ["vpc", "fex_vpc"]:
+ aci.fail_json(msg='A interface_type of "vpc" requires 2 leafs')
+ leafs = leafs[0]
+ elif len(leafs) == 2:
+ if interface_type not in ["vpc", "fex_vpc"]:
+ aci.fail_json(
+ msg='The interface_types "switch_port", "port_channel", and "fex" \
+ do not support using multiple leafs for a single binding'
+ )
+ leafs = "-".join(leafs)
+ else:
+ aci.fail_json(msg='The "leafs" parameter must not have more than 2 entries')
+
+ if extpaths is not None:
+ # Process extpaths, and support dash-delimited extpaths
+ extpaths = []
+ for extpath in module.params.get("extpaths"):
+ # Users are likely to use integers for extpaths IDs, which would raise an exception when using the join method
+ extpaths.extend(str(extpath).split("-"))
+ if len(extpaths) == 1:
+ if interface_type == "fex_vpc":
+ aci.fail_json(msg='A interface_type of "fex_vpc" requires 2 extpaths')
+ extpaths = extpaths[0]
+ elif len(extpaths) == 2:
+ if interface_type != "fex_vpc":
+ aci.fail_json(
+ msg='The interface_types "fex" \
+ and "fex_port_channel" do not support using multiple extpaths for a single binding'
+ )
+ extpaths = "-".join(extpaths)
+ else:
+ aci.fail_json(msg='The "extpaths" parameter must not have more than 2 entries')
+
+ if encap_id is not None:
+ if encap_id not in range(1, 4097):
+ aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096")
+ encap_id = "vlan-{0}".format(encap_id)
+
+ if primary_encap_id is not None:
+ try:
+ primary_encap_id = int(primary_encap_id)
+ if isinstance(primary_encap_id, int) and primary_encap_id in range(1, 4097):
+ primary_encap_id = "vlan-{0}".format(primary_encap_id)
+ else:
+ aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096 or unknown.")
+ except Exception as e:
+ if isinstance(primary_encap_id, str) and primary_encap_id != "unknown":
+ aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096 or unknown. %s" % e)
+
+ static_path = INTERFACE_TYPE_MAPPING[interface_type].format(pod_id=pod_id, leafs=leafs, extpaths=extpaths, interface=interface)
+
+ path_target_filter = {}
+ if pod_id is not None and leafs is not None and interface is not None and (interface_type != "fex" or extpaths is not None):
+ path_target_filter = {"tDn": static_path}
+
+ if interface_mode is not None:
+ interface_mode = INTERFACE_MODE_MAPPING[interface_mode]
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvAp",
+ aci_rn="ap-{0}".format(ap),
+ module_object=ap,
+ target_filter={"name": ap},
+ ),
+ subclass_2=dict(
+ aci_class="fvAEPg",
+ aci_rn="epg-{0}".format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ subclass_3=dict(
+ aci_class="fvRsPathAtt",
+ aci_rn="rspathAtt-[{0}]".format(static_path),
+ module_object=static_path,
+ target_filter=path_target_filter,
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvRsPathAtt",
+ class_config=dict(
+ descr=description,
+ encap=encap_id,
+ primaryEncap=primary_encap_id,
+ instrImedcy=deploy_immediacy,
+ mode=interface_mode,
+ tDn=static_path,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvRsPathAtt")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_static_node_mgmt_address.py b/ansible_collections/cisco/aci/plugins/modules/aci_static_node_mgmt_address.py
new file mode 100644
index 00000000..26a4b1db
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_static_node_mgmt_address.py
@@ -0,0 +1,346 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1)
+# Copyright: (c) 2020, Lionel Hercot <lhercot@cisco.com>
+# Copyright: (c) 2020, Shreyas Srish <ssrish@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_static_node_mgmt_address
+short_description: In band or Out of band management IP address
+description:
+- Cisco ACI Fabric Node IP address
+options:
+ epg:
+ description:
+ - The name of the end point group
+ type: str
+ pod_id:
+ description:
+ - The pod number of the leaf, spine or APIC
+ type: int
+ node_id:
+ description:
+ - ACI Fabric's node id of a leaf, spine or APIC
+ type: int
+ ipv4_address:
+ description:
+ - ipv4 address of in band/out of band mgmt
+ type: str
+ aliases: [ ip ]
+ ipv4_gw:
+ description:
+ - Gateway address of in band / out of band mgmt network
+ type: str
+ aliases: [ gw ]
+ ipv6_address:
+ description:
+ - ipv6 address of in band/out of band mgmt
+ type: str
+ aliases: [ ipv6 ]
+ ipv6_gw:
+ description:
+ - GW address of in band/out of band mgmt
+ type: str
+ type:
+ description:
+ - type of management interface
+ type: str
+ choices: [ in_band, out_of_band ]
+ required: true
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+author:
+- Sudhakar Shet Kudtarkar (@kudtarkar1)
+- Lionel Hercot (@lhercot)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add ipv4 address to in band mgmt interface
+ cisco.aci.aci_static_node_mgmt_address:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ pod_id: 1
+ type: in_band
+ node_id: 1102
+ ipv4_address: "3.1.1.2/24"
+ ipv4_gw: "3.1.1.1"
+ state: present
+ delegate_to: localhost
+
+- name: Add ipv4 address to out of band mgmt interface
+ cisco.aci.aci_static_node_mgmt_address:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ pod_id: 1
+ band_type: out_of_band
+ node_id: 1102
+ ipv4_address: "3.1.1.2/24"
+ ipv4_gw: "3.1.1.1"
+ state: present
+ delegate_to: localhost
+
+- name: Remove ipv4 address to in band mgmt interface
+ cisco.aci.aci_static_node_mgmt_address:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ pod_id: 1
+ type: in_band
+ node_id: 1102
+ ipv4_address: "3.1.1.2/24"
+ ipv4_gw: "3.1.1.1"
+ state: absent
+ delegate_to: localhost
+
+- name: Query the in band mgmt ipv4 address
+ cisco.aci.aci_static_node_mgmt_address:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ pod_id: 1
+ type: in_band
+ node_id: 1102
+ ipv4_address: "3.1.1.2/24"
+ ipv4_gw: "3.1.1.1"
+ state: query
+ delegate_to: localhost
+
+- name: Query all addresses in epg out of band25wf
+ cisco.aci.aci_static_node_mgmt_address:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ epg: default
+ type: out_of_band
+ state: query
+ delegate_to: localhost
+
+- name: Query all in band addresses
+ cisco.aci.aci_static_node_mgmt_address:
+ host: "Host IP"
+ username: admin
+ password: SomeSecretePassword
+ type: in_band
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?xml version="1.0" encoding="UTF-8"?><imdata totalCount="1"><error code="122" text="unknown managed object class "/></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: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: class_map (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ node_id=dict(type="int"),
+ pod_id=dict(type="int"),
+ type=dict(type="str", choices=["in_band", "out_of_band"], required=True),
+ epg=dict(type="str"),
+ ipv4_address=dict(type="str", aliases=["ip"]),
+ ipv4_gw=dict(type="str", aliases=["gw"]),
+ ipv6_address=dict(type="str", aliases=["ipv6"]),
+ ipv6_gw=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[["state", "absent", ["node_id", "epg"]], ["state", "present", ["node_id", "epg", "ipv4_address", "ipv4_gw"]]],
+ )
+
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ type = module.params.get("type")
+ epg = module.params.get("epg")
+ ipv4_address = module.params.get("ipv4_address")
+ ipv4_gw = module.params.get("ipv4_gw")
+ ipv6_address = module.params.get("ipv6_address")
+ ipv6_gw = module.params.get("ipv6_gw")
+ state = module.params.get("state")
+
+ class_map = dict(
+ in_band=list([dict(aci_class="mgmtInb", aci_rn="inb-{0}"), dict(aci_class="mgmtRsInBStNode", aci_rn="rsinBStNode-[{0}]")]),
+ out_of_band=list([dict(aci_class="mgmtOob", aci_rn="oob-{0}"), dict(aci_class="mgmtRsOoBStNode", aci_rn="rsooBStNode-[{0}]")]),
+ )
+
+ static_path = None
+ if pod_id is not None and node_id is not None:
+ static_path = "topology/pod-{0}/node-{1}".format(pod_id, node_id)
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-mgmt",
+ module_object="mgmt",
+ target_filter={"name": "mgmt"},
+ ),
+ subclass_1=dict(
+ aci_class="mgmtMgmtP",
+ aci_rn="mgmtp-default",
+ module_object="default",
+ target_filter={"name": "default"},
+ ),
+ subclass_2=dict(
+ aci_class=class_map.get(type)[0]["aci_class"],
+ aci_rn=class_map.get(type)[0]["aci_rn"].format(epg),
+ module_object=epg,
+ target_filter={"name": epg},
+ ),
+ subclass_3=dict(
+ aci_class=class_map.get(type)[1]["aci_class"],
+ aci_rn=class_map.get(type)[1]["aci_rn"].format(static_path),
+ module_object=static_path,
+ target_filter={"name": static_path},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=class_map.get(type)[1]["aci_class"],
+ class_config=dict(addr=ipv4_address, gw=ipv4_gw, v6Addr=ipv6_address, v6Gw=ipv6_gw),
+ )
+ aci.get_diff(aci_class=class_map.get(type)[1]["aci_class"])
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py b/ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py
new file mode 100644
index 00000000..2b850f1c
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_switch_leaf_selector.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_switch_leaf_selector
+short_description: Bind leaf selectors to switch policy leaf profiles (infra:LeafS, infra:NodeBlk, infra:RsAccNodePGrep)
+description:
+- Bind leaf selectors (with node block range and policy group) to switch policy leaf profiles on Cisco ACI fabrics.
+options:
+ description:
+ description:
+ - The description to assign to the C(leaf).
+ type: str
+ leaf_profile:
+ description:
+ - Name of the Leaf Profile to which we add a Selector.
+ type: str
+ aliases: [ leaf_profile_name ]
+ leaf:
+ description:
+ - Name of Leaf Selector.
+ type: str
+ aliases: [ name, leaf_name, leaf_profile_leaf_name, leaf_selector_name ]
+ leaf_node_blk:
+ description:
+ - Name of Node Block range to be added to Leaf Selector of given Leaf Profile.
+ type: str
+ aliases: [ leaf_node_blk_name, node_blk_name ]
+ leaf_node_blk_description:
+ description:
+ - The description to assign to the C(leaf_node_blk)
+ type: str
+ from:
+ description:
+ - Start of Node Block range.
+ type: int
+ aliases: [ node_blk_range_from, from_range, range_from ]
+ to:
+ description:
+ - Start of Node Block range.
+ type: int
+ aliases: [ node_blk_range_to, to_range, range_to ]
+ policy_group:
+ description:
+ - Name of the Policy Group to be added to Leaf Selector of given Leaf Profile.
+ type: str
+ aliases: [ name, policy_group_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- This module is to be used with M(cisco.aci.aci_switch_policy_leaf_profile).
+ One first creates a leaf profile (infra:NodeP) and then creates an associated selector (infra:LeafS),
+seealso:
+- module: cisco.aci.aci_switch_policy_leaf_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(infra:LeafS),
+ B(infra:NodeBlk) and B(infra:RsAccNodePGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+"""
+
+EXAMPLES = r"""
+- name: adding a switch policy leaf profile selector associated Node Block range (w/ policy group)
+ cisco.aci.aci_switch_leaf_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ leaf: leaf_selector_name
+ leaf_node_blk: node_blk_name
+ from: 1011
+ to: 1011
+ policy_group: somepolicygroupname
+ state: present
+ delegate_to: localhost
+
+- name: adding a switch policy leaf profile selector associated Node Block range (w/o policy group)
+ cisco.aci.aci_switch_leaf_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ leaf: leaf_selector_name
+ leaf_node_blk: node_blk_name
+ from: 1011
+ to: 1011
+ state: present
+ delegate_to: localhost
+
+- name: Removing a switch policy leaf profile selector
+ cisco.aci.aci_switch_leaf_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ leaf: leaf_selector_name
+ state: absent
+ delegate_to: localhost
+
+- name: Querying a switch policy leaf profile selector
+ cisco.aci.aci_switch_leaf_selector:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ leaf: leaf_selector_name
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ {
+ "description": dict(type="str"),
+ "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"),
+ # NOTE: Keyword 'from' is a reserved word in python, so we need it as a string
+ "from": dict(type="int", aliases=["node_blk_range_from", "from_range", "range_from"]),
+ "to": dict(type="int", aliases=["node_blk_range_to", "to_range", "range_to"]),
+ "policy_group": dict(type="str", aliases=["policy_group_name"]),
+ "state": dict(type="str", default="present", choices=["absent", "present", "query"]),
+ "name_alias": dict(type="str"),
+ }
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[["state", "absent", ["leaf_profile", "leaf"]], ["state", "present", ["leaf_profile", "leaf", "leaf_node_blk", "from", "to"]]],
+ )
+
+ description = module.params.get("description")
+ leaf_profile = module.params.get("leaf_profile")
+ leaf = module.params.get("leaf")
+ leaf_node_blk = module.params.get("leaf_node_blk")
+ leaf_node_blk_description = module.params.get("leaf_node_blk_description")
+ from_ = module.params.get("from")
+ to_ = module.params.get("to")
+ policy_group = module.params.get("policy_group")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ # Build child_configs dynamically
+ child_configs = [
+ dict(
+ infraNodeBlk=dict(
+ attributes=dict(
+ descr=leaf_node_blk_description,
+ name=leaf_node_blk,
+ from_=from_,
+ to_=to_,
+ ),
+ ),
+ ),
+ ]
+
+ # Add infraRsAccNodePGrp only when policy_group was defined
+ if policy_group is not None:
+ child_configs.append(
+ dict(
+ infraRsAccNodePGrp=dict(
+ attributes=dict(
+ tDn="uni/infra/funcprof/accnodepgrp-{0}".format(policy_group),
+ ),
+ ),
+ )
+ )
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraNodeP",
+ aci_rn="infra/nprof-{0}".format(leaf_profile),
+ module_object=leaf_profile,
+ target_filter={"name": leaf_profile},
+ ),
+ subclass_1=dict(
+ aci_class="infraLeafS",
+ # NOTE: normal rn: leaves-{name}-typ-{type}, hence here hardcoded to range for purposes of module
+ aci_rn="leaves-{0}-typ-range".format(leaf),
+ module_object=leaf,
+ target_filter={"name": 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"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraLeafS",
+ class_config=dict(
+ descr=description,
+ name=leaf,
+ nameAlias=name_alias,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="infraLeafS")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py
new file mode 100644
index 00000000..8d62ee22
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_leaf_profile.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_switch_policy_leaf_profile
+short_description: Manage switch policy leaf profiles (infra:NodeP)
+description:
+- Manage switch policy leaf profiles on Cisco ACI fabrics.
+options:
+ leaf_profile:
+ description:
+ - The name of the Leaf Profile.
+ type: str
+ aliases: [ leaf_profile_name, name ]
+ description:
+ description:
+ - Description for the Leaf Profile.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_switch_policy_leaf_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(infra:NodeP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+"""
+
+EXAMPLES = r"""
+- name: creating a Leaf Profile with description
+ cisco.aci.aci_switch_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ description: sw_description
+ state: present
+ delegate_to: localhost
+
+- name: Deleting a Leaf Profile
+ cisco.aci.aci_switch_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ state: absent
+ delegate_to: localhost
+
+- name: Query a Leaf Profile
+ cisco.aci.aci_switch_policy_leaf_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ leaf_profile: sw_name
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ 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"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["leaf_profile"]],
+ ["state", "present", ["leaf_profile"]],
+ ],
+ )
+
+ leaf_profile = module.params.get("leaf_profile")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="infraNodeP",
+ aci_rn="infra/nprof-{0}".format(leaf_profile),
+ module_object=leaf_profile,
+ target_filter={"name": leaf_profile},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="infraNodeP",
+ class_config=dict(
+ name=leaf_profile,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="infraNodeP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py
new file mode 100644
index 00000000..a7994db7
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_switch_policy_vpc_protection_group.py
@@ -0,0 +1,305 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Bruno Calogero <brunocalogero@hotmail.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_switch_policy_vpc_protection_group
+short_description: Manage switch policy explicit vPC protection groups (fabric:ExplicitGEp, fabric:NodePEp).
+description:
+- Manage switch policy explicit vPC protection groups on Cisco ACI fabrics.
+options:
+ protection_group:
+ description:
+ - The name of the Explicit vPC Protection Group.
+ type: str
+ aliases: [ name, protection_group_name ]
+ protection_group_id:
+ description:
+ - The Explicit vPC Protection Group ID.
+ type: int
+ aliases: [ id ]
+ vpc_domain_policy:
+ description:
+ - The vPC domain policy to be associated with the Explicit vPC Protection Group.
+ type: str
+ aliases: [ vpc_domain_policy_name ]
+ switch_1_id:
+ description:
+ - The ID of the first Leaf Switch for the Explicit vPC Protection Group.
+ type: int
+ switch_2_id:
+ description:
+ - The ID of the Second Leaf Switch for the Explicit vPC Protection Group.
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_switch_policy_leaf_profile
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fabric:ExplicitGEp) and B(fabric:NodePEp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Bruno Calogero (@brunocalogero)
+"""
+
+EXAMPLES = r"""
+- name: Add vPC Protection Group
+ cisco.aci.aci_switch_policy_vpc_protection_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ protection_group: leafPair101-vpcGrp
+ protection_group_id: 6
+ switch_1_id: 1011
+ switch_2_id: 1012
+ state: present
+ delegate_to: localhost
+
+- name: Remove Explicit vPC Protection Group
+ cisco.aci.aci_switch_policy_vpc_protection_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ protection_group: leafPair101-vpcGrp
+ state: absent
+ delegate_to: localhost
+
+- name: Query vPC Protection Groups
+ cisco.aci.aci_switch_policy_vpc_protection_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query our vPC Protection Group
+ cisco.aci.aci_switch_policy_vpc_protection_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ protection_group: leafPair101-vpcGrp
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ 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"),
+ switch_2_id=dict(type="int"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["protection_group"]],
+ ["state", "present", ["protection_group", "protection_group_id", "switch_1_id", "switch_2_id"]],
+ ],
+ )
+
+ protection_group = module.params.get("protection_group")
+ protection_group_id = module.params.get("protection_group_id")
+ vpc_domain_policy = module.params.get("vpc_domain_policy")
+ switch_1_id = module.params.get("switch_1_id")
+ switch_2_id = module.params.get("switch_2_id")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fabricExplicitGEp",
+ aci_rn="fabric/protpol/expgep-{0}".format(protection_group),
+ module_object=protection_group,
+ target_filter={"name": protection_group},
+ ),
+ child_classes=["fabricNodePEp", "fabricNodePEp", "fabricRsVpcInstPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fabricExplicitGEp",
+ class_config=dict(
+ name=protection_group,
+ id=protection_group_id,
+ nameAlias=name_alias,
+ ),
+ child_configs=[
+ dict(
+ fabricNodePEp=dict(
+ attributes=dict(
+ id="{0}".format(switch_1_id),
+ ),
+ ),
+ ),
+ dict(
+ fabricNodePEp=dict(
+ attributes=dict(
+ id="{0}".format(switch_2_id),
+ ),
+ ),
+ ),
+ dict(
+ fabricRsVpcInstPol=dict(
+ attributes=dict(
+ tnVpcInstPolName=vpc_domain_policy,
+ ),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="fabricExplicitGEp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_syslog_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_syslog_group.py
new file mode 100644
index 00000000..bab3379a
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_syslog_group.py
@@ -0,0 +1,337 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_syslog_group
+short_description: Manage Syslog groups (syslog:Group, syslog:Console, syslog:File and syslog:Prof).
+description:
+- Manage syslog groups
+options:
+ admin_state:
+ description:
+ - Administrative state of the syslog group
+ type: str
+ choices: [ enabled, disabled ]
+ console_logging:
+ description:
+ - Log events to console
+ type: str
+ choices: [ enabled, disabled ]
+ console_log_severity:
+ description:
+ - Severity of events to log to console
+ type: str
+ choices: [ alerts, critical, debugging, emergencies, error, information, notifications, warnings ]
+ local_file_logging:
+ description:
+ - Log to local file
+ type: str
+ choices: [ enabled, disabled ]
+ local_file_log_severity:
+ description:
+ - Severity of events to log to local file
+ type: str
+ choices: [ alerts, critical, debugging, emergencies, error, information, notifications, warnings ]
+ format:
+ description:
+ - Format of the syslog messages. If omitted when creating a group, ACI defaults to using aci format.
+ type: str
+ choices: [ aci, nxos ]
+ include_ms:
+ description:
+ - Include milliseconds in log timestamps
+ type: bool
+ include_time_zone:
+ description:
+ - Include timezone in log timestamps
+ type: bool
+ name:
+ description:
+ - Name of the syslog group
+ type: str
+ aliases: [ syslog_group, syslog_group_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(syslog:Group).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a syslog group
+ cisco.aci.aci_syslog_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_group
+ local_file_logging: enabled
+ local_file_log_severity: warnings
+ console_logging: enabled
+ console_log_severity: critical
+ state: present
+ delegate_to: localhost
+
+- name: Disable logging to local file
+ cisco.aci.aci_syslog_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_group
+ local_file_logging: disabled
+ state: present
+ delegate_to: localhost
+
+- name: Remove a syslog group
+ cisco.aci.aci_syslog_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_group
+ state: absent
+ delegate_to: localhost
+
+- name: Query a syslog group
+ cisco.aci.aci_syslog_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_group
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all syslog groups
+ cisco.aci.aci_syslog_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["syslog_group", "syslog_group_name"]),
+ format=dict(type="str", choices=["aci", "nxos"]),
+ admin_state=dict(type="str", choices=["enabled", "disabled"]),
+ console_logging=dict(type="str", choices=["enabled", "disabled"]),
+ console_log_severity=dict(type="str", choices=["alerts", "critical", "debugging", "emergencies", "error", "information", "notifications", "warnings"]),
+ local_file_logging=dict(type="str", choices=["enabled", "disabled"]),
+ local_file_log_severity=dict(
+ type="str", choices=["alerts", "critical", "debugging", "emergencies", "error", "information", "notifications", "warnings"]
+ ),
+ include_ms=dict(type="bool"),
+ include_time_zone=dict(type="bool"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ format = module.params.get("format")
+ admin_state = module.params.get("admin_state")
+ console_logging = module.params.get("console_logging")
+ console_log_severity = module.params.get("console_log_severity")
+ local_file_logging = module.params.get("local_file_logging")
+ local_file_log_severity = module.params.get("local_file_log_severity")
+ include_ms = aci.boolean(module.params.get("include_ms"))
+ include_time_zone = aci.boolean(module.params.get("include_time_zone"))
+ state = module.params.get("state")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="syslogGroup",
+ aci_rn="fabric/slgroup-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["syslogRemoteDest", "syslogProf", "syslogFile", "syslogConsole"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ class_config = dict(
+ name=name,
+ format=format,
+ includeMilliSeconds=include_ms,
+ )
+ if include_time_zone is not None:
+ class_config["includeTimeZone"] = include_time_zone
+ aci.payload(
+ aci_class="syslogGroup",
+ class_config=class_config,
+ child_configs=[
+ dict(
+ syslogProf=dict(
+ attributes=dict(adminState=admin_state, name="syslog"),
+ ),
+ ),
+ dict(
+ syslogFile=dict(
+ attributes=dict(adminState=local_file_logging, format=format, severity=local_file_log_severity),
+ ),
+ ),
+ dict(
+ syslogConsole=dict(
+ attributes=dict(adminState=console_logging, format=format, severity=console_log_severity),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="syslogGroup")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_syslog_remote_dest.py b/ansible_collections/cisco/aci/plugins/modules/aci_syslog_remote_dest.py
new file mode 100644
index 00000000..b3c1a02b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_syslog_remote_dest.py
@@ -0,0 +1,333 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_syslog_remote_dest
+short_description: Manage Syslog Remote Destinations (syslog:RemoteDest).
+description:
+- Manage remote destinations for syslog messages within
+ an existing syslog group object
+options:
+ admin_state:
+ description:
+ - Administrative state of the syslog remote destination
+ type: str
+ choices: [ enabled, disabled ]
+ description:
+ description:
+ - Description of the remote destination
+ type: str
+ destination:
+ description:
+ - Hostname or IP address to send syslog messages to
+ type: str
+ format:
+ description:
+ - Format of the syslog messages
+ type: str
+ choices: [ aci, nxos ]
+ facility:
+ description:
+ - Forwarding facility for syslog messages
+ type: str
+ choices: [ local0, local1, local2, local3, local4, local5, local6, local7 ]
+ group:
+ description:
+ - Name of an existing syslog group
+ type: str
+ aliases: [ syslog_group, syslog_group_name ]
+ mgmt_epg:
+ description:
+ - Name of a management EPG to send syslog messages from
+ type: str
+ name:
+ description:
+ - Name of the syslog remote destination
+ type: str
+ aliases: [ remote_destination_name, remote_destination ]
+ severity:
+ description:
+ - Severity of messages to send to remote syslog
+ type: str
+ choices: [ alerts, critical, debugging, emergencies, error, information, notifications, warnings]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ syslog_port:
+ description:
+ - UDP port to send syslog messages to
+ type: int
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(syslog:RemoteDest).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Create a syslog remote destination
+ cisco.aci.aci_syslog_remote_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_syslog_group
+ facility: local7
+ destination: 10.20.30.40
+ syslog_port: 5678
+ mgmt_epg: oob-default
+ state: present
+ delegate_to: localhost
+
+- name: Delete syslog remote destination
+ cisco.aci.aci_syslog_remote_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_syslog_group
+ destination: 10.20.30.40
+ state: absent
+ delegate_to: localhost
+
+- name: Query a syslog remote destination
+ cisco.aci.aci_syslog_remote_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ group: my_syslog_group
+ destination: 10.20.30.40
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all syslog remote destinations
+ cisco.aci.aci_syslog_remote_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["remote_destination_name", "remote_destination"]),
+ format=dict(type="str", choices=["aci", "nxos"]),
+ admin_state=dict(type="str", choices=["enabled", "disabled"]),
+ description=dict(type="str"),
+ destination=dict(type="str"),
+ facility=dict(type="str", choices=["local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7"]),
+ group=dict(type="str", aliases=["syslog_group", "syslog_group_name"]),
+ mgmt_epg=dict(type="str"),
+ syslog_port=dict(type="int"),
+ severity=dict(type="str", choices=["alerts", "critical", "debugging", "emergencies", "error", "information", "notifications", "warnings"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["group", "destination"]],
+ ["state", "present", ["group", "destination"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ name = module.params.get("name")
+ format = module.params.get("format")
+ admin_state = module.params.get("admin_state")
+ description = module.params.get("description")
+ destination = module.params.get("destination")
+ facility = module.params.get("facility")
+ group = module.params.get("group")
+ syslog_port = module.params.get("syslog_port")
+ severity = module.params.get("severity")
+ state = module.params.get("state")
+ mgmt_epg = module.params.get("mgmt_epg")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="syslogGroup",
+ aci_rn="fabric/slgroup-{0}".format(group),
+ module_object=group,
+ target_filter={"name": group},
+ ),
+ subclass_1=dict(
+ aci_class="syslogRemoteDest",
+ aci_rn="rdst-{0}".format(destination),
+ module_object=destination,
+ target_filter={"host": destination},
+ ),
+ child_classes=["fileRsARemoteHostToEpg"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if mgmt_epg:
+ child_configs.append(
+ dict(
+ fileRsARemoteHostToEpg=dict(
+ attributes=dict(tDn=("uni/tn-mgmt/mgmtp-default/{0}".format(mgmt_epg))),
+ ),
+ )
+ )
+ aci.payload(
+ aci_class="syslogRemoteDest",
+ class_config=dict(
+ adminState=admin_state,
+ descr=description,
+ format=format,
+ forwardingFacility=facility,
+ host=destination,
+ name=name,
+ port=syslog_port,
+ severity=severity,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="syslogRemoteDest")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_syslog_source.py b/ansible_collections/cisco/aci/plugins/modules/aci_syslog_source.py
new file mode 100644
index 00000000..95dcd7f3
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_syslog_source.py
@@ -0,0 +1,280 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Tim Cragg <tcragg@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_syslog_source
+short_description: Manage Syslog Source objects (syslog:Src)
+description:
+- Manage Syslog Source objects
+options:
+ name:
+ description:
+ - Name of the Syslog Source policy
+ type: str
+ aliases: [ syslog_src, syslog_source ]
+ include:
+ description:
+ - List of message types to include
+ type: list
+ elements: str
+ choices: [ audit, events, faults, session ]
+ min_severity:
+ description:
+ - Minimum Severity of message to include
+ type: str
+ choices: [ alerts, critical, debugging, emergencies, errors, information, notifications, warnings ]
+ destination_group:
+ description:
+ - Name of an existing syslog group
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ default: present
+ choices: [ absent, present, query ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(syslog:Src).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new syslog source
+ cisco.aci.aci_syslog_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_src
+ include:
+ - audit
+ - events
+ - faults
+ min_severity: errors
+ destination_group: my_syslog_group
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing syslog source
+ cisco.aci.aci_syslog_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_src
+ state: absent
+ delegate_to: localhost
+
+- name: Query all syslog sources
+ cisco.aci.aci_syslog_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific syslog source
+ cisco.aci.aci_syslog_source:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_syslog_src
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["syslog_src", "syslog_source"]),
+ include=dict(type="list", elements="str", choices=["audit", "events", "faults", "session"]),
+ min_severity=dict(type="str", choices=["alerts", "critical", "debugging", "emergencies", "errors", "information", "notifications", "warnings"]),
+ destination_group=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ include = module.params.get("include")
+ min_severity = module.params.get("min_severity")
+ destination_group = module.params.get("destination_group")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="syslogSrc",
+ aci_rn="fabric/moncommon/slsrc-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["syslogRsDestGroup"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ class_config = dict(name=name, minSev=min_severity)
+ if include:
+ class_config["incl"] = ",".join(include)
+
+ aci.payload(
+ aci_class="syslogSrc",
+ class_config=class_config,
+ child_configs=[
+ dict(
+ syslogRsDestGroup=dict(
+ attributes=dict(tDn=("uni/fabric/slgroup-{0}".format(destination_group))),
+ ),
+ ),
+ ],
+ )
+
+ aci.get_diff(aci_class="syslogSrc")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_system.py b/ansible_collections/cisco/aci/plugins/modules/aci_system.py
new file mode 100644
index 00000000..2d134ab6
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_system.py
@@ -0,0 +1,194 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Lionel Hercot (@lhercot) <lhercot@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r"""
+---
+module: aci_system
+short_description: Query the ACI system information (top:System)
+description:
+- Query the ACI system information (top:System) on Cisco ACI.
+author:
+- Lionel Hercot (@lhercot)
+options:
+ id:
+ description:
+ - The controller node ID
+ aliases: [ controller, node ]
+ type: int
+ state:
+ description:
+ - Use C(query) for listing an object or multiple objects.
+ choices: [ query ]
+ default: query
+ type: str
+
+notes:
+- More information about the internal APIC class B(top:System) from
+ L(the APIC Management Information Model reference,https://developer.cisco.com/docs/apic-mim-ref/).
+- This module is used to query system information for both cloud and on-premises controllers.
+
+extends_documentation_fragment:
+- cisco.aci.aci
+"""
+
+EXAMPLES = r"""
+- name: Query all controllers system information
+ cisco.aci.aci_system:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ state: query
+ delegate_to: localhost
+
+- name: Query controller 1 specific system information
+ cisco.aci.aci_system:
+ host: apic
+ username: userName
+ password: somePassword
+ validate_certs: no
+ id: 1
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ id=dict(type="int", aliases=["controller", "node"]),
+ state=dict(type="str", default="query", choices=["query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ id = module.params.get("id")
+
+ aci = ACIModule(module)
+ aci.construct_url(root_class=dict(aci_class="topSystem", target_filter={"id": id}))
+
+ aci.get_existing()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py b/ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py
new file mode 100644
index 00000000..aaa0914b
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_taboo_contract.py
@@ -0,0 +1,289 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_taboo_contract
+short_description: Manage taboo contracts (vz:BrCP)
+description:
+- Manage taboo contracts on Cisco ACI fabrics.
+options:
+ taboo_contract:
+ description:
+ - The name of the Taboo Contract.
+ type: str
+ aliases: [ name ]
+ description:
+ description:
+ - The description for the Taboo Contract.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ scope:
+ description:
+ - The scope of a service contract.
+ - The APIC defaults to C(context) when unset during creation.
+ type: str
+ choices: [ application-profile, context, global, tenant ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vz:BrCP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add taboo contract
+ cisco.aci.aci_taboo_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_test
+ taboo_contract: taboo_contract_test
+ state: present
+ delegate_to: localhost
+
+- name: Remove taboo contract
+ cisco.aci.aci_taboo_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_test
+ taboo_contract: taboo_contract_test
+ state: absent
+ delegate_to: localhost
+
+- name: Query all taboo contracts
+ cisco.aci.aci_taboo_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific taboo contract
+ cisco.aci.aci_taboo_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: ansible_test
+ taboo_contract: taboo_contract_test
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ taboo_contract=dict(type="str", aliases=["name"]), # Not required for querying all contracts
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all contracts
+ scope=dict(type="str", choices=["application-profile", "context", "global", "tenant"]),
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "taboo_contract"]],
+ ["state", "present", ["tenant", "taboo_contract"]],
+ ],
+ )
+
+ taboo_contract = module.params.get("taboo_contract")
+ description = module.params.get("description")
+ scope = module.params.get("scope")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="vzTaboo",
+ aci_rn="taboo-{0}".format(taboo_contract),
+ module_object=taboo_contract,
+ target_filter={"name": taboo_contract},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vzTaboo",
+ class_config=dict(
+ name=taboo_contract,
+ descr=description,
+ scope=scope,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="vzTaboo")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tag.py b/ansible_collections/cisco/aci/plugins/modules/aci_tag.py
new file mode 100644
index 00000000..8c42a612
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tag.py
@@ -0,0 +1,270 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tag
+short_description: Tagging of ACI objects
+description:
+- Tagging a object on Cisco ACI fabric.
+options:
+ dn:
+ description:
+ - Unique Distinguished Name (DN) from ACI object model.
+ type: str
+ tag_key:
+ description:
+ - Unique identifier of tag object.
+ type: str
+ tag_value:
+ description:
+ - Value of the property.
+ type: str
+ tag_type:
+ description:
+ - Type of tag object.
+ type: str
+ choices: [ annotation, instance, tag ]
+ required: yes
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+
+notes:
+- The ACI object must exist before using this module in your playbook.
+- CAVEAT - Due to deprecation of the 'tagInst' object, creating a tag with tag_type 'instance' automatically generates a
+ 'annotation' tag_type tag with an empty value. When deleting a tag_type 'instance', the 'tagAnnotation' object will
+ remain present and needs to be deleted separately.
+seealso:
+- name: Cisco APIC System Management Configuration Guide
+ description: More information about the tagging can be found in the Cisco APIC System Management Configuration Guide.
+ link: https://www.cisco.com/c/en/us/support/cloud-systems-management/application-policy-infrastructure-controller-apic/tsd-products-support-series-home.html
+author:
+- Akini Ross (@akinross)
+"""
+
+EXAMPLES = r"""
+- name: Add a new annotation tag
+ cisco.aci.aci_tag:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dn: SomeValidAciDN
+ tag_key: foo
+ tag_value: bar
+ tag_type: annotation
+ state: present
+ delegate_to: localhost
+- name: Delete annotation tag
+ cisco.aci.aci_tag:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dn: SomeValidAciDN
+ tag_key: foo
+ tag_type: annotation
+ state: absent
+ delegate_to: localhost
+- name: Query annotation tag
+ cisco.aci.aci_tag:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ dn: SomeValidAciDN
+ tag_key: foo
+ tag_type: annotation
+ state: query
+ delegate_to: localhost
+- name: Query annotation tags
+ cisco.aci.aci_tag:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tag_type: annotation
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(
+ dn=dict(type="str"),
+ tag_key=dict(type="str", no_log=False),
+ tag_value=dict(type="str", default=""),
+ tag_type=dict(type="str", choices=["annotation", "instance", "tag"], required=True),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["dn", "tag_key"]],
+ ["state", "present", ["dn", "tag_key"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ dn = module.params.get("dn")
+ tag_key = module.params.get("tag_key")
+ tag_value = module.params.get("tag_value")
+ tag_type = module.params.get("tag_type")
+ state = module.params.get("state")
+
+ if module.params.get("dn") is not None:
+ dn = dn.lstrip("uni/")
+
+ aci_type = dict(
+ annotation=dict(dn="{0}/annotationKey-{1}".format(dn, tag_key), name="tagAnnotation", config=dict(value=tag_value)),
+ instance=dict(dn="{0}/tag-{1}".format(dn, tag_key), name="tagInst", config=dict()),
+ tag=dict(dn="{0}/tagKey-{1}".format(dn, tag_key), name="tagTag", config=dict(value=tag_value)),
+ )
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class=aci_type[tag_type]["name"],
+ aci_rn=aci_type[tag_type]["dn"],
+ module_object=tag_key,
+ target_filter={"name": tag_key},
+ )
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class=aci_type[tag_type]["name"], class_config=aci_type[tag_type]["config"])
+ aci.get_diff(aci_class=aci_type[tag_type]["name"])
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant.py
new file mode 100644
index 00000000..95fa8b85
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant.py
@@ -0,0 +1,264 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant
+short_description: Manage tenants (fv:Tenant)
+description:
+- Manage tenants on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ name, tenant_name ]
+ description:
+ description:
+ - Description for the tenant.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_ap
+- module: cisco.aci.aci_bd
+- module: cisco.aci.aci_contract
+- module: cisco.aci.aci_filter
+- module: cisco.aci.aci_vrf
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:Tenant).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add a new tenant
+ cisco.aci.aci_tenant:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ description: Production tenant
+ state: present
+ delegate_to: localhost
+
+- name: Remove a tenant
+ cisco.aci.aci_tenant:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: absent
+ delegate_to: localhost
+
+- name: Query a tenant
+ cisco.aci.aci_tenant:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all tenants
+ cisco.aci.aci_tenant:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["name", "tenant_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant"]],
+ ["state", "present", ["tenant"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvTenant",
+ class_config=dict(
+ name=tenant,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvTenant")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py
new file mode 100644
index 00000000..55b76f3d
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_action_rule_profile.py
@@ -0,0 +1,277 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant_action_rule_profile
+short_description: Manage action rule profiles (rtctrl:AttrP)
+description:
+- Manage action rule profiles on Cisco ACI fabrics.
+options:
+ action_rule:
+ description:
+ - The name of the action rule profile.
+ type: str
+ aliases: [ action_rule_name, name ]
+ description:
+ description:
+ - The description for the action rule profile.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(rtctrl:AttrP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Create a action rule profile
+ cisco.aci.aci_tenant_action_rule_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ action_rule: my_action_rule
+ tenant: prod
+ state: present
+ delegate_to: localhost
+
+- name: Delete a action rule profile
+ cisco.aci.aci_tenant_action_rule_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ action_rule: my_action_rule
+ tenant: prod
+ state: absent
+ delegate_to: localhost
+
+- name: Query all action rule profiles
+ cisco.aci.aci_tenant_action_rule_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific action rule profile
+ cisco.aci.aci_tenant_action_rule_profile:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ action_rule: my_action_rule
+ tenant: prod
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ action_rule=dict(type="str", aliases=["action_rule_name", "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"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["action_rule", "tenant"]],
+ ["state", "present", ["action_rule", "tenant"]],
+ ],
+ )
+
+ action_rule = module.params.get("action_rule")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="rtctrlAttrP",
+ aci_rn="attr-{0}".format(action_rule),
+ module_object=action_rule,
+ target_filter={"name": action_rule},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="rtctrlAttrP",
+ class_config=dict(
+ name=action_rule,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="rtctrlAttrP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py
new file mode 100644
index 00000000..c1896bb3
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_ep_retention_policy.py
@@ -0,0 +1,364 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant_ep_retention_policy
+short_description: Manage End Point (EP) retention protocol policies (fv:EpRetPol)
+description:
+- Manage End Point (EP) retention protocol policies on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ epr_policy:
+ description:
+ - The name of the end point retention policy.
+ type: str
+ aliases: [ epr_name, name ]
+ bounce_age:
+ description:
+ - Bounce entry aging interval in seconds.
+ - Accepted values range between C(150) and C(65535); 0 is used for infinite.
+ - The APIC defaults to C(630) when unset during creation.
+ type: int
+ bounce_trigger:
+ description:
+ - Determines if the bounce entries are installed by RARP Flood or COOP Protocol.
+ - The APIC defaults to C(coop) when unset during creation.
+ type: str
+ choices: [ coop, flood ]
+ hold_interval:
+ description:
+ - Hold interval in seconds.
+ - Accepted values range between C(5) and C(65535).
+ - The APIC defaults to C(300) when unset during creation.
+ type: int
+ local_ep_interval:
+ description:
+ - Local end point aging interval in seconds.
+ - Accepted values range between C(120) and C(65535); 0 is used for infinite.
+ - The APIC defaults to C(900) when unset during creation.
+ type: int
+ remote_ep_interval:
+ description:
+ - Remote end point aging interval in seconds.
+ - Accepted values range between C(120) and C(65535); 0 is used for infinite.
+ - The APIC defaults to C(300) when unset during creation.
+ type: int
+ move_frequency:
+ description:
+ - Move frequency per second.
+ - Accepted values range between C(0) and C(65535); 0 is used for none.
+ - The APIC defaults to C(256) when unset during creation.
+ type: int
+ description:
+ description:
+ - Description for the End point retention policy.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:EpRetPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Swetha Chunduri (@schunduri)
+"""
+
+EXAMPLES = r"""
+- name: Add a new EPR policy
+ cisco.aci.aci_tenant_ep_retention_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ epr_policy: EPRPol1
+ bounce_age: 630
+ hold_interval: 300
+ local_ep_interval: 900
+ remote_ep_interval: 300
+ move_frequency: 256
+ description: test
+ state: present
+ delegate_to: localhost
+
+- name: Remove an EPR policy
+ cisco.aci.aci_tenant_ep_retention_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ epr_policy: EPRPol1
+ state: absent
+ delegate_to: localhost
+
+- name: Query an EPR policy
+ cisco.aci.aci_tenant_ep_retention_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: production
+ epr_policy: EPRPol1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all EPR policies
+ cisco.aci.aci_tenant_ep_retention_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+BOUNCE_TRIG_MAPPING = dict(
+ coop="protocol",
+ rarp="rarp-flood",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ epr_policy=dict(type="str", aliases=["epr_name", "name"]), # Not required for querying all objects
+ bounce_age=dict(type="int"),
+ bounce_trigger=dict(type="str", choices=["coop", "flood"]),
+ hold_interval=dict(type="int"),
+ local_ep_interval=dict(type="int"),
+ remote_ep_interval=dict(type="int"),
+ description=dict(type="str", aliases=["descr"]),
+ move_frequency=dict(type="int"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["epr_policy", "tenant"]],
+ ["state", "present", ["epr_policy", "tenant"]],
+ ],
+ )
+
+ epr_policy = module.params.get("epr_policy")
+ bounce_age = module.params.get("bounce_age")
+ if bounce_age is not None and bounce_age != 0 and bounce_age not in range(150, 65536):
+ module.fail_json(msg="The bounce_age must be a value of 0 or between 150 and 65535")
+ if bounce_age == 0:
+ bounce_age = "infinite"
+ bounce_trigger = module.params.get("bounce_trigger")
+ if bounce_trigger is not None:
+ bounce_trigger = BOUNCE_TRIG_MAPPING[bounce_trigger]
+ description = module.params.get("description")
+ hold_interval = module.params.get("hold_interval")
+ if hold_interval is not None and hold_interval not in range(5, 65536):
+ module.fail_json(msg="The hold_interval must be a value between 5 and 65535")
+ local_ep_interval = module.params.get("local_ep_interval")
+ if local_ep_interval is not None and local_ep_interval != 0 and local_ep_interval not in range(120, 65536):
+ module.fail_json(msg="The local_ep_interval must be a value of 0 or between 120 and 65535")
+ if local_ep_interval == 0:
+ local_ep_interval = "infinite"
+ move_frequency = module.params.get("move_frequency")
+ if move_frequency is not None and move_frequency not in range(65536):
+ module.fail_json(msg="The move_frequency must be a value between 0 and 65535")
+ if move_frequency == 0:
+ move_frequency = "none"
+ remote_ep_interval = module.params.get("remote_ep_interval")
+ if remote_ep_interval is not None and remote_ep_interval not in range(120, 65536):
+ module.fail_json(msg="The remote_ep_interval must be a value of 0 or between 120 and 65535")
+ if remote_ep_interval == 0:
+ remote_ep_interval = "infinite"
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvEpRetPol",
+ aci_rn="epRPol-{0}".format(epr_policy),
+ module_object=epr_policy,
+ target_filter={"name": epr_policy},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvEpRetPol",
+ class_config=dict(
+ name=epr_policy,
+ descr=description,
+ bounceAgeIntvl=bounce_age,
+ bounceTrig=bounce_trigger,
+ holdIntvl=hold_interval,
+ localEpAgeIntvl=local_ep_interval,
+ remoteEpAgeIntvl=remote_ep_interval,
+ moveFreq=move_frequency,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvEpRetPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py
new file mode 100644
index 00000000..eaae5331
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_dst_group.py
@@ -0,0 +1,391 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant_span_dst_group
+short_description: Manage SPAN destination groups (span:DestGrp)
+description:
+- Manage SPAN destination groups on Cisco ACI fabrics.
+options:
+ destination_group:
+ description:
+ - The name of the SPAN destination group.
+ type: str
+ aliases: [ name, dst_group ]
+ description:
+ description:
+ - The description of the SPAN destination group.
+ type: str
+ aliases: [ descr ]
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ destination_epg:
+ description:
+ - The destination end point group.
+ type: dict
+ suboptions:
+ epg:
+ description:
+ - The name of the end point group.
+ type: str
+ ap:
+ description:
+ - The name of application profile.
+ type: str
+ tenant:
+ description:
+ - The name of the tenant.
+ type: str
+ aliases: [ tenant_name ]
+ source_ip:
+ description:
+ - The source IP address or prefix.
+ type: str
+ destination_ip:
+ description:
+ - The destination IP address.
+ type: str
+ span_version:
+ description:
+ - SPAN version.
+ type: str
+ choices: [ version_1, version_2 ]
+ flow_id:
+ description:
+ - The flow ID of the SPAN packet.
+ type: int
+ ttl:
+ description:
+ - The time to live of the span session packets.
+ type: int
+ mtu:
+ description:
+ - The MTU truncation size for the packets.
+ type: int
+ dscp:
+ description:
+ - The DSCP value for sending the monitored packets using ERSPAN.
+ type: str
+ choices: [ CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, unspecified ]
+ version_enforced:
+ description:
+ - Enforce SPAN version.
+ type: bool
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(span:DestGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Dag Wieers (@dagwieers)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add SPAN destination group
+ cisco.aci.aci_tenant_span_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Tenant1
+ destination_epg:
+ tenant: Test1
+ ap: ap1
+ epg: ep1
+ destination_group: group1
+ description: Test span
+ destination_ip: 10.0.0.1
+ source_ip: 10.0.2.1
+ version_enforced: false
+ span_version: version_1
+ ttl: 2
+ mtu: 1500
+ flow_id: 1
+ dscp: CS1
+ state: present
+ delegate_to: localhost
+
+- name: Remove SPAN destination group
+ cisco.aci.aci_tenant_span_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Tenant1
+ destination_group: group1
+ state: absent
+ delegate_to: localhost
+
+- name: Query SPAN destination group
+ cisco.aci.aci_tenant_span_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: Tenant1
+ destination_group: group1
+ state: query
+ delegate_to: localhost
+
+- name: Query all SPAN destination groups
+ cisco.aci.aci_tenant_span_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def destination_epg_spec():
+ return dict(
+ epg=dict(type="str"),
+ ap=dict(type="str"),
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ )
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ destination_epg=dict(type="dict", options=destination_epg_spec()),
+ destination_group=dict(type="str", aliases=["name", "dst_group"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ name_alias=dict(type="str"),
+ source_ip=dict(type="str"),
+ destination_ip=dict(type="str"),
+ mtu=dict(type="int"),
+ ttl=dict(type="int"),
+ flow_id=dict(type="int"),
+ version_enforced=dict(type="bool"),
+ span_version=dict(type="str", choices=["version_1", "version_2"]),
+ dscp=dict(type="str", choices=["CS0", "CS1", "CS2", "CS3", "CS4", "CS5", "CS6", "CS7", "EF", "VA", "AF11",
+ "AF12", "AF13", "AF21", "AF22", "AF23", "AF31", "AF32", "AF33", "AF41", "AF42", "AF43", "unspecified"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["destination_group", "tenant"]],
+ ["state", "present", ["destination_group", "destination_ip", "source_ip", "destination_epg", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ destination_epg = module.params.get("destination_epg")
+ destination_group = module.params.get("destination_group")
+ description = module.params.get("description")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ destination_ip = module.params.get("destination_ip")
+ source_ip = module.params.get("source_ip")
+ span_version = module.params.get("span_version")
+ name_alias = module.params.get("name_alias")
+ dscp = module.params.get("dscp")
+ mtu = str(module.params.get("mtu"))
+ ttl = str(module.params.get("ttl"))
+ flow_id = str(module.params.get("flow_id"))
+ version_enforced = module.params.get("version_enforced")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="spanDestGrp",
+ aci_rn="destgrp-{0}".format(destination_group),
+ module_object=destination_group,
+ target_filter={"name": destination_group},
+ ),
+ child_classes=["spanDest", "spanRsDestEpg"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ dest_tdn = "uni/tn-{0}/ap-{1}/epg-{2}".format(destination_epg["tenant"], destination_epg["ap"], destination_epg["epg"])
+
+ if version_enforced is True:
+ version_enforced = "yes"
+ else:
+ version_enforced = "no"
+
+ if span_version == "version_1":
+ span_version = "ver1"
+ else:
+ span_version = "ver2"
+
+ child_configs = [
+ dict(spanDest=dict(attributes=dict(name=destination_group), children=[dict(spanRsDestEpg=dict(attributes=dict(ip=destination_ip,
+ srcIpPrefix=source_ip, ver=span_version, verEnforced=version_enforced, ttl=ttl,
+ mtu=mtu, flowId=flow_id, dscp=dscp, tDn=dest_tdn)))])),
+ ]
+
+ aci.payload(
+ aci_class="spanDestGrp",
+ class_config=dict(
+ name=destination_group,
+ descr=description,
+ nameAlias=name_alias,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="spanDestGrp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py
new file mode 100644
index 00000000..7db62ffe
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group.py
@@ -0,0 +1,296 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant_span_src_group
+short_description: Manage SPAN source groups (span:SrcGrp)
+description:
+- Manage SPAN source groups on Cisco ACI fabrics.
+options:
+ admin_state:
+ description:
+ - Enable or disable the span sources.
+ - The APIC defaults to C(yes) when unset during creation.
+ type: bool
+ description:
+ description:
+ - The description for Span source group.
+ type: str
+ aliases: [ descr ]
+ dst_group:
+ description:
+ - The Span destination group to associate with the source group.
+ type: str
+ src_group:
+ description:
+ - The name of the Span source group.
+ type: str
+ aliases: [ name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(span:SrcGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Create a SPAN source group
+ cisco.aci.aci_tenant_span_src_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ tenant: prod
+ state: present
+ delegate_to: localhost
+
+- name: Delete a SPAN source group
+ cisco.aci.aci_tenant_span_src_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ tenant: prod
+ state: absent
+ delegate_to: localhost
+
+- name: Query all SPAN source groups
+ cisco.aci.aci_tenant_span_src_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific SPAN source group
+ cisco.aci.aci_tenant_span_src_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ tenant: prod
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ src_group=dict(type="str", aliases=["name"]), # Not required for querying all objects
+ admin_state=dict(type="bool"),
+ description=dict(type="str", aliases=["descr"]),
+ dst_group=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["src_group", "tenant"]],
+ ["state", "present", ["src_group", "tenant"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ admin_state = aci.boolean(module.params.get("admin_state"), "enabled", "disabled")
+ description = module.params.get("description")
+ dst_group = module.params.get("dst_group")
+ src_group = module.params.get("src_group")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="spanSrcGrp",
+ aci_rn="srcgrp-{0}".format(src_group),
+ module_object=src_group,
+ target_filter={"name": src_group},
+ ),
+ child_classes=["spanSpanLbl"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="spanSrcGrp",
+ class_config=dict(
+ adminSt=admin_state,
+ descr=description,
+ name=src_group,
+ nameAlias=name_alias,
+ ),
+ child_configs=[{"spanSpanLbl": {"attributes": {"name": dst_group}}}],
+ )
+
+ aci.get_diff(aci_class="spanSrcGrp")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_src.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_src.py
new file mode 100644
index 00000000..ff490f08
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_src.py
@@ -0,0 +1,319 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2022, Akini Ross (@akinross)
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant_span_src_group_src
+short_description: Manage SPAN source group sources (span:Src)
+description:
+- Manage SPAN source group sources on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the Span source.
+ type: str
+ description:
+ description:
+ - The description for Span source.
+ type: str
+ aliases: [ descr ]
+ src_group:
+ description:
+ - The name of the Span source group.
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+ direction:
+ description:
+ - The direction of the SPAN source.
+ type: str
+ choices: [ incoming, outgoing, both ]
+ src_epg:
+ description:
+ - The name of the Span source epg.
+ type: str
+ aliases: [ epg ]
+ src_ap:
+ description:
+ - The name of the Span source ap.
+ type: str
+ aliases: [ ap ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(span:SrcGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Akini Ross (@akinross)
+"""
+
+EXAMPLES = r"""
+- name: Create a SPAN source
+ cisco.aci.aci_tenant_span_src_group_src:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ tenant: prod
+ name: test
+ direction: incoming
+ src_epg: epg1
+ state: present
+ delegate_to: localhost
+
+- name: Delete a SPAN source
+ cisco.aci.aci_tenant_span_src_group_src:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ tenant: prod
+ name: test
+ state: absent
+ delegate_to: localhost
+
+- name: Query all SPAN sources
+ cisco.aci.aci_tenant_span_src_group_src:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific SPAN source
+ cisco.aci.aci_tenant_span_src_group_src:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: test
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+DIRECTION_MAP = {
+ "incoming": "in", "outgoing": "out", "both": "both"
+}
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ description=dict(type="str", aliases=["descr"]),
+ direction=dict(type="str", choices=["incoming", "outgoing", "both"]),
+ name=dict(type="str"), # Not required for querying all objects
+ src_ap=dict(type="str", aliases=["ap"]),
+ src_epg=dict(type="str", aliases=["epg"]),
+ 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"]), # Not required for querying all objects
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name", "src_group", "tenant"]],
+ ["state", "present", ["name", "direction", "src_group", "tenant"]],
+ ],
+ required_together=[('src_ap', 'src_epg')],
+ )
+
+ aci = ACIModule(module)
+
+ description = module.params.get("description")
+ direction = module.params.get("direction")
+ name = module.params.get("name")
+ src_ap = module.params.get("src_ap")
+ src_epg = module.params.get("src_epg")
+ src_group = module.params.get("src_group")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="spanSrcGrp",
+ aci_rn="srcgrp-{0}".format(src_group),
+ module_object=src_group,
+ target_filter={"name": src_group},
+ ),
+ subclass_2=dict(
+ aci_class="spanSrc",
+ aci_rn="src-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["spanRsSrcToEpg"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+
+ tdn = None
+ if src_epg:
+ tdn = "uni/tn-{0}/ap-{1}/epg-{2}".format(tenant, src_ap, src_epg)
+
+ aci.payload(
+ aci_class="spanSrc",
+ class_config=dict(descr=description, name=name, dir=DIRECTION_MAP.get(direction)),
+ child_configs=[{"spanRsSrcToEpg": {"attributes": {"tDn": tdn}}}]
+ )
+
+ aci.get_diff(aci_class="spanSrc")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py
new file mode 100644
index 00000000..8ad77bdc
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_tenant_span_src_group_to_dst_group.py
@@ -0,0 +1,295 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_tenant_span_src_group_to_dst_group
+short_description: Bind SPAN source groups to destination groups (span:SpanLbl)
+description:
+- Bind SPAN source groups to associated destination groups on Cisco ACI fabrics.
+options:
+ description:
+ description:
+ - The description for Span source group to destination group binding.
+ type: str
+ aliases: [ descr ]
+ dst_group:
+ description:
+ - The Span destination group to associate with the source group.
+ type: str
+ src_group:
+ description:
+ - The name of the Span source group.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant), C(src_group), and C(dst_group) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_tenant_span_src_group), and M(cisco.aci.aci_tenant_span_dst_group) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_tenant_span_src_group
+- module: cisco.aci.aci_tenant_span_dst_group
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(span:SrcGrp).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Bind SPAN source to destination group
+ cisco.aci.aci_tenant_span_src_group_to_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ dst_group: my_span_destination_group
+ tenant: prod
+ state: present
+ delegate_to: localhost
+
+- name: Unbind SPAN source to destination group
+ cisco.aci.aci_tenant_span_src_group_to_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ dst_group: my_span_destination_group
+ tenant: prod
+ state: absent
+ delegate_to: localhost
+
+- name: Query all SPAN source to destination group bindings
+ cisco.aci.aci_tenant_span_src_group_to_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a specific SPAN source to destination group binding
+ cisco.aci.aci_tenant_span_src_group_to_dst_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ src_group: my_span_source_group
+ dst_group: my_span_destination_group
+ tenant: prod
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ dst_group=dict(type="str"), # Not required for querying all objects
+ src_group=dict(type="str"), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["dst_group", "src_group", "tenant"]],
+ ["state", "present", ["dst_group", "src_group", "tenant"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ dst_group = module.params.get("dst_group")
+ src_group = module.params.get("src_group")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ name_alias = module.params.get("name_alias")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="spanSrcGrp",
+ aci_rn="srcgrp-{0}".format(src_group),
+ module_object=src_group,
+ target_filter={"name": src_group},
+ ),
+ subclass_2=dict(
+ aci_class="spanSpanLbl",
+ aci_rn="spanlbl-{0}".format(dst_group),
+ module_object=dst_group,
+ target_filter={"name": dst_group},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="spanSpanLbl",
+ class_config=dict(
+ descr=description,
+ name=dst_group,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="spanSpanLbl")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py b/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py
new file mode 100644
index 00000000..bd7d5dce
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool.py
@@ -0,0 +1,286 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Jacob McGill (@jmcgill298)
+# Copyright: (c) 2018, Dag Wieers (@dagwieers) <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vlan_pool
+short_description: Manage VLAN pools (fvns:VlanInstP)
+description:
+- Manage VLAN pools on Cisco ACI fabrics.
+options:
+ pool_allocation_mode:
+ description:
+ - The method used for allocating VLANs to resources.
+ type: str
+ choices: [ dynamic, static]
+ aliases: [ allocation_mode, mode ]
+ description:
+ description:
+ - Description for the C(pool).
+ type: str
+ aliases: [ descr ]
+ pool:
+ description:
+ - The name of the pool.
+ type: str
+ aliases: [ name, pool_name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_encap_pool
+- module: cisco.aci.aci_vlan_pool_encap_block
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fvns:VlanInstP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a new VLAN pool
+ cisco.aci.aci_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: dynamic
+ description: Production VLANs
+ state: present
+ delegate_to: localhost
+
+- name: Remove a VLAN pool
+ cisco.aci.aci_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: dynamic
+ state: absent
+ delegate_to: localhost
+
+- name: Query a VLAN pool
+ cisco.aci.aci_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: dynamic
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all VLAN pools
+ cisco.aci.aci_vlan_pool:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ pool=dict(type="str", aliases=["name", "pool_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ pool_allocation_mode=dict(type="str", aliases=["allocation_mode", "mode"], choices=["dynamic", "static"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["pool"]],
+ ["state", "present", ["pool"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ pool = module.params.get("pool")
+ pool_allocation_mode = module.params.get("pool_allocation_mode")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ pool_name = pool
+
+ # ACI Pool URL requires the allocation mode for vlan and vsan pools (ex: uni/infra/vlanns-[poolname]-static)
+ if 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' when 'pool' is provided")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvnsVlanInstP",
+ aci_rn="infra/vlanns-{0}".format(pool_name),
+ module_object=pool,
+ target_filter={"name": pool},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvnsVlanInstP",
+ class_config=dict(
+ allocMode=pool_allocation_mode,
+ descr=description,
+ name=pool,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="fvnsVlanInstP")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py b/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py
new file mode 100644
index 00000000..fea47b48
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vlan_pool_encap_block.py
@@ -0,0 +1,367 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2017, Jacob McGill (jmcgill298)
+# Copyright: (c) 2018, Dag Wieers (dagwieers) <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vlan_pool_encap_block
+short_description: Manage encap blocks assigned to VLAN pools (fvns:EncapBlk)
+description:
+- Manage VLAN encap blocks that are assigned to VLAN pools on Cisco ACI fabrics.
+options:
+ allocation_mode:
+ description:
+ - The method used for allocating encaps to resources.
+ type: str
+ choices: [ dynamic, inherit, static]
+ aliases: [ mode ]
+ description:
+ description:
+ - Description for the pool encap block.
+ type: str
+ aliases: [ descr ]
+ pool:
+ description:
+ - The name of the pool that the encap block should be assigned to.
+ type: str
+ aliases: [ pool_name ]
+ pool_allocation_mode:
+ description:
+ - The method used for allocating encaps to resources.
+ type: str
+ choices: [ dynamic, static]
+ aliases: [ pool_mode ]
+ block_end:
+ description:
+ - The end of encap block.
+ type: int
+ aliases: [ end ]
+ block_name:
+ description:
+ - The name to give to the encap block.
+ type: str
+ aliases: [ name ]
+ block_start:
+ description:
+ - The start of the encap block.
+ type: int
+ aliases: [ start ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(pool) must exist in order to add or delete a encap block.
+seealso:
+- module: cisco.aci.aci_encap_pool_range
+- module: cisco.aci.aci_vlan_pool
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fvns:EncapBlk).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+- Dag Wieers (@dagwieers)
+"""
+
+EXAMPLES = r"""
+- name: Add a new VLAN encap block
+ cisco.aci.aci_vlan_pool_encap_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: static
+ block_start: 20
+ block_end: 50
+ state: present
+ delegate_to: localhost
+
+- name: Remove a VLAN encap block
+ cisco.aci.aci_vlan_pool_encap_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: static
+ block_start: 20
+ block_end: 50
+ state: absent
+ delegate_to: localhost
+
+- name: Query a VLAN encap block
+ cisco.aci.aci_vlan_pool_encap_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: static
+ block_start: 20
+ block_end: 50
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query a VLAN pool for encap blocks
+ cisco.aci.aci_vlan_pool_encap_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ pool: production
+ pool_allocation_mode: static
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all VLAN encap blocks
+ cisco.aci.aci_vlan_pool_encap_block:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ pool=dict(type="str", aliases=["pool_name"]), # Not required for querying all objects
+ 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
+ allocation_mode=dict(type="str", aliases=["mode"], choices=["dynamic", "inherit", "static"]),
+ description=dict(type="str", aliases=["descr"]),
+ pool_allocation_mode=dict(type="str", aliases=["pool_mode"], choices=["dynamic", "static"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['pool', 'pool_allocation_mode', 'block_end', 'block_start']],
+ ['state', 'present', ['pool', 'pool_allocation_mode', 'block_end', 'block_start']],
+ ],
+ )
+
+ allocation_mode = module.params.get("allocation_mode")
+ description = module.params.get("description")
+ pool = module.params.get("pool")
+ pool_allocation_mode = module.params.get("pool_allocation_mode")
+ block_end = module.params.get("block_end")
+ block_name = module.params.get("block_name")
+ block_start = module.params.get("block_start")
+ state = module.params.get("state")
+ name_alias = module.params.get("name_alias")
+
+ if block_end is not None:
+ encap_end = "vlan-{0}".format(block_end)
+ else:
+ encap_end = None
+
+ if block_start is not None:
+ encap_start = "vlan-{0}".format(block_start)
+ else:
+ encap_start = None
+
+ # Collect proper mo information
+ aci_block_mo = "from-[{0}]-to-[{1}]".format(encap_start, encap_end)
+ pool_name = pool
+
+ # Validate block_end and block_start are valid for its respective encap type
+ for encap_id in block_end, block_start:
+ if encap_id is not None:
+ if not 1 <= encap_id <= 4094:
+ module.fail_json(msg="vlan pools must have 'block_start' and 'block_end' values between 1 and 4094")
+
+ if block_end is not None and block_start is not None:
+ # Validate block_start is less than block_end
+ if block_start > block_end:
+ module.fail_json(msg="The 'block_start' must be less than or equal to the 'block_end'")
+
+ elif block_end is None and block_start is None:
+ if block_name is None:
+ # Reset range managed object to None for aci util to properly handle query
+ aci_block_mo = None
+
+ # ACI Pool URL requires the allocation mode (ex: uni/infra/vlanns-[poolname]-static)
+ if 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' when 'pool' is provided")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvnsVlanInstP",
+ aci_rn="infra/vlanns-{0}".format(pool_name),
+ module_object=pool,
+ target_filter={"name": pool},
+ ),
+ subclass_1=dict(
+ aci_class="fvnsEncapBlk",
+ aci_rn=aci_block_mo,
+ module_object=aci_block_mo,
+ target_filter={"from": encap_start, "to": encap_end, "name": block_name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvnsEncapBlk",
+ class_config={
+ "allocMode": allocation_mode,
+ "descr": description,
+ "from": encap_start,
+ "name": block_name,
+ "to": encap_end,
+ "nameAlias": name_alias,
+ },
+ )
+
+ aci.get_diff(aci_class="fvnsEncapBlk")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vmm_controller.py b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_controller.py
new file mode 100644
index 00000000..b066d8b9
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_controller.py
@@ -0,0 +1,372 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Manuel Widmer <mawidmer@cisco.com>
+# Copyright: (c) 2021, Anvitha Jain (@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vmm_controller
+short_description: Manage VMM Controller for virtual domains profiles (vmm:CtrlrP)
+description:
+- Manage vCenter virtual domains on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - Name of VMM Controller.
+ type: str
+ aliases: []
+ controller_hostname:
+ description:
+ - Hostname or IP of the controller.
+ type: str
+ aliases: []
+ dvs_version:
+ description:
+ - Version of the VMware DVS.
+ type: str
+ aliases: []
+ choices: [ 'unmanaged', '5.1', '5.5', '6.0', '6.5', '6.6', '7.0' ]
+ stats_collection:
+ description:
+ - Whether stats collection is enabled.
+ type: str
+ choices: [ 'enabled', 'disabled' ]
+ default: disabled
+ aliases: []
+ domain:
+ description:
+ - Name of the virtual domain profile.
+ type: str
+ aliases: [ domain_name, domain_profile ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ credentials:
+ description:
+ - Name of the VMM credentials to be used
+ type: str
+ inband_management_epg:
+ description:
+ - Name of the management EPG to be used by the controller. Only supports in-band management EPGs for now.
+ type: str
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ datacenter:
+ description:
+ - Name of the data center, as seen in vCenter
+ type: str
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vmm_credential
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vmm:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Manuel Widmer (@lumean)
+"""
+
+EXAMPLES = r"""
+- name: Add controller to VMware VMM domain
+ cisco.aci.aci_vmm_controller:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ name: vCenter1
+ controller_hostname: 10.1.1.1
+ dvs_version: unmanaged
+ vm_provider: vmware
+ credentials: vCenterCredentials1
+ datacenter: DC1
+ state: present
+
+- name: Remove controller from VMware VMM domain
+ cisco.aci.aci_vmm_controller:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ name: vCenter1
+ vm_provider: vmware
+ state: absent
+
+- name: Query a specific VMware VMM controller
+ cisco.aci.aci_vmm_controller:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ name: vCenter1
+ vm_provider: vmware
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all VMware VMM controller
+ cisco.aci.aci_vmm_controller:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ vm_provider: vmware
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+VM_SCOPE_MAPPING = dict(
+ cloudfoundry="cloudfoundry",
+ kubernetes="kubernetes",
+ microsoft="MicrosoftSCVMM",
+ openshift="openshift",
+ openstack="openstack",
+ redhat="rhev",
+ vmware="vm",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ name=dict(type="str"),
+ controller_hostname=dict(type="str"),
+ dvs_version=dict(type="str", choices=["unmanaged", "5.1", "5.5", "6.0", "6.5", "6.6", "7.0"]),
+ stats_collection=dict(type="str", default="disabled", choices=["enabled", "disabled"]),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ credentials=dict(type="str"),
+ inband_management_epg=dict(type="str"),
+ name_alias=dict(type="str"),
+ datacenter=dict(type="str"),
+ vm_provider=dict(type="str", choices=list(VM_PROVIDER_MAPPING.keys())),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["domain", "vm_provider", "name"]],
+ ["state", "present", ["domain", "vm_provider", "name"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ controller_hostname = module.params.get("controller_hostname")
+ dvs_version = module.params.get("dvs_version")
+ stats_collection = module.params.get("stats_collection")
+ domain = module.params.get("domain")
+ state = module.params.get("state")
+ credentials = module.params.get("credentials")
+ inband_management_epg = module.params.get("inband_management_epg")
+ name_alias = module.params.get("name_alias")
+ datacenter = module.params.get("datacenter")
+ vm_provider = module.params.get("vm_provider")
+
+ controller_class = "vmmCtrlrP"
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="vmmProvP",
+ aci_rn="vmmp-{0}".format(VM_PROVIDER_MAPPING.get(vm_provider)),
+ module_object=vm_provider,
+ target_filter={"name": vm_provider},
+ ),
+ subclass_1=dict(
+ aci_class="vmmDomP",
+ aci_rn="dom-{0}".format(domain),
+ module_object=domain,
+ target_filter={"name": domain},
+ ),
+ subclass_2=dict(
+ aci_class="vmmCtrlrP",
+ aci_rn="ctrlr-{0}".format(name),
+ module_object=name,
+ target_filter={"name": "name"},
+ ),
+ child_classes=["vmmRsMgmtEPg", "vmmRsAcc"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ children = list()
+ if inband_management_epg is not None:
+ children.append(dict(vmmRsMgmtEPg=dict(attributes=dict(tDn="uni/tn-mgmt/mgmtp-default/inb-{0}".format(inband_management_epg)))))
+
+ if credentials is not None:
+ children.append(
+ dict(vmmRsAcc=dict(attributes=dict(tDn="uni/vmmp-{0}/dom-{1}/usracc-{2}".format(VM_PROVIDER_MAPPING.get(vm_provider), domain, credentials))))
+ )
+
+ aci.payload(
+ aci_class=controller_class,
+ class_config=dict(
+ name=name,
+ hostOrIp=controller_hostname,
+ dvsVersion=dvs_version,
+ statsMode=stats_collection,
+ rootContName=datacenter,
+ nameAlias=name_alias,
+ scope=VM_SCOPE_MAPPING.get(vm_provider),
+ ),
+ child_configs=children,
+ )
+
+ aci.get_diff(aci_class=controller_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py
new file mode 100644
index 00000000..8f23aabe
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_credential.py
@@ -0,0 +1,319 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vmm_credential
+short_description: Manage virtual domain credential profiles (vmm:UsrAccP)
+description:
+- Manage virtual domain credential profiles on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - Name of the credential profile.
+ type: str
+ aliases: [ credential_name, credential_profile ]
+ credential_password:
+ description:
+ - VMM controller password.
+ type: str
+ aliases: []
+ credential_username:
+ description:
+ - VMM controller username.
+ type: str
+ aliases: []
+ description:
+ description:
+ - Description for the tenant.
+ type: str
+ aliases: [ descr ]
+ domain:
+ description:
+ - Name of the virtual domain profile.
+ type: str
+ aliases: [ domain_name, domain_profile, name ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vmm:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jason Juenger (@jasonjuenger)
+"""
+
+EXAMPLES = r"""
+- name: Add credential to VMware VMM domain
+ cisco.aci.aci_vmm_credential:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ description: secure credential
+ name: vCenterCredential
+ credential_username: vCenterUsername
+ credential_password: vCenterPassword
+ vm_provider: vmware
+ state: present
+
+- name: Remove credential from VMware VMM domain
+ cisco.aci.aci_vmm_credential:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ name: myCredential
+ vm_provider: vmware
+ state: absent
+
+- name: Query a specific VMware VMM credential
+ cisco.aci.aci_vmm_credential:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ name: vCenterCredential
+ vm_provider: vmware
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all VMware VMM credentials
+ cisco.aci.aci_vmm_credential:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ vm_provider: vmware
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["credential_name", "credential_profile"]),
+ credential_password=dict(type="str", no_log=True),
+ credential_username=dict(type="str"),
+ description=dict(type="str", aliases=["descr"]),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ vm_provider=dict(type="str", choices=list(VM_PROVIDER_MAPPING.keys())),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["domain"]],
+ ["state", "present", ["domain"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ credential_password = module.params.get("credential_password")
+ credential_username = module.params.get("credential_username")
+ description = module.params.get("description")
+ domain = module.params.get("domain")
+ state = module.params.get("state")
+ vm_provider = module.params.get("vm_provider")
+ name_alias = module.params.get("name_alias")
+
+ credential_class = "vmmUsrAccP"
+ usracc_mo = "uni/vmmp-{0}/dom-{1}/usracc-{2}".format(VM_PROVIDER_MAPPING.get(vm_provider), domain, name)
+ usracc_rn = "vmmp-{0}/dom-{1}/usracc-{2}".format(VM_PROVIDER_MAPPING.get(vm_provider), domain, name)
+
+ # Ensure that querying all objects works when only domain is provided
+ if name is None:
+ usracc_mo = None
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class=credential_class,
+ aci_rn=usracc_rn,
+ module_object=usracc_mo,
+ target_filter={"name": domain, "usracc": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=credential_class,
+ class_config=dict(
+ descr=description,
+ name=name,
+ pwd=credential_password,
+ usr=credential_username,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class=credential_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink.py b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink.py
new file mode 100644
index 00000000..d85d21de
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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': 'certified'}
+
+DOCUMENTATION = r'''
+---
+module: aci_vmm_uplink
+short_description: Manage VMM uplinks (vmm:UplinkP)
+description:
+- Manage VMM Uplinks on Cisco ACI fabrics.
+options:
+ domain:
+ description:
+ - Name of the VMM domain
+ type: str
+ uplink_id:
+ description:
+ - Numerical ID of the uplink
+ type: int
+ uplink_name:
+ description:
+ - Name of the uplink
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(domain) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_domain) module can be used for this.
+seealso:
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vmm_uplink_container
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vmm:UplinkP).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new uplink
+ cisco.aci.aci_vmm_uplink:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: my_vmm_domain
+ uplink_id: 1
+ uplink_name: uplink1
+ state: present
+ delegate_to: localhost
+
+- name: Delete uplink container
+ cisco.aci.aci_vmm_uplink:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: my_vmm_domain
+ uplink_id: 1
+ state: absent
+ delegate_to: localhost
+
+- name: Query uplink container
+ cisco.aci.aci_vmm_uplink_container:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: my_vmm_domain
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ domain=dict(type='str'),
+ uplink_id=dict(type='int'),
+ uplink_name=dict(type='str'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['uplink_id', 'domain']],
+ ['state', 'present', ['uplink_id', 'domain']],
+ ],
+ )
+
+ domain = module.params.get('domain')
+ uplink_id = module.params.get('uplink_id')
+ uplink_name = module.params.get('uplink_name')
+ state = module.params.get('state')
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class='vmmProvP',
+ aci_rn='vmmp-VMware',
+ module_object='VMware',
+ target_filter={'name': 'VMware'},
+ ),
+ subclass_1=dict(
+ aci_class='vmmDomP',
+ aci_rn='dom-{0}'.format(domain),
+ module_object=domain,
+ target_filter={'name': domain},
+ ),
+ subclass_2=dict(
+ aci_class='vmmUplinkPCont',
+ aci_rn='uplinkpcont',
+ module_object='uplinkpcont',
+ target_filter={'rn': 'uplinkpcont'},
+ ),
+ subclass_3=dict(
+ aci_class='vmmUplinkP',
+ aci_rn='uplinkp-{0}'.format(uplink_id),
+ module_object=uplink_id,
+ target_filter={'uplinkId': uplink_id},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='vmmUplinkP',
+ class_config=dict(
+ uplinkId=uplink_id,
+ uplinkName=uplink_name,
+ ),
+ )
+
+ aci.get_diff(aci_class='vmmUplinkP')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink_container.py b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink_container.py
new file mode 100644
index 00000000..f8873123
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_uplink_container.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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': 'certified'}
+
+DOCUMENTATION = r'''
+---
+module: aci_vmm_uplink_container
+short_description: Manage VMM uplink containers (vmm:UplinkPCont)
+description:
+- Manage VMM Uplink containers on Cisco ACI fabrics.
+- Individual uplinks within the container are managed using the M(cisco.aci.aci_vmm_uplink) module
+options:
+ domain:
+ description:
+ - Name of the VMM domain
+ type: str
+ num_of_uplinks:
+ description:
+ - Number of uplinks in the container
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+notes:
+- The C(domain) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_domain) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_domain
+- module: cisco.aci.aci_vrf
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vmm:UplinkPCont).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+'''
+
+EXAMPLES = r'''
+- name: Add a new uplink container
+ cisco.aci.aci_vmm_uplink_container:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: my_vmm_domain
+ num_of_uplinks: 2
+ state: present
+ delegate_to: localhost
+
+- name: Delete uplink container
+ cisco.aci.aci_vmm_uplink_container:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: my_vmm_domain
+ state: absent
+ delegate_to: localhost
+
+- name: Query uplink container
+ cisco.aci.aci_vmm_uplink_container:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: my_vmm_domain
+ state: query
+ delegate_to: localhost
+ register: query_result
+'''
+
+RETURN = r'''
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ domain=dict(type='str'),
+ num_of_uplinks=dict(type='int'),
+ state=dict(type='str', default='present', choices=['absent', 'present', 'query']),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'absent', ['domain']],
+ ['state', 'present', ['domain', 'num_of_uplinks']],
+ ],
+ )
+
+ domain = module.params.get('domain')
+ num_of_uplinks = module.params.get('num_of_uplinks')
+ state = module.params.get('state')
+
+ aci = ACIModule(module)
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class='vmmProvP',
+ aci_rn='vmmp-VMware',
+ module_object='VMware',
+ target_filter={'name': 'VMware'},
+ ),
+ subclass_1=dict(
+ aci_class='vmmDomP',
+ aci_rn='dom-{0}'.format(domain),
+ module_object=domain,
+ target_filter={'name': domain},
+ ),
+ subclass_2=dict(
+ aci_class='vmmUplinkPCont',
+ aci_rn='uplinkpcont',
+ module_object='uplinkpcont',
+ target_filter={'rn': 'uplinkpcont'},
+ ),
+ child_classes=['vmmUplinkP'],
+ )
+
+ aci.get_existing()
+
+ if state == 'present':
+ aci.payload(
+ aci_class='vmmUplinkPCont',
+ class_config=dict(
+ numOfUplinks=num_of_uplinks,
+ ),
+ )
+
+ aci.get_diff(aci_class='vmmUplinkPCont')
+
+ aci.post_config()
+
+ elif state == 'absent':
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vmm_vswitch_policy.py b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_vswitch_policy.py
new file mode 100644
index 00000000..206e5877
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vmm_vswitch_policy.py
@@ -0,0 +1,471 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2021, Manuel Widmer <mawidmer@cisco.com>
+# Copyright: (c) 2021, Anvitha Jain (@anvitha-jain) <anvjain@cisco.com>
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vmm_vswitch_policy
+short_description: Manage vSwitch policy for VMware virtual domains profiles (vmm:DomP)
+description:
+- Manage vSwitch policy for VMware VMM domains on Cisco ACI fabrics.
+options:
+ port_channel_policy:
+ description:
+ - Name of the fabric access port-channel policy.
+ type: str
+ lldp_policy:
+ description:
+ - Name of the fabric access LLDP policy.
+ type: str
+ cdp_policy:
+ description:
+ - Name of the fabric access CDP policy.
+ type: str
+ mtu_policy:
+ description:
+ - VMWare only.
+ - Name of the fabric access MTU policy.
+ type: str
+ domain:
+ description:
+ - Name of the virtual domain profile.
+ type: str
+ aliases: [ domain_name, domain_profile ]
+ enhanced_lag:
+ description:
+ - List of enhanced LAG policies if vSwitch needs to be connected via VPC.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the enhanced Lag policy.
+ type: str
+ required: true
+ lacp_mode:
+ description:
+ - LACP port channel mode.
+ type: str
+ choices: [ active, passive ]
+ load_balancing_mode:
+ description:
+ - Load balancing mode of the port channel.
+ - See also https://pubhub.devnetcloud.com/media/apic-mim-ref-421/docs/MO-lacpEnhancedLagPol.html.
+ type: str
+ choices:
+ - dst-ip
+ - dst-ip-l4port
+ - dst-ip-vlan
+ - dst-ip-l4port-vlan
+ - dst-mac
+ - dst-l4port
+ - src-ip
+ - src-ip-l4port
+ - src-ip-vlan
+ - src-ip-l4port-vlan
+ - src-mac
+ - src-l4port
+ - src-dst-ip
+ - src-dst-ip-l4port
+ - src-dst-ip-vlan
+ - src-dst-ip-l4port-vlan
+ - src-dst-mac
+ - src-dst-l4port
+ - src-port-id
+ - vlan
+ number_uplinks:
+ description:
+ - Number of uplinks, must be between 2 and 8.
+ type: int
+ stp_policy:
+ description:
+ - SCVMM only.
+ - Name of the STP policy.
+ type: str
+ netflow_exporter:
+ description:
+ - Parameters for the netflow exporter policy
+ type: dict
+ suboptions:
+ name:
+ description:
+ - Name of the netflow exporter policy
+ type: str
+ required: true
+ active_flow_timeout:
+ description:
+ - Specifies the delay in seconds that NetFlow waits after the active flow is initiated, after which NetFlow sends the collected data.
+ - The range is from 60 to 3600. The default value is 60
+ type: int
+ idle_flow_timeout:
+ description:
+ - Specifies the delay in seconds that NetFlow waits after the idle flow is initiated, after which NetFlow sends the collected data.
+ - The range is from 10 to 600. The default value is 15.
+ type: int
+ sampling_rate:
+ description:
+ - (VDS only) Specifies how many packets that NetFlow will drop after every collected packet.
+ If you specify a value of 0, then NetFlow does not drop any packets.
+ - The range is from 0 to 1000. The default value is 0.
+ type: int
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ vm_provider:
+ description:
+ - The VM platform for VMM Domains.
+ - Support for Kubernetes was added in ACI v3.0.
+ - Support for CloudFoundry, OpenShift and Red Hat was added in ACI v3.1.
+ type: str
+ choices: [ cloudfoundry, kubernetes, microsoft, openshift, openstack, redhat, vmware ]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- module: cisco.aci.aci_domain
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vmm:DomP)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Manuel Widmer (@lumean)
+- Anvitha Jain (@anvitha-jain)
+"""
+
+EXAMPLES = r"""
+- name: Add a vSwitch policy with LLDP
+ cisco.aci.aci_vmm_vswitch_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ lldp_policy: LLDP_ENABLED
+ domain: vmware_dom
+ vm_provider: vmware
+ state: present
+
+- name: Add a vSwitch policy with link aggregation
+ cisco.aci.aci_vmm_vswitch_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ port_channel_policy: LACP_ACTIVE
+ lldp_policy: LLDP_ENABLED
+ domain: vmware_dom
+ vm_provider: vmware
+ enhanced_lag:
+ - name: my_lacp_uplink
+ lacp_mode: active
+ load_balancing_mode: src-dst-ip
+ number_uplinks: 2
+ state: present
+
+- name: Remove vSwitch Policy from VMware VMM domain
+ cisco.aci.aci_vmm_vswitch_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ vm_provider: vmware
+ state: absent
+
+- name: Query the vSwitch policy of the VMWare domain
+ cisco.aci.aci_vmm_vswitch_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ domain: vmware_dom
+ vm_provider: vmware
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, enhanced_lag_spec, netflow_spec
+from ansible_collections.cisco.aci.plugins.module_utils.aci import aci_annotation_spec, aci_owner_spec
+# via UI vSwitch Policy can only be added for VMware and Microsoft vmm domains
+# behavior for other domains is currently untested.
+VM_PROVIDER_MAPPING = dict(
+ cloudfoundry="CloudFoundry",
+ kubernetes="Kubernetes",
+ microsoft="Microsoft",
+ openshift="OpenShift",
+ openstack="OpenStack",
+ redhat="Redhat",
+ vmware="VMware",
+)
+
+# enhanced_lag_spec = dict(
+# name=dict(type='str', required=True),
+# lacp_mode=dict(type='str', choices=['active', 'passive']),
+# load_balancing_mode=dict(
+# type='str',
+# choices=['dst-ip', 'dst-ip-l4port', 'dst-ip-vlan', 'dst-ip-l4port-vlan', 'dst-mac', 'dst-l4port',
+# 'src-ip', 'src-ip-l4port', 'src-ip-vlan', 'src-ip-l4port-vlan', 'src-mac', 'src-l4port',
+# 'src-dst-ip', 'src-dst-ip-l4port', 'src-dst-ip-vlan', 'src-dst-ip-l4port-vlan', 'src-dst-mac',
+# 'src-dst-l4port', 'src-port-id', 'vlan']),
+# number_uplinks=dict(type='int'),
+# )
+
+# netflow_spec = dict(
+# name=dict(type='str', required=True),
+# active_flow_timeout=dict(type='int'),
+# idle_flow_timeout=dict(type='int'),
+# sampling_rate=dict(type='int'),
+# )
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ port_channel_policy=dict(type="str"),
+ lldp_policy=dict(type="str"),
+ cdp_policy=dict(type="str"),
+ mtu_policy=dict(type="str"),
+ stp_policy=dict(type="str"),
+ enhanced_lag=dict(type="list", elements="dict", options=enhanced_lag_spec()),
+ netflow_exporter=dict(type="dict", options=netflow_spec()),
+ domain=dict(type="str", aliases=["domain_name", "domain_profile"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ vm_provider=dict(type="str", choices=list(VM_PROVIDER_MAPPING.keys())),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["domain", "vm_provider"]],
+ ["state", "present", ["domain", "vm_provider"]],
+ ],
+ )
+
+ port_channel_policy = module.params.get("port_channel_policy")
+ lldp_policy = module.params.get("lldp_policy")
+ cdp_policy = module.params.get("cdp_policy")
+ mtu_policy = module.params.get("mtu_policy")
+ stp_policy = module.params.get("stp_policy")
+ netflow_exporter = module.params.get("netflow_exporter")
+ enhanced_lag = module.params.get("enhanced_lag")
+ domain = module.params.get("domain")
+ state = module.params.get("state")
+ vm_provider = module.params.get("vm_provider")
+
+ aci = ACIModule(module)
+ vswitch_class = "vmmVSwitchPolicyCont"
+
+ child_classes = ["vmmRsVswitchOverrideLldpIfPol", "vmmRsVswitchOverrideLacpPol", "vmmRsVswitchOverrideCdpIfPol", "lacpEnhancedLagPol"]
+ if mtu_policy is not None:
+ child_classes.append("vmmRsVswitchOverrideMtuPol")
+
+ if stp_policy is not None:
+ child_classes.append("vmmRsVswitchOverrideStpPol")
+
+ if isinstance(netflow_exporter, dict):
+ child_classes.append("vmmRsVswitchExporterPol")
+
+ aci.construct_url(
+ root_class=dict(
+ aci_class="vmmProvP",
+ aci_rn="vmmp-{0}".format(VM_PROVIDER_MAPPING.get(vm_provider)),
+ module_object=vm_provider,
+ target_filter={"name": vm_provider},
+ ),
+ subclass_1=dict(
+ aci_class="vmmDomP",
+ aci_rn="dom-{0}".format(domain),
+ module_object=domain,
+ target_filter={"name": domain},
+ ),
+ subclass_2=dict(
+ aci_class="vmmVSwitchPolicyCont",
+ aci_rn="vswitchpolcont",
+ module_object="vswitchpolcont",
+ target_filter={"name": "vswitchpolcont"},
+ ),
+ child_classes=child_classes,
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ children = list()
+
+ if port_channel_policy is not None:
+ children.append(dict(vmmRsVswitchOverrideLacpPol=dict(attributes=dict(tDn="uni/infra/lacplagp-{0}".format(port_channel_policy)))))
+
+ if lldp_policy is not None:
+ children.append(dict(vmmRsVswitchOverrideLldpIfPol=dict(attributes=dict(tDn="uni/infra/lldpIfP-{0}".format(lldp_policy)))))
+
+ if cdp_policy is not None:
+ children.append(dict(vmmRsVswitchOverrideCdpIfPol=dict(attributes=dict(tDn="uni/infra/cdpIfP-{0}".format(cdp_policy)))))
+
+ if mtu_policy is not None:
+ children.append(dict(vmmRsVswitchOverrideMtuPol=dict(attributes=dict(tDn="uni/fabric/l2pol-{0}".format(mtu_policy)))))
+
+ if stp_policy is not None:
+ children.append(dict(vmmRsVswitchOverrideStpPol=dict(attributes=dict(tDn="uni/infra/ifPol-{0}".format(stp_policy)))))
+
+ if isinstance(netflow_exporter, dict):
+ children.append(
+ dict(
+ vmmRsVswitchExporterPol=dict(
+ attributes=dict(
+ tDn="uni/infra/vmmexporterpol-{0}".format(netflow_exporter["name"]),
+ activeFlowTimeOut=netflow_exporter["active_flow_timeout"],
+ idleFlowTimeOut=netflow_exporter["idle_flow_timeout"],
+ samplingRate=netflow_exporter["sampling_rate"],
+ )
+ )
+ )
+ )
+
+ if isinstance(enhanced_lag, list):
+ for lag_dict in enhanced_lag:
+ children.append(
+ dict(
+ lacpEnhancedLagPol=dict(
+ attributes=dict(
+ name=lag_dict["name"],
+ mode=lag_dict["lacp_mode"],
+ lbmode=lag_dict["load_balancing_mode"],
+ numLinks=lag_dict["number_uplinks"],
+ )
+ )
+ )
+ )
+
+ aci.payload(aci_class=vswitch_class, class_config=dict(rn="vswitchpolcont"), child_configs=children)
+
+ aci.get_diff(aci_class=vswitch_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vrf.py b/ansible_collections/cisco/aci/plugins/modules/aci_vrf.py
new file mode 100644
index 00000000..49c0c9fc
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vrf.py
@@ -0,0 +1,328 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vrf
+short_description: Manage contexts or VRFs (fv:Ctx)
+description:
+- Manage contexts or VRFs on Cisco ACI fabrics.
+- Each context is a private network associated to a tenant, i.e. VRF.
+- Enable Preferred Groups for VRF
+options:
+ tenant:
+ description:
+ - The name of the Tenant the VRF should belong to.
+ type: str
+ aliases: [ tenant_name ]
+ vrf:
+ description:
+ - The name of the VRF.
+ type: str
+ aliases: [ context, name, vrf_name ]
+ policy_control_direction:
+ description:
+ - Determines if the policy should be enforced by the fabric on ingress or egress.
+ type: str
+ choices: [ egress, ingress ]
+ policy_control_preference:
+ description:
+ - Determines if the fabric should enforce contract policies to allow routing and packet forwarding.
+ type: str
+ choices: [ enforced, unenforced ]
+ description:
+ description:
+ - The description for the VRF.
+ type: str
+ aliases: [ descr ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+ name_alias:
+ description:
+ - The alias for the current object. This relates to the nameAlias field in ACI.
+ type: str
+ preferred_group:
+ description:
+ - Enables preferred groups for the VRF under vzAny
+ type: str
+ choices: [enabled, disabled]
+ match_type:
+ description:
+ - Configures match type for contracts under vzAny
+ type: str
+ choices: [ all, at_least_one, at_most_one, none]
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+notes:
+- The C(tenant) used must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:Ctx).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Jacob McGill (@jmcgill298)
+"""
+
+EXAMPLES = r"""
+- name: Add a new VRF to a tenant
+ cisco.aci.aci_vrf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ vrf: vrf_lab
+ tenant: lab_tenant
+ descr: Lab VRF
+ policy_control_preference: enforced
+ policy_control_direction: ingress
+ state: present
+ delegate_to: localhost
+
+- name: Remove a VRF for a tenant
+ cisco.aci.aci_vrf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ vrf: vrf_lab
+ tenant: lab_tenant
+ state: absent
+ delegate_to: localhost
+
+- name: Query a VRF of a tenant
+ cisco.aci.aci_vrf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ vrf: vrf_lab
+ tenant: lab_tenant
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all VRFs
+ cisco.aci.aci_vrf:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+MATCH_TYPE_MAPPING = dict(
+ all="All",
+ at_least_one="AtleastOne",
+ at_most_one="AtmostOne",
+ none="None",
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects
+ vrf=dict(type="str", aliases=["context", "name", "vrf_name"]), # Not required for querying all objects
+ description=dict(type="str", aliases=["descr"]),
+ policy_control_direction=dict(type="str", choices=["egress", "ingress"]),
+ policy_control_preference=dict(type="str", choices=["enforced", "unenforced"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ preferred_group=dict(type="str", choices=["enabled", "disabled"]),
+ match_type=dict(type="str", choices=["all", "at_least_one", "at_most_one", "none"]),
+ name_alias=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "vrf"]],
+ ["state", "present", ["tenant", "vrf"]],
+ ],
+ )
+
+ description = module.params.get("description")
+ policy_control_direction = module.params.get("policy_control_direction")
+ policy_control_preference = module.params.get("policy_control_preference")
+ state = module.params.get("state")
+ tenant = module.params.get("tenant")
+ vrf = module.params.get("vrf")
+ name_alias = module.params.get("name_alias")
+ preferred_group = module.params.get("preferred_group")
+ match_type = module.params.get("match_type")
+
+ if match_type is not None:
+ match_type = MATCH_TYPE_MAPPING[match_type]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="fvTenant",
+ aci_rn="tn-{0}".format(tenant),
+ module_object=tenant,
+ target_filter={"name": tenant},
+ ),
+ subclass_1=dict(
+ aci_class="fvCtx",
+ aci_rn="ctx-{0}".format(vrf),
+ module_object=vrf,
+ target_filter={"name": vrf},
+ ),
+ child_classes=["vzAny"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="fvCtx",
+ class_config=dict(
+ descr=description,
+ pcEnfDir=policy_control_direction,
+ pcEnfPref=policy_control_preference,
+ name=vrf,
+ nameAlias=name_alias,
+ ),
+ child_configs=[
+ dict(vzAny=dict(attributes=dict(prefGrMemb=preferred_group, matchT=match_type))),
+ ],
+ )
+
+ aci.get_diff(aci_class="fvCtx")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/cisco/aci/plugins/modules/aci_vzany_to_contract.py b/ansible_collections/cisco/aci/plugins/modules/aci_vzany_to_contract.py
new file mode 100644
index 00000000..4795a2e0
--- /dev/null
+++ b/ansible_collections/cisco/aci/plugins/modules/aci_vzany_to_contract.py
@@ -0,0 +1,280 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see LICENSE 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": "certified"}
+
+DOCUMENTATION = r"""
+---
+module: aci_vzany_to_contract
+short_description: Attach contracts to vzAny (vz:RsAnyToProv, vz:RsAnyToCons, vz:RsAnyToConsIf)
+description:
+- Bind contracts to vzAny on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of the Tenant.
+ type: str
+ aliases: [ tenant_name ]
+ vrf:
+ description:
+ - The name of the VRF.
+ type: str
+ aliases: [ context, vrf_name ]
+ contract:
+ description:
+ - The name of the contract or contract interface.
+ type: str
+ aliases: [ contract_name ]
+ type:
+ description:
+ - Determines if this is a provided or consumed contract or a consumed contract interface.
+ type: str
+ aliases: [ contract_type ]
+ required: yes
+ choices: [ provider, consumer, interface ]
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_vrf
+- module: cisco.aci.aci_contract
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(fv:RsCons) and B(fv:RsProv).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Marcel Zehnder (@maercu)
+"""
+
+EXAMPLES = r"""
+- name: Add a new contract to vzAny
+ cisco.aci.aci_vzany_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: vzatest
+ vrf: vzatest
+ contract: vzatest_http
+ type: provider
+ state: present
+ delegate_to: localhost
+
+- name: Remove an existing contract from vzAny
+ cisco.aci.aci_vzany_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: vzatest
+ vrf: vzatest
+ contract: vzatest_http
+ type: provider
+ state: absent
+ delegate_to: localhost
+
+- name: Query a specific contract to vzAny binding
+ cisco.aci.aci_vzany_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: vzatest
+ vrf: vzatest
+ contract: vzatest_http
+ type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all provider contract to vzAny bindings
+ cisco.aci.aci_vzany_to_contract:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ type: provider
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: '<?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: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+ACI_CLASS_MAPPING = dict(
+ provider={"class": "vzRsAnyToProv", "rn": "rsanyToProv-", "target_attribute": "tnVzBrCPName"},
+ consumer={"class": "vzRsAnyToCons", "rn": "rsanyToCons-", "target_attribute": "tnVzBrCPName"},
+ interface={"class": "vzRsAnyToConsIf", "rn": "rsanyToConsIf-", "target_attribute": "tnVzCPIfName"},
+)
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ vrf=dict(type="str", aliases=["context", "vrf_name"]),
+ contract=dict(type="str", aliases=["contract_name"]),
+ type=dict(type="str", required=True, choices=["provider", "consumer", "interface"], aliases=["contract_type"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[["state", "absent", ["contract", "vrf", "tenant"]], ["state", "present", ["contract", "vrf", "tenant"]]],
+ )
+
+ tenant = module.params.get("tenant")
+ vrf = module.params.get("vrf")
+ contract = module.params.get("contract")
+ type = module.params.get("type")
+ state = module.params.get("state")
+
+ aci_class = ACI_CLASS_MAPPING[type]["class"]
+ aci_rn = ACI_CLASS_MAPPING[type]["rn"]
+ aci_target_attribute = ACI_CLASS_MAPPING[type]["target_attribute"]
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(aci_class="fvTenant", aci_rn="tn-{0}".format(tenant), module_object=tenant, target_filter={"name": tenant}),
+ subclass_1=dict(aci_class="fvCtx", aci_rn="ctx-{0}".format(vrf), module_object=vrf, target_filter={"name": vrf}),
+ subclass_2=dict(aci_class="vzAny", aci_rn="any", module_object="any", target_filter={"name": "any"}),
+ subclass_3=dict(aci_class=aci_class, aci_rn="{0}{1}".format(aci_rn, contract), module_object=contract, target_filter={aci_target_attribute: contract}),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(aci_class=aci_class, class_config={aci_target_attribute: contract})
+
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()