summaryrefslogtreecommitdiffstats
path: root/collections-debian-merged/ansible_collections/cisco/nxos/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
commita453ac31f3428614cceb99027f8efbdb9258a40b (patch)
treef61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/cisco/nxos/plugins
parentInitial commit. (diff)
downloadansible-upstream.tar.xz
ansible-upstream.zip
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/cisco/nxos/plugins')
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos.py264
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos_file_copy.py632
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/nxos.py391
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/nxos.py119
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/filter/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/nxos.py271
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/inventory/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py85
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/acls.py361
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py64
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/bgp_global.py516
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/facts.py26
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py63
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/interfaces.py69
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py76
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py85
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/lacp.py74
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py79
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py73
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/lldp_global.py91
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py70
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/ospf_interfaces.py130
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/ospfv2.py697
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/ospfv3.py551
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/static_routes.py94
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/telemetry.py118
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/vlans.py67
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/telemetry.py146
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py350
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/acls.py765
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py323
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/bgp_global.py412
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py302
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/interfaces.py495
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py355
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py568
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/lacp.py234
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py336
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py335
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/lldp_global.py285
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py325
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/ospf_interfaces.py206
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/ospfv2.py238
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/ospfv3.py253
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/static_routes.py616
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/telemetry.py665
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/vlans.py338
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py135
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/acls.py283
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py114
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/bgp_global.py135
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/facts.py145
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py100
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/interfaces.py119
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py109
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py144
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/lacp.py93
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py119
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py112
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/base.py822
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/lldp_global.py113
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py129
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/ospf_interfaces.py82
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/ospfv2.py97
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/ospfv3.py93
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/static_routes.py245
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/telemetry.py210
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/vlans.py192
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/nxos.py1501
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/bgp_global.py1455
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospf_interfaces.py505
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv2.py1105
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv3.py951
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/telemetry.py276
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/utils.py145
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server.py364
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server_host.py383
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl.py698
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interface.py227
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interfaces.py437
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acls.py819
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_banner.py229
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_global.py336
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_interfaces.py299
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp.py786
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_af.py935
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_global.py1657
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor.py588
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor_af.py815
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_command.py235
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_config.py598
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_devicealias.py593
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_global.py109
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_vni.py310
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_facts.py248
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_feature.py285
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_file_copy.py201
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir.py371
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir_profile_management.py218
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp.py537
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp_interfaces.py261
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp.py168
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_interface.py687
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_snooping.py321
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_install_os.py607
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface.py991
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface_ospf.py614
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interfaces.py403
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interface.py702
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interfaces.py404
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interface.py290
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interfaces.py404
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp.py275
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp_interfaces.py378
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lag_interfaces.py365
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_linkagg.py559
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp.py129
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_global.py342
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_interfaces.py260
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_logging.py979
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp.py459
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_auth.py347
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_options.py173
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_nxapi.py442
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf.py171
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_interfaces.py1445
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_vrf.py495
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv2.py1981
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv3.py1699
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_overlay_global.py204
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim.py220
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_interface.py621
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_rp_address.py256
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ping.py237
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_reboot.py94
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rollback.py134
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rpm.py421
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_smu.py163
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snapshot.py425
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_community.py253
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_contact.py151
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_host.py521
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_location.py156
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_traps.py331
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_user.py413
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_route.py383
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_routes.py476
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_system.py402
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_telemetry.py337
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld.py266
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld_interface.py315
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_user.py483
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlan.py849
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlans.py438
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc.py482
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc_interface.py353
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf.py643
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_af.py283
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_interface.py278
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrrp.py442
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vsan.py385
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_domain.py220
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_password.py287
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_version.py217
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep.py488
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep_vni.py482
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_zone_zoneset.py990
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_devicealias.py593
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_vsan.py385
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_zone_zoneset.py990
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/nxos.py142
248 files changed, 66980 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos.py
new file mode 100644
index 00000000..3f5f7176
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos.py
@@ -0,0 +1,264 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import copy
+import re
+import sys
+
+from ansible import constants as C
+from ansible_collections.ansible.netcommon.plugins.action.network import (
+ ActionModule as ActionNetworkModule,
+)
+from ansible.module_utils.connection import Connection
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ load_provider,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_provider_spec,
+)
+from ansible.utils.display import Display
+
+display = Display()
+
+
+class ActionModule(ActionNetworkModule):
+ def run(self, tmp=None, task_vars=None):
+ del tmp # tmp no longer has any effect
+
+ module_name = self._task.action.split(".")[-1]
+ self._config_module = (
+ True if module_name in ["nxos_config", "config"] else False
+ )
+ persistent_connection = self._play_context.connection.split(".")[-1]
+ warnings = []
+
+ if (
+ self._play_context.connection in ("httpapi", "local")
+ or self._task.args.get("provider", {}).get("transport") == "nxapi"
+ ) and module_name in ("nxos_file_copy", "nxos_nxapi"):
+ return {
+ "failed": True,
+ "msg": "Transport type 'nxapi' is not valid for '%s' module."
+ % (module_name),
+ }
+
+ if module_name == "nxos_file_copy":
+ self._task.args["host"] = self._play_context.remote_addr
+ self._task.args["password"] = self._play_context.password
+ if self._play_context.connection == "network_cli":
+ self._task.args["username"] = self._play_context.remote_user
+ elif self._play_context.connection == "local":
+ self._task.args[
+ "username"
+ ] = self._play_context.connection_user
+
+ if module_name == "nxos_install_os":
+ connection = self._connection
+ if connection.transport == "local":
+ persistent_command_timeout = C.PERSISTENT_COMMAND_TIMEOUT
+ persistent_connect_timeout = C.PERSISTENT_CONNECT_TIMEOUT
+ else:
+ persistent_command_timeout = connection.get_option(
+ "persistent_command_timeout"
+ )
+ persistent_connect_timeout = connection.get_option(
+ "persistent_connect_timeout"
+ )
+
+ display.vvvv(
+ "PERSISTENT_COMMAND_TIMEOUT is %s"
+ % str(persistent_command_timeout),
+ self._play_context.remote_addr,
+ )
+ display.vvvv(
+ "PERSISTENT_CONNECT_TIMEOUT is %s"
+ % str(persistent_connect_timeout),
+ self._play_context.remote_addr,
+ )
+ if (
+ persistent_command_timeout < 600
+ or persistent_connect_timeout < 600
+ ):
+ msg = (
+ "PERSISTENT_COMMAND_TIMEOUT and PERSISTENT_CONNECT_TIMEOUT"
+ )
+ msg += " must be set to 600 seconds or higher when using nxos_install_os module."
+ msg += " Current persistent_command_timeout setting:" + str(
+ persistent_command_timeout
+ )
+ msg += " Current persistent_connect_timeout setting:" + str(
+ persistent_connect_timeout
+ )
+ return {"failed": True, "msg": msg}
+
+ if persistent_connection in ("network_cli", "httpapi"):
+ provider = self._task.args.get("provider", {})
+ if any(provider.values()):
+ display.warning(
+ "provider is unnecessary when using %s and will be ignored"
+ % self._play_context.connection
+ )
+ del self._task.args["provider"]
+
+ if module_name == "nxos_gir":
+ conn = Connection(self._connection.socket_path)
+ persistent_command_timeout = conn.get_option(
+ "persistent_command_timeout"
+ )
+ gir_timeout = 200
+ if persistent_command_timeout < gir_timeout:
+ conn.set_option("persistent_command_timeout", gir_timeout)
+ msg = (
+ "timeout value extended to %ss for nxos_gir"
+ % gir_timeout
+ )
+ display.warning(msg)
+
+ elif self._play_context.connection == "local":
+ provider = load_provider(nxos_provider_spec, self._task.args)
+ transport = provider["transport"] or "cli"
+
+ display.vvvv(
+ "connection transport is %s" % transport,
+ self._play_context.remote_addr,
+ )
+
+ if transport == "cli":
+ pc = copy.deepcopy(self._play_context)
+ pc.connection = "ansible.netcommon.network_cli"
+ pc.network_os = "cisco.nxos.nxos"
+ pc.remote_addr = (
+ provider["host"] or self._play_context.remote_addr
+ )
+ pc.port = int(
+ provider["port"] or self._play_context.port or 22
+ )
+ pc.remote_user = (
+ provider["username"] or self._play_context.connection_user
+ )
+ pc.password = (
+ provider["password"] or self._play_context.password
+ )
+ pc.private_key_file = (
+ provider["ssh_keyfile"]
+ or self._play_context.private_key_file
+ )
+ pc.become = provider["authorize"] or False
+ if pc.become:
+ pc.become_method = "enable"
+ pc.become_pass = provider["auth_pass"]
+
+ connection = self._shared_loader_obj.connection_loader.get(
+ "ansible.netcommon.persistent",
+ pc,
+ sys.stdin,
+ task_uuid=self._task._uuid,
+ )
+
+ # TODO: Remove below code after ansible minimal is cut out
+ if connection is None:
+ pc.connection = "network_cli"
+ pc.network_os = "nxos"
+ connection = self._shared_loader_obj.connection_loader.get(
+ "persistent", pc, sys.stdin, task_uuid=self._task._uuid
+ )
+
+ display.vvv(
+ "using connection plugin %s (was local)" % pc.connection,
+ pc.remote_addr,
+ )
+
+ command_timeout = (
+ int(provider["timeout"])
+ if provider["timeout"]
+ else connection.get_option("persistent_command_timeout")
+ )
+ connection.set_options(
+ direct={"persistent_command_timeout": command_timeout}
+ )
+
+ socket_path = connection.run()
+ display.vvvv("socket_path: %s" % socket_path, pc.remote_addr)
+ if not socket_path:
+ return {
+ "failed": True,
+ "msg": "unable to open shell. Please see: "
+ + "https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell",
+ }
+
+ task_vars["ansible_socket"] = socket_path
+
+ else:
+ self._task.args[
+ "provider"
+ ] = ActionModule.nxapi_implementation(
+ provider, self._play_context
+ )
+ warnings.append(
+ [
+ "connection local support for this module is deprecated and will be removed in version 2.14,"
+ " use connection either httpapi or ansible.netcommon.httpapi (whichever is applicable)"
+ ]
+ )
+ else:
+ return {
+ "failed": True,
+ "msg": "Connection type %s is not valid for this module"
+ % self._play_context.connection,
+ }
+
+ result = super(ActionModule, self).run(task_vars=task_vars)
+ if warnings:
+ if "warnings" in result:
+ result["warnings"].extend(warnings)
+ else:
+ result["warnings"] = warnings
+ return result
+
+ @staticmethod
+ def nxapi_implementation(provider, play_context):
+ provider["transport"] = "nxapi"
+ if provider.get("host") is None:
+ provider["host"] = play_context.remote_addr
+
+ if provider.get("port") is None:
+ if provider.get("use_ssl"):
+ provider["port"] = 443
+ else:
+ provider["port"] = 80
+
+ if provider.get("timeout") is None:
+ provider["timeout"] = C.PERSISTENT_COMMAND_TIMEOUT
+
+ if provider.get("username") is None:
+ provider["username"] = play_context.connection_user
+
+ if provider.get("password") is None:
+ provider["password"] = play_context.password
+
+ if provider.get("use_ssl") is None:
+ provider["use_ssl"] = False
+
+ if provider.get("validate_certs") is None:
+ provider["validate_certs"] = True
+
+ return provider
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos_file_copy.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos_file_copy.py
new file mode 100644
index 00000000..1296f0ce
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/action/nxos_file_copy.py
@@ -0,0 +1,632 @@
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import copy
+import hashlib
+import os
+import re
+import time
+
+from ansible.errors import AnsibleError
+from ansible.module_utils._text import to_text, to_bytes, to_native
+from ansible.module_utils.common import validation
+from ansible.module_utils.connection import Connection
+from ansible.plugins.action import ActionBase
+from ansible.utils.display import Display
+from ansible.module_utils.compat.paramiko import paramiko
+from ansible.module_utils import six
+
+try:
+ from scp import SCPClient
+
+ HAS_SCP = True
+except ImportError:
+ HAS_SCP = False
+
+try:
+ import pexpect
+
+ HAS_PEXPECT = True
+except ImportError:
+ HAS_PEXPECT = False
+
+display = Display()
+
+
+class ActionModule(ActionBase):
+ def process_playbook_values(self):
+ """ Get playbook values and perform input validation """
+ argument_spec = dict(
+ vrf=dict(type="str", default="management"),
+ connect_ssh_port=dict(type="int", default=22),
+ file_system=dict(type="str", default="bootflash:"),
+ file_pull=dict(type="bool", default=False),
+ file_pull_timeout=dict(type="int", default=300),
+ file_pull_protocol=dict(
+ type="str",
+ default="scp",
+ choices=["scp", "sftp", "http", "https", "tftp", "ftp"],
+ ),
+ file_pull_compact=dict(type="bool", default=False),
+ file_pull_kstack=dict(type="bool", default=False),
+ local_file=dict(type="path"),
+ local_file_directory=dict(type="path"),
+ remote_file=dict(type="path"),
+ remote_scp_server=dict(type="str"),
+ remote_scp_server_user=dict(type="str"),
+ remote_scp_server_password=dict(no_log=True),
+ )
+
+ playvals = {}
+ # Process key value pairs from playbook task
+ for key in argument_spec.keys():
+ playvals[key] = self._task.args.get(
+ key, argument_spec[key].get("default")
+ )
+ if playvals[key] is None:
+ continue
+
+ option_type = argument_spec[key].get("type", "str")
+ try:
+ if option_type == "str":
+ playvals[key] = validation.check_type_str(playvals[key])
+ elif option_type == "int":
+ playvals[key] = validation.check_type_int(playvals[key])
+ elif option_type == "bool":
+ playvals[key] = validation.check_type_bool(playvals[key])
+ elif option_type == "path":
+ playvals[key] = validation.check_type_path(playvals[key])
+ else:
+ raise AnsibleError(
+ "Unrecognized type <{0}> for playbook parameter <{1}>".format(
+ option_type, key
+ )
+ )
+
+ except (TypeError, ValueError) as e:
+ raise AnsibleError(
+ "argument %s is of type %s and we were unable to convert to %s: %s"
+ % (key, type(playvals[key]), option_type, to_native(e))
+ )
+
+ if "choices" in argument_spec[key] and playvals[
+ key
+ ] not in argument_spec[key].get("choices"):
+ raise AnsibleError(
+ "argument {0} with value {1} is not valid. Allowed values are {2}".format(
+ key,
+ playvals[key],
+ ", ".join(argument_spec[key].get("choices")),
+ )
+ )
+
+ # Validate playbook dependencies
+ if playvals["file_pull"]:
+ if playvals.get("remote_file") is None:
+ raise AnsibleError(
+ "Playbook parameter <remote_file> required when <file_pull> is True"
+ )
+ if playvals.get("remote_scp_server") is None:
+ raise AnsibleError(
+ "Playbook parameter <remote_scp_server> required when <file_pull> is True"
+ )
+
+ if playvals["remote_scp_server"] or playvals["remote_scp_server_user"]:
+
+ if None in (
+ playvals["remote_scp_server"],
+ playvals["remote_scp_server_user"],
+ ):
+ params = "<remote_scp_server>, <remote_scp_server_user>"
+ raise AnsibleError(
+ "Playbook parameters {0} must be set together".format(
+ params
+ )
+ )
+
+ return playvals
+
+ def check_library_dependencies(self, file_pull):
+ if file_pull:
+ if not HAS_PEXPECT:
+ msg = "library pexpect is required when file_pull is True but does not appear to be "
+ msg += "installed. It can be installed using `pip install pexpect`"
+ raise AnsibleError(msg)
+ else:
+ if paramiko is None:
+ msg = "library paramiko is required when file_pull is False but does not appear to be "
+ msg += "installed. It can be installed using `pip install paramiko`"
+ raise AnsibleError(msg)
+
+ if not HAS_SCP:
+ msg = "library scp is required when file_pull is False but does not appear to be "
+ msg += "installed. It can be installed using `pip install scp`"
+ raise AnsibleError(msg)
+
+ def md5sum_check(self, dst, file_system):
+ command = "show file {0}{1} md5sum".format(file_system, dst)
+ remote_filehash = self.conn.exec_command(command)
+ remote_filehash = to_bytes(
+ remote_filehash, errors="surrogate_or_strict"
+ )
+
+ local_file = self.playvals["local_file"]
+ try:
+ with open(local_file, "rb") as f:
+ filecontent = f.read()
+ except (OSError, IOError) as exc:
+ raise AnsibleError(
+ "Error reading the file: {0}".format(to_text(exc))
+ )
+
+ filecontent = to_bytes(filecontent, errors="surrogate_or_strict")
+ local_filehash = hashlib.md5(filecontent).hexdigest()
+
+ decoded_rhash = remote_filehash.decode("UTF-8")
+
+ if local_filehash == decoded_rhash:
+ return True
+ else:
+ return False
+
+ def remote_file_exists(self, remote_file, file_system):
+ command = "dir {0}/{1}".format(file_system, remote_file)
+ body = self.conn.exec_command(command)
+
+ if "No such file" in body:
+ return False
+ else:
+ return self.md5sum_check(remote_file, file_system)
+
+ def verify_remote_file_exists(self, dst, file_system):
+ command = "dir {0}/{1}".format(file_system, dst)
+ body = self.conn.exec_command(command)
+ if "No such file" in body:
+ return 0
+ return body.split()[0].strip()
+
+ def local_file_exists(self, file):
+ return os.path.isfile(file)
+
+ def get_flash_size(self, file_system):
+ command = "dir {0}".format(file_system)
+ body = self.conn.exec_command(command)
+
+ match = re.search(r"(\d+) bytes free", body)
+ if match:
+ bytes_free = match.group(1)
+ return int(bytes_free)
+
+ match = re.search(r"No such file or directory", body)
+ if match:
+ raise AnsibleError(
+ "Invalid nxos filesystem {0}".format(file_system)
+ )
+ else:
+ raise AnsibleError(
+ "Unable to determine size of filesystem {0}".format(
+ file_system
+ )
+ )
+
+ def enough_space(self, file, file_system):
+ flash_size = self.get_flash_size(file_system)
+ file_size = os.path.getsize(file)
+ if file_size > flash_size:
+ return False
+
+ return True
+
+ def transfer_file_to_device(self, remote_file):
+ timeout = self.socket_timeout
+ local_file = self.playvals["local_file"]
+ file_system = self.playvals["file_system"]
+
+ if not self.enough_space(local_file, file_system):
+ raise AnsibleError(
+ "Could not transfer file. Not enough space on device."
+ )
+
+ # frp = full_remote_path, flp = full_local_path
+ frp = "{0}{1}".format(file_system, remote_file)
+ flp = os.path.join(os.path.abspath(local_file))
+ try:
+ self.conn.copy_file(
+ source=flp, destination=frp, proto="scp", timeout=timeout
+ )
+ except Exception as exc:
+ self.results["failed"] = True
+ self.results["msg"] = "Exception received : %s" % exc
+
+ def file_push(self):
+ local_file = self.playvals["local_file"]
+ remote_file = self.playvals["remote_file"] or os.path.basename(
+ local_file
+ )
+ file_system = self.playvals["file_system"]
+
+ if not self.local_file_exists(local_file):
+ raise AnsibleError("Local file {0} not found".format(local_file))
+
+ remote_file = remote_file or os.path.basename(local_file)
+ remote_exists = self.remote_file_exists(remote_file, file_system)
+
+ if not remote_exists:
+ self.results["changed"] = True
+ file_exists = False
+ else:
+ self.results[
+ "transfer_status"
+ ] = "No Transfer: File already copied to remote device."
+ file_exists = True
+
+ if not self.play_context.check_mode and not file_exists:
+ self.transfer_file_to_device(remote_file)
+ self.results[
+ "transfer_status"
+ ] = "Sent: File copied to remote device."
+
+ self.results["local_file"] = local_file
+ if remote_file is None:
+ remote_file = os.path.basename(local_file)
+ self.results["remote_file"] = remote_file
+
+ def copy_file_from_remote(self, local, local_file_directory, file_system):
+ self.results["failed"] = False
+ nxos_hostname = self.play_context.remote_addr
+ nxos_username = self.play_context.remote_user
+ nxos_password = self.play_context.password or ""
+ port = self.playvals["connect_ssh_port"]
+
+ # Build copy command components that will be used to initiate copy from the nxos device.
+ cmdroot = "copy " + self.playvals["file_pull_protocol"] + "://"
+ ruser = self.playvals["remote_scp_server_user"] + "@"
+ rserver = self.playvals["remote_scp_server"]
+ rfile = self.playvals["remote_file"] + " "
+ vrf = " vrf " + self.playvals["vrf"]
+ local_dir_root = "/"
+ if self.playvals["file_pull_compact"]:
+ compact = " compact "
+ else:
+ compact = ""
+ if self.playvals["file_pull_kstack"]:
+ kstack = " use-kstack "
+ else:
+ kstack = ""
+
+ def process_outcomes(session, timeout=None):
+ if timeout is None:
+ timeout = 10
+ outcome = {}
+ outcome["user_response_required"] = False
+ outcome["password_prompt_detected"] = False
+ outcome["existing_file_with_same_name"] = False
+ outcome["final_prompt_detected"] = False
+ outcome["copy_complete"] = False
+ outcome["expect_timeout"] = False
+ outcome["error"] = False
+ outcome["error_data"] = None
+
+ # Possible outcomes key:
+ # 0) - Are you sure you want to continue connecting (yes/no)
+ # 1) - Password: or @servers's password:
+ # 2) - Warning: There is already a file existing with this name. Do you want to overwrite (y/n)?[n]
+ # 3) - Timeout conditions
+ # 4) - No space on nxos device file_system
+ # 5) - Username/Password or file permission issues
+ # 6) - File does not exist on remote scp server
+ # 7) - invalid nxos command
+ # 8) - compact option not supported
+ # 9) - compaction attempt failed
+ # 10) - other failures like attempting to compact non image file
+ # 11) - failure to resolve hostname
+ # 12) - Too many authentication failures
+ # 13) - Copy to / from this server not permitted
+ # 14) - Copy completed without issues
+ # 15) - nxos_router_prompt#
+ # 16) - pexpect timeout
+ possible_outcomes = [
+ r"sure you want to continue connecting \(yes/no\)\? ",
+ "(?i)Password: ",
+ "file existing with this name",
+ "timed out",
+ "(?i)No space.*#",
+ "(?i)Permission denied.*#",
+ "(?i)No such file.*#",
+ ".*Invalid command.*#",
+ "Compaction is not supported on this platform.*#",
+ "Compact of.*failed.*#",
+ "(?i)Failed.*#",
+ "(?i)Could not resolve hostname",
+ "(?i)Too many authentication failures",
+ r"(?i)Copying to\/from this server name is not permitted",
+ "(?i)Copy complete",
+ r"#\s",
+ pexpect.TIMEOUT,
+ ]
+ index = session.expect(possible_outcomes, timeout=timeout)
+ # Each index maps to items in possible_outcomes
+ if index == 0:
+ outcome["user_response_required"] = True
+ return outcome
+ elif index == 1:
+ outcome["password_prompt_detected"] = True
+ return outcome
+ elif index == 2:
+ outcome["existing_file_with_same_name"] = True
+ return outcome
+ elif index in [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]:
+ decoded_before = session.before.decode("UTF-8")
+ decoded_after = session.after.decode("UTF-8")
+ before = decoded_before.strip().replace(" \x08", "")
+ after = decoded_after.strip().replace(" \x08", "")
+ outcome["error"] = True
+ outcome["error_data"] = "COMMAND {0} ERROR {1}".format(
+ before, after
+ )
+ return outcome
+ elif index == 14:
+ outcome["copy_complete"] = True
+ return outcome
+ elif index == 15:
+ outcome["final_prompt_detected"] = True
+ return outcome
+ elif index == 16:
+ # The before property will contain all text up to the expected string pattern.
+ # The after string will contain the text that was matched by the expected pattern.
+ outcome["expect_timeout"] = True
+ outcome[
+ "error_data"
+ ] = "Expect Timeout error occurred: BEFORE {0} AFTER {1}".format(
+ session.before, session.after
+ )
+ return outcome
+ else:
+ outcome["error"] = True
+ outcome[
+ "error_data"
+ ] = "Unrecognized error occurred: BEFORE {0} AFTER {1}".format(
+ session.before, session.after
+ )
+ return outcome
+
+ return outcome
+
+ # Spawn pexpect connection to NX-OS device.
+ nxos_session = pexpect.spawn(
+ "ssh " + nxos_username + "@" + nxos_hostname + " -p" + str(port)
+ )
+ # There might be multiple user_response_required prompts or intermittent timeouts
+ # spawning the expect session so loop up to 24 times during the spawn process.
+ max_attempts = 24
+ for connect_attempt in range(max_attempts):
+ outcome = process_outcomes(nxos_session)
+ if outcome["user_response_required"]:
+ nxos_session.sendline("yes")
+ continue
+ if outcome["password_prompt_detected"]:
+ time.sleep(3)
+ nxos_session.sendline(nxos_password)
+ continue
+ if outcome["final_prompt_detected"]:
+ break
+ if outcome["error"] or outcome["expect_timeout"]:
+ # Error encountered, try to spawn expect session n more times up to max_attempts - 1
+ if connect_attempt < max_attempts:
+ outcome["error"] = False
+ outcome["expect_timeout"] = False
+ nxos_session.close()
+ nxos_session = pexpect.spawn(
+ "ssh "
+ + nxos_username
+ + "@"
+ + nxos_hostname
+ + " -p"
+ + str(port)
+ )
+ continue
+ self.results["failed"] = True
+ outcome["error_data"] = re.sub(
+ nxos_password, "", outcome["error_data"]
+ )
+ self.results["error_data"] = (
+ "Failed to spawn expect session! " + outcome["error_data"]
+ )
+ nxos_session.close()
+ return
+ else:
+ # The before property will contain all text up to the expected string pattern.
+ # The after string will contain the text that was matched by the expected pattern.
+ msg = "After {0} attempts, failed to spawn pexpect session to {1}"
+ msg += "BEFORE: {2}, AFTER: {3}"
+ error_msg = msg.format(
+ connect_attempt,
+ nxos_hostname,
+ nxos_session.before,
+ nxos_session.after,
+ )
+ re.sub(nxos_password, "", error_msg)
+ nxos_session.close()
+ raise AnsibleError(error_msg)
+
+ # Create local file directory under NX-OS filesystem if
+ # local_file_directory playbook parameter is set.
+ if local_file_directory:
+ dir_array = local_file_directory.split("/")
+ for each in dir_array:
+ if each:
+ mkdir_cmd = "mkdir " + local_dir_root + each
+ nxos_session.sendline(mkdir_cmd)
+ outcome = process_outcomes(nxos_session)
+ if outcome["error"] or outcome["expect_timeout"]:
+ self.results["mkdir_cmd"] = mkdir_cmd
+ self.results["failed"] = True
+ outcome["error_data"] = re.sub(
+ nxos_password, "", outcome["error_data"]
+ )
+ self.results["error_data"] = outcome["error_data"]
+ return
+ local_dir_root += each + "/"
+
+ # Initiate file copy
+ copy_cmd = (
+ cmdroot
+ + ruser
+ + rserver
+ + rfile
+ + file_system
+ + local_dir_root
+ + local
+ + compact
+ + vrf
+ + kstack
+ )
+ self.results["copy_cmd"] = copy_cmd
+ nxos_session.sendline(copy_cmd)
+ for copy_attempt in range(6):
+ outcome = process_outcomes(
+ nxos_session, self.playvals["file_pull_timeout"]
+ )
+ if outcome["user_response_required"]:
+ nxos_session.sendline("yes")
+ continue
+ if outcome["password_prompt_detected"]:
+ if self.playvals.get("remote_scp_server_password"):
+ nxos_session.sendline(
+ self.playvals["remote_scp_server_password"]
+ )
+ else:
+ err_msg = "Remote scp server {0} requires a password.".format(
+ rserver
+ )
+ err_msg += " Set the <remote_scp_server_password> playbook parameter or configure nxos device for passwordless scp"
+ raise AnsibleError(err_msg)
+ continue
+ if outcome["existing_file_with_same_name"]:
+ nxos_session.sendline("y")
+ continue
+ if outcome["copy_complete"]:
+ self.results[
+ "transfer_status"
+ ] = "Received: File copied/pulled to nxos device from remote scp server."
+ break
+ if outcome["error"] or outcome["expect_timeout"]:
+ self.results["failed"] = True
+ outcome["error_data"] = re.sub(
+ nxos_password, "", outcome["error_data"]
+ )
+ if self.playvals.get("remote_scp_server_password"):
+ outcome["error_data"] = re.sub(
+ self.playvals["remote_scp_server_password"],
+ "",
+ outcome["error_data"],
+ )
+ self.results["error_data"] = outcome["error_data"]
+ nxos_session.close()
+ return
+ else:
+ # The before property will contain all text up to the expected string pattern.
+ # The after string will contain the text that was matched by the expected pattern.
+ msg = "After {0} attempts, failed to copy file to {1}"
+ msg += "BEFORE: {2}, AFTER: {3}, CMD: {4}"
+ error_msg = msg.format(
+ copy_attempt,
+ nxos_hostname,
+ nxos_session.before,
+ nxos_session.before,
+ copy_cmd,
+ )
+ re.sub(nxos_password, "", error_msg)
+ if self.playvals.get("remote_scp_server_password"):
+ re.sub(
+ self.playvals["remote_scp_server_password"], "", error_msg
+ )
+ nxos_session.close()
+ raise AnsibleError(error_msg)
+
+ nxos_session.close()
+
+ def file_pull(self):
+ local_file = self.playvals["local_file"]
+ remote_file = self.playvals["remote_file"]
+ file_system = self.playvals["file_system"]
+ # Note: This is the local file directory on the remote nxos device.
+ local_file_dir = self.playvals["local_file_directory"]
+
+ local_file = local_file or self.playvals["remote_file"].split("/")[-1]
+
+ if not self.play_context.check_mode:
+ self.copy_file_from_remote(local_file, local_file_dir, file_system)
+
+ if not self.results["failed"]:
+ self.results["changed"] = True
+ self.results["remote_file"] = remote_file
+ if local_file_dir:
+ dir = local_file_dir
+ else:
+ dir = ""
+ self.results["local_file"] = file_system + dir + "/" + local_file
+ self.results["remote_scp_server"] = self.playvals[
+ "remote_scp_server"
+ ]
+
+ # This is the main run method for the action plugin to copy files
+ def run(self, tmp=None, task_vars=None):
+ socket_path = None
+ self.play_context = copy.deepcopy(self._play_context)
+ self.results = super(ActionModule, self).run(task_vars=task_vars)
+
+ if self.play_context.connection.split(".")[-1] != "network_cli":
+ # Plugin is supported only with network_cli
+ self.results["failed"] = True
+ self.results["msg"] = (
+ "Connection type must be fully qualified name for network_cli connection type, got %s"
+ % self.play_context.connection
+ )
+ return self.results
+
+ # Get playbook values
+ self.playvals = self.process_playbook_values()
+
+ file_pull = self.playvals["file_pull"]
+ self.check_library_dependencies(file_pull)
+
+ if socket_path is None:
+ socket_path = self._connection.socket_path
+ self.conn = Connection(socket_path)
+
+ # Call get_capabilities() to start the connection to the device.
+ self.conn.get_capabilities()
+
+ self.socket_timeout = self.conn.get_option(
+ "persistent_command_timeout"
+ )
+
+ # This action plugin support two modes of operation.
+ # - file_pull is False - Push files from the ansible controller to nxos switch.
+ # - file_pull is True - Initiate copy from the device to pull files to the nxos switch.
+ self.results["transfer_status"] = "No Transfer"
+ self.results["file_system"] = self.playvals["file_system"]
+ if file_pull:
+ self.file_pull()
+ else:
+ self.file_push()
+
+ return self.results
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/nxos.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/nxos.py
new file mode 100644
index 00000000..6b4fb6b4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/cliconf/nxos.py
@@ -0,0 +1,391 @@
+#
+# (c) 2017 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+author: Ansible Networking Team
+cliconf: nxos
+short_description: Use NX-OS cliconf to run commands on Cisco NX-OS platform
+description:
+- This nxos plugin provides low level abstraction apis for sending and receiving CLI
+ commands from Cisco NX-OS network devices.
+version_added: 1.0.0
+"""
+
+import json
+import re
+
+from ansible.errors import AnsibleConnectionFailure
+from ansible.module_utils._text import to_bytes, to_text
+from ansible.module_utils.common._collections_compat import Mapping
+from ansible.module_utils.connection import ConnectionError
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ NetworkConfig,
+ dumps,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+)
+from ansible.plugins.cliconf import CliconfBase, enable_mode
+
+
+class Cliconf(CliconfBase):
+ def __init__(self, *args, **kwargs):
+ self._module_context = {}
+ self._device_info = {}
+ super(Cliconf, self).__init__(*args, **kwargs)
+
+ def read_module_context(self, module_key):
+ if self._module_context.get(module_key):
+ return self._module_context[module_key]
+
+ return None
+
+ def save_module_context(self, module_key, module_context):
+ self._module_context[module_key] = module_context
+
+ return None
+
+ def get_device_info(self):
+ if not self._device_info:
+ device_info = {}
+
+ device_info["network_os"] = "nxos"
+ reply = self.get("show version")
+ platform_reply = self.get("show inventory")
+
+ match_sys_ver = re.search(
+ r"\s+system:\s+version\s*(\S+)", reply, re.M
+ )
+ if match_sys_ver:
+ device_info["network_os_version"] = match_sys_ver.group(1)
+ else:
+ match_kick_ver = re.search(
+ r"\s+kickstart:\s+version\s*(\S+)", reply, re.M
+ )
+ if match_kick_ver:
+ device_info["network_os_version"] = match_kick_ver.group(1)
+
+ if "network_os_version" not in device_info:
+ match_sys_ver = re.search(
+ r"\s+NXOS:\s+version\s*(\S+)", reply, re.M
+ )
+ if match_sys_ver:
+ device_info["network_os_version"] = match_sys_ver.group(1)
+
+ match_chassis_id = re.search(
+ r"Hardware\n\s+cisco(.+)$", reply, re.M
+ )
+ if match_chassis_id:
+ device_info["network_os_model"] = match_chassis_id.group(
+ 1
+ ).strip()
+
+ match_host_name = re.search(
+ r"\s+Device name:\s*(\S+)", reply, re.M
+ )
+ if match_host_name:
+ device_info["network_os_hostname"] = match_host_name.group(1)
+
+ match_isan_file_name = re.search(
+ r"\s+system image file is:\s*(\S+)", reply, re.M
+ )
+ if match_isan_file_name:
+ device_info["network_os_image"] = match_isan_file_name.group(1)
+ else:
+ match_kick_file_name = re.search(
+ r"\s+kickstart image file is:\s*(\S+)", reply, re.M
+ )
+ if match_kick_file_name:
+ device_info[
+ "network_os_image"
+ ] = match_kick_file_name.group(1)
+
+ if "network_os_image" not in device_info:
+ match_isan_file_name = re.search(
+ r"\s+NXOS image file is:\s*(\S+)", reply, re.M
+ )
+ if match_isan_file_name:
+ device_info[
+ "network_os_image"
+ ] = match_isan_file_name.group(1)
+
+ match_os_platform = re.search(
+ r'NAME: "Chassis",\s*DESCR:.*\n' r"PID:\s*(\S+)",
+ platform_reply,
+ re.M,
+ )
+ if match_os_platform:
+ device_info["network_os_platform"] = match_os_platform.group(1)
+
+ self._device_info = device_info
+
+ return self._device_info
+
+ def get_diff(
+ self,
+ candidate=None,
+ running=None,
+ diff_match="line",
+ diff_ignore_lines=None,
+ path=None,
+ diff_replace="line",
+ ):
+ diff = {}
+ device_operations = self.get_device_operations()
+ option_values = self.get_option_values()
+
+ if candidate is None and device_operations["supports_generate_diff"]:
+ raise ValueError(
+ "candidate configuration is required to generate diff"
+ )
+
+ if diff_match not in option_values["diff_match"]:
+ raise ValueError(
+ "'match' value %s in invalid, valid values are %s"
+ % (diff_match, ", ".join(option_values["diff_match"]))
+ )
+
+ if diff_replace not in option_values["diff_replace"]:
+ raise ValueError(
+ "'replace' value %s in invalid, valid values are %s"
+ % (diff_replace, ", ".join(option_values["diff_replace"]))
+ )
+
+ # prepare candidate configuration
+ candidate_obj = NetworkConfig(indent=2)
+ candidate_obj.load(candidate)
+
+ if running and diff_match != "none" and diff_replace != "config":
+ # running configuration
+ running_obj = NetworkConfig(
+ indent=2, contents=running, ignore_lines=diff_ignore_lines
+ )
+ configdiffobjs = candidate_obj.difference(
+ running_obj, path=path, match=diff_match, replace=diff_replace
+ )
+
+ else:
+ configdiffobjs = candidate_obj.items
+
+ diff["config_diff"] = (
+ dumps(configdiffobjs, "commands") if configdiffobjs else ""
+ )
+ return diff
+
+ def get_config(self, source="running", format="text", flags=None):
+ options_values = self.get_option_values()
+ if format not in options_values["format"]:
+ raise ValueError(
+ "'format' value %s is invalid. Valid values are %s"
+ % (format, ",".join(options_values["format"]))
+ )
+
+ lookup = {"running": "running-config", "startup": "startup-config"}
+ if source not in lookup:
+ raise ValueError(
+ "fetching configuration from %s is not supported" % source
+ )
+
+ cmd = "show {0} ".format(lookup[source])
+ if format and format != "text":
+ cmd += "| %s " % format
+
+ if flags:
+ cmd += " ".join(to_list(flags))
+ cmd = cmd.strip()
+
+ return self.send_command(cmd)
+
+ def edit_config(
+ self, candidate=None, commit=True, replace=None, comment=None
+ ):
+ resp = {}
+ operations = self.get_device_operations()
+ self.check_edit_config_capability(
+ operations, candidate, commit, replace, comment
+ )
+ results = []
+ requests = []
+
+ if replace:
+ device_info = self.get_device_info()
+ # not all NX-OS versions support `config replace`
+ # we let the device throw the invalid command error
+ candidate = "config replace {0}".format(replace)
+
+ if commit:
+ self.send_command("configure terminal")
+
+ for line in to_list(candidate):
+ if not isinstance(line, Mapping):
+ line = {"command": line}
+
+ cmd = line["command"]
+ if cmd != "end":
+ results.append(self.send_command(**line))
+ requests.append(cmd)
+
+ self.send_command("end")
+ else:
+ raise ValueError("check mode is not supported")
+
+ resp["request"] = requests
+ resp["response"] = results
+ return resp
+
+ def get(
+ self,
+ command,
+ prompt=None,
+ answer=None,
+ sendonly=False,
+ output=None,
+ newline=True,
+ check_all=False,
+ ):
+ if output:
+ command = self._get_command_with_output(command, output)
+ return self.send_command(
+ command=command,
+ prompt=prompt,
+ answer=answer,
+ sendonly=sendonly,
+ newline=newline,
+ check_all=check_all,
+ )
+
+ def run_commands(self, commands=None, check_rc=True):
+ if commands is None:
+ raise ValueError("'commands' value is required")
+
+ responses = list()
+ for cmd in to_list(commands):
+ if not isinstance(cmd, Mapping):
+ cmd = {"command": cmd}
+
+ output = cmd.pop("output", None)
+ if output:
+ cmd["command"] = self._get_command_with_output(
+ cmd["command"], output
+ )
+
+ try:
+ out = self.send_command(**cmd)
+ except AnsibleConnectionFailure as e:
+ if check_rc is True:
+ raise
+ out = getattr(e, "err", e)
+
+ if out is not None:
+ try:
+ out = to_text(out, errors="surrogate_or_strict").strip()
+ except UnicodeError:
+ raise ConnectionError(
+ message=u"Failed to decode output from %s: %s"
+ % (cmd, to_text(out))
+ )
+
+ try:
+ out = json.loads(out)
+ except ValueError:
+ pass
+
+ responses.append(out)
+ return responses
+
+ def get_device_operations(self):
+ return {
+ "supports_diff_replace": True,
+ "supports_commit": False,
+ "supports_rollback": False,
+ "supports_defaults": True,
+ "supports_onbox_diff": False,
+ "supports_commit_comment": False,
+ "supports_multiline_delimiter": False,
+ "supports_diff_match": True,
+ "supports_diff_ignore_lines": True,
+ "supports_generate_diff": True,
+ "supports_replace": True,
+ }
+
+ def get_option_values(self):
+ return {
+ "format": ["text", "json"],
+ "diff_match": ["line", "strict", "exact", "none"],
+ "diff_replace": ["line", "block", "config"],
+ "output": ["text", "json"],
+ }
+
+ def get_capabilities(self):
+ result = super(Cliconf, self).get_capabilities()
+ result["rpc"] += ["get_diff", "run_commands"]
+ result["device_operations"] = self.get_device_operations()
+ result.update(self.get_option_values())
+
+ return json.dumps(result)
+
+ def set_cli_prompt_context(self):
+ """
+ Make sure we are in the operational cli context
+ :return: None
+ """
+ if self._connection.connected:
+ out = self._connection.get_prompt()
+ if out is None:
+ raise AnsibleConnectionFailure(
+ message=u"cli prompt is not identified from the last received"
+ u" response window: %s"
+ % self._connection._last_recv_window
+ )
+ # Match prompts ending in )# except those with (maint-mode)#
+ config_prompt = re.compile(r"^.*\((?!maint-mode).*\)#$")
+
+ while config_prompt.match(
+ to_text(out, errors="surrogate_then_replace").strip()
+ ):
+ self._connection.queue_message(
+ "vvvv", "wrong context, sending exit to device"
+ )
+ self._connection.send_command("exit")
+ out = self._connection.get_prompt()
+
+ def _get_command_with_output(self, command, output):
+ options_values = self.get_option_values()
+ if output not in options_values["output"]:
+ raise ValueError(
+ "'output' value %s is invalid. Valid values are %s"
+ % (output, ",".join(options_values["output"]))
+ )
+
+ if output == "json" and not command.endswith("| json"):
+ device_info = self.get_device_info()
+ model = device_info.get("network_os_model", "")
+ platform = device_info.get("network_os_platform", "")
+ if platform.startswith("DS-") and "MDS" in model:
+ cmd = "%s | json native" % command
+ else:
+ cmd = "%s | json" % command
+ elif output == "text" and command.endswith("| json"):
+ cmd = command.rsplit("|", 1)[0]
+ else:
+ cmd = command
+ return cmd
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/nxos.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/nxos.py
new file mode 100644
index 00000000..e76dd7ba
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/doc_fragments/nxos.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+class ModuleDocFragment(object):
+
+ # Standard files documentation fragment
+ DOCUMENTATION = r"""options:
+ provider:
+ description:
+ - B(Deprecated)
+ - 'Starting with Ansible 2.5 we recommend using C(connection: network_cli).'
+ - 'Starting with Ansible 2.6 we recommend using C(connection: httpapi) for NX-API.'
+ - This option will be removed in a release after 2022-06-01.
+ - For more information please see the L(NXOS Platform Options guide, ../network/user_guide/platform_nxos.html).
+ - HORIZONTALLINE
+ - A dict object containing connection details.
+ type: dict
+ suboptions:
+ host:
+ description:
+ - Specifies the DNS host name or address for connecting to the remote device
+ over the specified transport. The value of host is used as the destination
+ address for the transport.
+ type: str
+ port:
+ description:
+ - Specifies the port to use when building the connection to the remote device. This
+ value applies to either I(cli) or I(nxapi). The port value will default
+ to the appropriate transport common port if none is provided in the task. (cli=22,
+ http=80, https=443).
+ type: int
+ username:
+ description:
+ - Configures the username to use to authenticate the connection to the remote
+ device. This value is used to authenticate either the CLI login or the
+ nxapi authentication depending on which transport is used. If the value
+ is not specified in the task, the value of environment variable C(ANSIBLE_NET_USERNAME)
+ will be used instead.
+ type: str
+ password:
+ description:
+ - Specifies the password to use to authenticate the connection to the remote
+ device. This is a common argument used for either I(cli) or I(nxapi) transports.
+ If the value is not specified in the task, the value of environment variable
+ C(ANSIBLE_NET_PASSWORD) will be used instead.
+ type: str
+ authorize:
+ description:
+ - Instructs the module to enter privileged mode on the remote device before
+ sending any commands. If not specified, the device will attempt to execute
+ all commands in non-privileged mode. If the value is not specified in the
+ task, the value of environment variable C(ANSIBLE_NET_AUTHORIZE) will be
+ used instead.
+ type: bool
+ default: false
+ auth_pass:
+ description:
+ - Specifies the password to use if required to enter privileged mode on the
+ remote device. If I(authorize) is false, then this argument does nothing.
+ If the value is not specified in the task, the value of environment variable
+ C(ANSIBLE_NET_AUTH_PASS) will be used instead.
+ type: str
+ timeout:
+ description:
+ - Specifies the timeout in seconds for communicating with the network device
+ for either connecting or sending commands. If the timeout is exceeded before
+ the operation is completed, the module will error. NX-API can be slow to
+ return on long-running commands (sh mac, sh bgp, etc).
+ type: int
+ ssh_keyfile:
+ description:
+ - Specifies the SSH key to use to authenticate the connection to the remote
+ device. This argument is only used for the I(cli) transport. If the value
+ is not specified in the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE)
+ will be used instead.
+ type: str
+ transport:
+ description:
+ - Configures the transport connection to use when connecting to the remote
+ device. The transport argument supports connectivity to the device over
+ cli (ssh) or nxapi.
+ type: str
+ choices:
+ - cli
+ - nxapi
+ default: cli
+ use_ssl:
+ description:
+ - Configures the I(transport) to use SSL if set to C(yes) only when the C(transport=nxapi),
+ otherwise this value is ignored.
+ type: bool
+ default: false
+ validate_certs:
+ description:
+ - If C(no), SSL certificates will not be validated. This should only be used
+ on personally controlled sites using self-signed certificates. If the transport
+ argument is not nxapi, this value is ignored.
+ type: bool
+ default: False
+ use_proxy:
+ description:
+ - If C(no), the environment variables C(http_proxy) and C(https_proxy) will
+ be ignored.
+ type: bool
+ default: true
+notes:
+- For information on using CLI and NX-API see the :ref:`NXOS Platform Options guide
+ <nxos_platform_options>`
+- For more information on using Ansible to manage network devices see the :ref:`Ansible
+ Network Guide <network_guide>`
+- For more information on using Ansible to manage Cisco devices see the `Cisco integration
+ page <https://www.ansible.com/integrations/networks/cisco>`_.
+"""
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/filter/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/filter/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/filter/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/nxos.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/nxos.py
new file mode 100644
index 00000000..d6d911ba
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/httpapi/nxos.py
@@ -0,0 +1,271 @@
+# (c) 2018 Red Hat Inc.
+# 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 = """
+author: Ansible Networking Team
+httpapi: nxos
+short_description: Use NX-API to run commands on Cisco NX-OS platform
+description:
+- This plugin provides low level abstraction APIs for sending and receiving
+ commands using NX-API with devices running Cisco NX-OS.
+version_added: 1.0.0
+"""
+
+import json
+import re
+import collections
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.connection import ConnectionError
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+)
+from ansible.plugins.httpapi import HttpApiBase
+
+
+OPTIONS = {
+ "format": ["text", "json"],
+ "diff_match": ["line", "strict", "exact", "none"],
+ "diff_replace": ["line", "block", "config"],
+ "output": ["text", "json"],
+}
+
+
+class HttpApi(HttpApiBase):
+ def __init__(self, *args, **kwargs):
+ super(HttpApi, self).__init__(*args, **kwargs)
+ self._device_info = None
+ self._module_context = {}
+
+ def read_module_context(self, module_key):
+ if self._module_context.get(module_key):
+ return self._module_context[module_key]
+
+ return None
+
+ def save_module_context(self, module_key, module_context):
+ self._module_context[module_key] = module_context
+
+ return None
+
+ def send_request(self, data, **message_kwargs):
+ output = None
+ queue = list()
+ responses = list()
+
+ for item in to_list(data):
+ cmd_output = message_kwargs.get("output") or "text"
+ if isinstance(item, dict):
+ command = item["command"]
+ if "output" in item:
+ cmd_output = item["output"]
+ else:
+ command = item
+
+ # Emulate '| json' from CLI
+ if command.endswith("| json"):
+ command = command.rsplit("|", 1)[0]
+ cmd_output = "json"
+
+ if output and output != cmd_output:
+ responses.extend(self._run_queue(queue, output))
+ queue = list()
+
+ output = cmd_output
+ queue.append(command)
+
+ if queue:
+ responses.extend(self._run_queue(queue, output))
+
+ if len(responses) == 1:
+ return responses[0]
+ return responses
+
+ def _run_queue(self, queue, output):
+ if self._become:
+ self.connection.queue_message(
+ "warning",
+ "become has no effect over httpapi. Use network_cli if privilege escalation is required",
+ )
+
+ request = request_builder(queue, output)
+ headers = {"Content-Type": "application/json"}
+
+ response, response_data = self.connection.send(
+ "/ins", request, headers=headers, method="POST"
+ )
+
+ try:
+ response_data = json.loads(to_text(response_data.getvalue()))
+ except ValueError:
+ raise ConnectionError(
+ "Response was not valid JSON, got {0}".format(
+ to_text(response_data.getvalue())
+ )
+ )
+
+ results = handle_response(response_data)
+ return results
+
+ def get_device_info(self):
+ if self._device_info:
+ return self._device_info
+
+ device_info = {}
+
+ device_info["network_os"] = "nxos"
+ reply, platform_reply = self.send_request(
+ ["show version", "show inventory"]
+ )
+
+ find_os_version = [
+ r"\s+system:\s+version\s*(\S+)",
+ r"\s+kickstart:\s+version\s*(\S+)",
+ r"\s+NXOS:\s+version\s*(\S+)",
+ ]
+ for regex in find_os_version:
+ match_ver = re.search(regex, reply, re.M)
+ if match_ver:
+ device_info["network_os_version"] = match_ver.group(1)
+ break
+
+ match_chassis_id = re.search(
+ r"Hardware\n\s+cisco\s*(\S+\s+\S+)", reply, re.M
+ )
+ if match_chassis_id:
+ device_info["network_os_model"] = match_chassis_id.group(1)
+
+ match_host_name = re.search(r"\s+Device name:\s*(\S+)", reply, re.M)
+ if match_host_name:
+ device_info["network_os_hostname"] = match_host_name.group(1)
+
+ find_os_image = [
+ r"\s+system image file is:\s*(\S+)",
+ r"\s+kickstart image file is:\s*(\S+)",
+ r"\s+NXOS image file is:\s*(\S+)",
+ ]
+ for regex in find_os_image:
+ match_file_name = re.search(regex, reply, re.M)
+ if match_file_name:
+ device_info["network_os_image"] = match_file_name.group(1)
+ break
+
+ match_os_platform = re.search(
+ r'NAME: (?:"Chassis"| Chassis ),\s*DESCR:.*\nPID:\s*(\S+)',
+ platform_reply,
+ re.M,
+ )
+ if match_os_platform:
+ device_info["network_os_platform"] = match_os_platform.group(1)
+
+ self._device_info = device_info
+ return self._device_info
+
+ def get_device_operations(self):
+ platform = self.get_device_info().get("network_os_platform", "")
+ return {
+ "supports_diff_replace": True,
+ "supports_commit": False,
+ "supports_rollback": False,
+ "supports_defaults": True,
+ "supports_onbox_diff": False,
+ "supports_commit_comment": False,
+ "supports_multiline_delimiter": False,
+ "supports_diff_match": True,
+ "supports_diff_ignore_lines": True,
+ "supports_generate_diff": True,
+ "supports_replace": True if "9K" in platform else False,
+ }
+
+ def get_capabilities(self):
+ result = {}
+ result["rpc"] = []
+ result["device_info"] = self.get_device_info()
+ result["device_operations"] = self.get_device_operations()
+ result.update(OPTIONS)
+ result["network_api"] = "nxapi"
+
+ return json.dumps(result)
+
+ # Shims for resource module support
+ def get(self, command, output=None):
+ # This method is ONLY here to support resource modules. Therefore most
+ # arguments are unsupported and not present.
+
+ return self.send_request(data=command, output=output)
+
+ def edit_config(self, candidate):
+ # This method is ONLY here to support resource modules. Therefore most
+ # arguments are unsupported and not present.
+
+ responses = self.send_request(candidate, output="config")
+ return [resp for resp in to_list(responses) if resp != "{}"]
+
+
+def handle_response(response):
+ results = []
+
+ if response["ins_api"].get("outputs"):
+ for output in to_list(response["ins_api"]["outputs"]["output"]):
+ if output["code"] != "200":
+ # Best effort messages: some API output keys may not exist on some platforms
+ input_data = output.get("input", "")
+ msg = output.get("msg", "")
+ clierror = output.get("clierror", "")
+ raise ConnectionError(
+ "%s: %s: %s" % (input_data, msg, clierror),
+ code=output["code"],
+ )
+ elif "body" in output:
+ result = output["body"]
+ if isinstance(result, dict):
+ result = json.dumps(result)
+
+ results.append(result.strip())
+
+ return results
+
+
+def request_builder(commands, output, version="1.0", chunk="0", sid=None):
+ """Encodes a NXAPI JSON request message
+ """
+ output_to_command_type = {
+ "text": "cli_show_ascii",
+ "json": "cli_show",
+ "bash": "bash",
+ "config": "cli_conf",
+ }
+
+ maybe_output = commands[0].split("|")[-1].strip()
+ if maybe_output in output_to_command_type:
+ command_type = output_to_command_type[maybe_output]
+ commands = [command.split("|")[0].strip() for command in commands]
+ else:
+ try:
+ command_type = output_to_command_type[output]
+ except KeyError:
+ msg = "invalid format, received %s, expected one of %s" % (
+ output,
+ ",".join(output_to_command_type.keys()),
+ )
+ raise ConnectionError(msg)
+
+ if isinstance(commands, (list, set, tuple)):
+ commands = " ;".join(commands)
+
+ # Order should not matter but some versions of NX-OS software fail
+ # to process the payload properly if 'input' gets serialized before
+ # 'type' and the payload of 'input' contains the word 'type'.
+ msg = collections.OrderedDict()
+ msg["version"] = version
+ msg["type"] = command_type
+ msg["chunk"] = chunk
+ msg["sid"] = sid
+ msg["input"] = commands
+ msg["output_format"] = "json"
+
+ return json.dumps(dict(ins_api=msg))
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/inventory/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/inventory/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/inventory/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py
new file mode 100644
index 00000000..1d384043
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acl_interfaces/acl_interfaces.py
@@ -0,0 +1,85 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_acl_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class Acl_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_acl_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "access_groups": {
+ "elements": "dict",
+ "options": {
+ "acls": {
+ "elements": "dict",
+ "options": {
+ "direction": {
+ "choices": ["in", "out"],
+ "required": True,
+ "type": "str",
+ },
+ "name": {"required": True, "type": "str"},
+ "port": {"type": "bool"},
+ },
+ "type": "list",
+ },
+ "afi": {
+ "choices": ["ipv4", "ipv6"],
+ "required": True,
+ "type": "str",
+ },
+ },
+ "type": "list",
+ },
+ "name": {"required": True, "type": "str"},
+ },
+ "type": "list",
+ },
+ "running_config": {"type": "str"},
+ "state": {
+ "choices": [
+ "deleted",
+ "gathered",
+ "merged",
+ "overridden",
+ "rendered",
+ "replaced",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/acls.py
new file mode 100644
index 00000000..78afdf0e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/acls/acls.py
@@ -0,0 +1,361 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_acls module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class AclsArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_acls module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "acls": {
+ "elements": "dict",
+ "options": {
+ "aces": {
+ "elements": "dict",
+ "mutually_exclusive": [["grant", "remark"]],
+ "options": {
+ "destination": {
+ "mutually_exclusive": [
+ ["address", "any", "host", "prefix"],
+ [
+ "wildcard_bits",
+ "any",
+ "host",
+ "prefix",
+ ],
+ ],
+ "options": {
+ "address": {"type": "str"},
+ "any": {"type": "bool"},
+ "host": {"type": "str"},
+ "port_protocol": {
+ "mutually_exclusive": [
+ [
+ "eq",
+ "lt",
+ "neq",
+ "gt",
+ "range",
+ ]
+ ],
+ "options": {
+ "eq": {"type": "str"},
+ "gt": {"type": "str"},
+ "lt": {"type": "str"},
+ "neq": {"type": "str"},
+ "range": {
+ "options": {
+ "end": {"type": "str"},
+ "start": {
+ "type": "str"
+ },
+ },
+ "required_together": [
+ ["start", "end"]
+ ],
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "prefix": {"type": "str"},
+ "wildcard_bits": {"type": "str"},
+ },
+ "required_together": [
+ ["address", "wildcard_bits"]
+ ],
+ "type": "dict",
+ },
+ "dscp": {"type": "str"},
+ "fragments": {"type": "bool"},
+ "grant": {
+ "choices": ["permit", "deny"],
+ "type": "str",
+ },
+ "log": {"type": "bool"},
+ "precedence": {"type": "str"},
+ "protocol": {"type": "str"},
+ "protocol_options": {
+ "mutually_exclusive": [
+ ["icmp", "igmp", "tcp"]
+ ],
+ "options": {
+ "icmp": {
+ "options": {
+ "administratively_prohibited": {
+ "type": "bool"
+ },
+ "alternate_address": {
+ "type": "bool"
+ },
+ "conversion_error": {
+ "type": "bool"
+ },
+ "dod_host_prohibited": {
+ "type": "bool"
+ },
+ "dod_net_prohibited": {
+ "type": "bool"
+ },
+ "echo": {"type": "bool"},
+ "echo_reply": {"type": "bool"},
+ "echo_request": {
+ "type": "bool"
+ },
+ "general_parameter_problem": {
+ "type": "bool"
+ },
+ "host_isolated": {
+ "type": "bool"
+ },
+ "host_precedence_unreachable": {
+ "type": "bool"
+ },
+ "host_redirect": {
+ "type": "bool"
+ },
+ "host_tos_redirect": {
+ "type": "bool"
+ },
+ "host_tos_unreachable": {
+ "type": "bool"
+ },
+ "host_unknown": {
+ "type": "bool"
+ },
+ "host_unreachable": {
+ "type": "bool"
+ },
+ "information_reply": {
+ "type": "bool"
+ },
+ "information_request": {
+ "type": "bool"
+ },
+ "mask_reply": {"type": "bool"},
+ "mask_request": {
+ "type": "bool"
+ },
+ "message_code": {
+ "type": "int"
+ },
+ "message_type": {
+ "type": "int"
+ },
+ "mobile_redirect": {
+ "type": "bool"
+ },
+ "net_redirect": {
+ "type": "bool"
+ },
+ "net_tos_redirect": {
+ "type": "bool"
+ },
+ "net_tos_unreachable": {
+ "type": "bool"
+ },
+ "net_unreachable": {
+ "type": "bool"
+ },
+ "network_unknown": {
+ "type": "bool"
+ },
+ "no_room_for_option": {
+ "type": "bool"
+ },
+ "option_missing": {
+ "type": "bool"
+ },
+ "packet_too_big": {
+ "type": "bool"
+ },
+ "parameter_problem": {
+ "type": "bool"
+ },
+ "port_unreachable": {
+ "type": "bool"
+ },
+ "precedence_unreachable": {
+ "type": "bool"
+ },
+ "protocol_unreachable": {
+ "type": "bool"
+ },
+ "reassembly_timeout": {
+ "type": "bool"
+ },
+ "redirect": {"type": "bool"},
+ "router_advertisement": {
+ "type": "bool"
+ },
+ "router_solicitation": {
+ "type": "bool"
+ },
+ "source_quench": {
+ "type": "bool"
+ },
+ "source_route_failed": {
+ "type": "bool"
+ },
+ "time_exceeded": {
+ "type": "bool"
+ },
+ "timestamp_reply": {
+ "type": "bool"
+ },
+ "timestamp_request": {
+ "type": "bool"
+ },
+ "traceroute": {"type": "bool"},
+ "ttl_exceeded": {
+ "type": "bool"
+ },
+ "unreachable": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ },
+ "igmp": {
+ "mutually_exclusive": [
+ [
+ "dvmrp",
+ "host_query",
+ "host_report",
+ ]
+ ],
+ "options": {
+ "dvmrp": {"type": "bool"},
+ "host_query": {"type": "bool"},
+ "host_report": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ },
+ "tcp": {
+ "options": {
+ "ack": {"type": "bool"},
+ "established": {
+ "type": "bool"
+ },
+ "fin": {"type": "bool"},
+ "psh": {"type": "bool"},
+ "rst": {"type": "bool"},
+ "syn": {"type": "bool"},
+ "urg": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "remark": {"type": "str"},
+ "sequence": {"type": "int"},
+ "source": {
+ "mutually_exclusive": [
+ ["address", "any", "host", "prefix"],
+ [
+ "wildcard_bits",
+ "host",
+ "any",
+ "prefix",
+ ],
+ ],
+ "options": {
+ "address": {"type": "str"},
+ "any": {"type": "bool"},
+ "host": {"type": "str"},
+ "port_protocol": {
+ "mutually_exclusive": [
+ ["eq", "lt", "neq", "range"],
+ ["eq", "gt", "neq", "range"],
+ ],
+ "options": {
+ "eq": {"type": "str"},
+ "gt": {"type": "str"},
+ "lt": {"type": "str"},
+ "neq": {"type": "str"},
+ "range": {
+ "options": {
+ "end": {"type": "str"},
+ "start": {
+ "type": "str"
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "prefix": {"type": "str"},
+ "wildcard_bits": {"type": "str"},
+ },
+ "required_together": [
+ ["address", "wildcard_bits"]
+ ],
+ "type": "dict",
+ },
+ },
+ "type": "list",
+ },
+ "name": {"required": True, "type": "str"},
+ },
+ "type": "list",
+ },
+ "afi": {
+ "choices": ["ipv4", "ipv6"],
+ "required": True,
+ "type": "str",
+ },
+ },
+ "type": "list",
+ },
+ "running_config": {"type": "str"},
+ "state": {
+ "choices": [
+ "deleted",
+ "gathered",
+ "merged",
+ "overridden",
+ "rendered",
+ "replaced",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py
new file mode 100644
index 00000000..8319b8b5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bfd_interfaces/bfd_interfaces.py
@@ -0,0 +1,64 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+"""
+The arg spec for the nxos_bfd_interfaces module
+"""
+
+
+class Bfd_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_bfd_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str"},
+ "bfd": {"choices": ["enable", "disable"], "type": "str"},
+ "echo": {"choices": ["enable", "disable"], "type": "str"},
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/bgp_global.py
new file mode 100644
index 00000000..2142aa26
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/bgp_global/bgp_global.py
@@ -0,0 +1,516 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the
+# cli_rm_builder.
+#
+# Manually editing this file is not advised.
+#
+# To update the argspec make the desired changes
+# in the module docstring and re-run
+# cli_rm_builder.
+#
+#############################################
+
+"""
+The arg spec for the nxos_bgp_global module
+"""
+
+
+class Bgp_globalArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_bgp_global module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "type": "dict",
+ "options": {
+ "as_number": {"type": "str"},
+ "affinity_group": {
+ "type": "dict",
+ "options": {"group_id": {"type": "int"}},
+ },
+ "bestpath": {
+ "type": "dict",
+ "options": {
+ "always_compare_med": {"type": "bool"},
+ "as_path": {
+ "type": "dict",
+ "options": {
+ "ignore": {"type": "bool"},
+ "multipath_relax": {"type": "bool"},
+ },
+ },
+ "compare_neighborid": {"type": "bool"},
+ "compare_routerid": {"type": "bool"},
+ "cost_community_ignore": {"type": "bool"},
+ "igp_metric_ignore": {"type": "bool"},
+ "med": {
+ "type": "dict",
+ "options": {
+ "confed": {"type": "bool"},
+ "missing_as_worst": {"type": "bool"},
+ "non_deterministic": {"type": "bool"},
+ },
+ },
+ },
+ },
+ "cluster_id": {"type": "str"},
+ "confederation": {
+ "type": "dict",
+ "options": {
+ "identifier": {"type": "str"},
+ "peers": {"type": "list", "elements": "str"},
+ },
+ },
+ "disable_policy_batching": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "ipv4": {
+ "type": "dict",
+ "options": {"prefix_list": {"type": "str"}},
+ },
+ "ipv6": {
+ "type": "dict",
+ "options": {"prefix_list": {"type": "str"}},
+ },
+ "nexthop": {"type": "bool"},
+ },
+ },
+ "dynamic_med_interval": {"type": "int"},
+ "enforce_first_as": {"type": "bool"},
+ "enhanced_error": {"type": "bool"},
+ "fabric_soo": {"type": "str"},
+ "fast_external_fallover": {"type": "bool"},
+ "flush_routes": {"type": "bool"},
+ "graceful_restart": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "restart_time": {"type": "int"},
+ "stalepath_time": {"type": "int"},
+ "helper": {"type": "bool"},
+ },
+ },
+ "graceful_shutdown": {
+ "type": "dict",
+ "options": {
+ "activate": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "route_map": {"type": "str"},
+ },
+ },
+ "aware": {"type": "bool"},
+ },
+ },
+ "isolate": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "include_local": {"type": "bool"},
+ },
+ },
+ "log_neighbor_changes": {"type": "bool"},
+ "maxas_limit": {"type": "int"},
+ "neighbors": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "neighbor_address": {"type": "str", "required": True},
+ "neighbor_affinity_group": {
+ "type": "dict",
+ "options": {"group_id": {"type": "int"}},
+ },
+ "bmp_activate_server": {"type": "int"},
+ "capability": {
+ "type": "dict",
+ "options": {
+ "suppress_4_byte_as": {"type": "bool"}
+ },
+ },
+ "description": {"type": "str"},
+ "disable_connected_check": {"type": "bool"},
+ "dont_capability_negotiate": {"type": "bool"},
+ "dscp": {"type": "str"},
+ "dynamic_capability": {"type": "bool"},
+ "ebgp_multihop": {"type": "int"},
+ "graceful_shutdown": {
+ "type": "dict",
+ "options": {
+ "activate": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "route_map": {"type": "str"},
+ },
+ }
+ },
+ },
+ "inherit": {
+ "type": "dict",
+ "options": {
+ "peer": {"type": "str"},
+ "peer_session": {"type": "str"},
+ },
+ },
+ "local_as": {"type": "str"},
+ "log_neighbor_changes": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "disable": {"type": "bool"},
+ },
+ },
+ "low_memory": {
+ "type": "dict",
+ "options": {"exempt": {"type": "bool"}},
+ },
+ "password": {
+ "type": "dict",
+ "options": {
+ "encryption": {"type": "int"},
+ "key": {"type": "str"},
+ },
+ },
+ "path_attribute": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "action": {
+ "type": "str",
+ "choices": [
+ "discard",
+ "treat-as-withdraw",
+ ],
+ },
+ "type": {"type": "int"},
+ "range": {
+ "type": "dict",
+ "options": {
+ "start": {"type": "int"},
+ "end": {"type": "int"},
+ },
+ },
+ },
+ },
+ "peer_type": {
+ "type": "str",
+ "choices": [
+ "fabric-border-leaf",
+ "fabric-external",
+ ],
+ },
+ "remote_as": {"type": "str"},
+ "remove_private_as": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "replace_as": {"type": "bool"},
+ "all": {"type": "bool"},
+ },
+ },
+ "shutdown": {"type": "bool"},
+ "timers": {
+ "type": "dict",
+ "options": {
+ "keepalive": {"type": "int"},
+ "holdtime": {"type": "int"},
+ },
+ },
+ "transport": {
+ "type": "dict",
+ "options": {
+ "connection_mode": {
+ "type": "dict",
+ "options": {"passive": {"type": "bool"}},
+ }
+ },
+ },
+ "ttl_security": {
+ "type": "dict",
+ "options": {"hops": {"type": "int"}},
+ },
+ "update_source": {"type": "str"},
+ },
+ },
+ "neighbor_down": {
+ "type": "dict",
+ "options": {"fib_accelerate": {"type": "bool"}},
+ },
+ "nexthop": {
+ "type": "dict",
+ "options": {
+ "suppress_default_resolution": {"type": "bool"}
+ },
+ },
+ "rd": {
+ "type": "dict",
+ "options": {
+ "dual": {"type": "bool"},
+ "id": {"type": "int"},
+ },
+ },
+ "reconnect_interval": {"type": "int"},
+ "router_id": {"type": "str"},
+ "shutdown": {"type": "bool"},
+ "suppress_fib_pending": {"type": "bool"},
+ "timers": {
+ "type": "dict",
+ "options": {
+ "bestpath_limit": {
+ "type": "dict",
+ "options": {
+ "timeout": {"type": "int"},
+ "always": {"type": "bool"},
+ },
+ },
+ "bgp": {
+ "type": "dict",
+ "options": {
+ "keepalive": {"type": "int"},
+ "holdtime": {"type": "int"},
+ },
+ },
+ "prefix_peer_timeout": {"type": "int"},
+ "prefix_peer_wait": {"type": "int"},
+ },
+ },
+ "vrfs": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "vrf": {"type": "str"},
+ "allocate_index": {"type": "int"},
+ "bestpath": {
+ "type": "dict",
+ "options": {
+ "always_compare_med": {"type": "bool"},
+ "as_path": {
+ "type": "dict",
+ "options": {
+ "ignore": {"type": "bool"},
+ "multipath_relax": {"type": "bool"},
+ },
+ },
+ "compare_neighborid": {"type": "bool"},
+ "compare_routerid": {"type": "bool"},
+ "cost_community_ignore": {"type": "bool"},
+ "igp_metric_ignore": {"type": "bool"},
+ "med": {
+ "type": "dict",
+ "options": {
+ "confed": {"type": "bool"},
+ "missing_as_worst": {"type": "bool"},
+ "non_deterministic": {"type": "bool"},
+ },
+ },
+ },
+ },
+ "cluster_id": {"type": "str"},
+ "confederation": {
+ "type": "dict",
+ "options": {
+ "identifier": {"type": "str"},
+ "peers": {"type": "list", "elements": "str"},
+ },
+ },
+ "graceful_restart": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "restart_time": {"type": "int"},
+ "stalepath_time": {"type": "int"},
+ "helper": {"type": "bool"},
+ },
+ },
+ "local_as": {"type": "str"},
+ "log_neighbor_changes": {"type": "bool"},
+ "maxas_limit": {"type": "int"},
+ "neighbors": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "neighbor_address": {
+ "type": "str",
+ "required": True,
+ },
+ "neighbor_affinity_group": {
+ "type": "dict",
+ "options": {"group_id": {"type": "int"}},
+ },
+ "bmp_activate_server": {"type": "int"},
+ "capability": {
+ "type": "dict",
+ "options": {
+ "suppress_4_byte_as": {"type": "bool"}
+ },
+ },
+ "description": {"type": "str"},
+ "disable_connected_check": {"type": "bool"},
+ "dont_capability_negotiate": {"type": "bool"},
+ "dscp": {"type": "str"},
+ "dynamic_capability": {"type": "bool"},
+ "ebgp_multihop": {"type": "int"},
+ "graceful_shutdown": {
+ "type": "dict",
+ "options": {
+ "activate": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "route_map": {"type": "str"},
+ },
+ }
+ },
+ },
+ "inherit": {
+ "type": "dict",
+ "options": {
+ "peer": {"type": "str"},
+ "peer_session": {"type": "str"},
+ },
+ },
+ "local_as": {"type": "str"},
+ "log_neighbor_changes": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "disable": {"type": "bool"},
+ },
+ },
+ "low_memory": {
+ "type": "dict",
+ "options": {"exempt": {"type": "bool"}},
+ },
+ "password": {
+ "type": "dict",
+ "options": {
+ "encryption": {"type": "int"},
+ "key": {"type": "str"},
+ },
+ },
+ "path_attribute": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "action": {
+ "type": "str",
+ "choices": [
+ "discard",
+ "treat-as-withdraw",
+ ],
+ },
+ "type": {"type": "int"},
+ "range": {
+ "type": "dict",
+ "options": {
+ "start": {"type": "int"},
+ "end": {"type": "int"},
+ },
+ },
+ },
+ },
+ "peer_type": {
+ "type": "str",
+ "choices": [
+ "fabric-border-leaf",
+ "fabric-external",
+ ],
+ },
+ "remote_as": {"type": "str"},
+ "remove_private_as": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "replace_as": {"type": "bool"},
+ "all": {"type": "bool"},
+ },
+ },
+ "shutdown": {"type": "bool"},
+ "timers": {
+ "type": "dict",
+ "options": {
+ "keepalive": {"type": "int"},
+ "holdtime": {"type": "int"},
+ },
+ },
+ "transport": {
+ "type": "dict",
+ "options": {
+ "connection_mode": {
+ "type": "dict",
+ "options": {
+ "passive": {"type": "bool"}
+ },
+ }
+ },
+ },
+ "ttl_security": {
+ "type": "dict",
+ "options": {"hops": {"type": "int"}},
+ },
+ "update_source": {"type": "str"},
+ },
+ },
+ "neighbor_down": {
+ "type": "dict",
+ "options": {"fib_accelerate": {"type": "bool"}},
+ },
+ "reconnect_interval": {"type": "int"},
+ "router_id": {"type": "str"},
+ "timers": {
+ "type": "dict",
+ "options": {
+ "bestpath_limit": {
+ "type": "dict",
+ "options": {
+ "timeout": {"type": "int"},
+ "always": {"type": "bool"},
+ },
+ },
+ "bgp": {
+ "type": "dict",
+ "options": {
+ "keepalive": {"type": "int"},
+ "holdtime": {"type": "int"},
+ },
+ },
+ "prefix_peer_timeout": {"type": "int"},
+ "prefix_peer_wait": {"type": "int"},
+ },
+ },
+ },
+ },
+ },
+ },
+ "state": {
+ "type": "str",
+ "choices": [
+ "merged",
+ "replaced",
+ "deleted",
+ "purged",
+ "parsed",
+ "gathered",
+ "rendered",
+ ],
+ "default": "merged",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/facts.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/facts.py
new file mode 100644
index 00000000..cada4b86
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/facts/facts.py
@@ -0,0 +1,26 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# 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
+"""
+The arg spec for the nxos facts module.
+"""
+
+
+class FactsArgs(object):
+ """ The arg spec for the nxos facts module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "gather_subset": dict(
+ default=["!config"], type="list", elements="str"
+ ),
+ "gather_network_resources": dict(type="list", elements="str"),
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py
new file mode 100644
index 00000000..6145dabb
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/hsrp_interfaces/hsrp_interfaces.py
@@ -0,0 +1,63 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+"""
+The arg spec for the nxos_hsrp_interfaces module
+"""
+
+
+class Hsrp_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_hsrp_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str"},
+ "bfd": {"choices": ["enable", "disable"], "type": "str"},
+ },
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "gathered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/interfaces.py
new file mode 100644
index 00000000..fd2ae36c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/interfaces/interfaces.py
@@ -0,0 +1,69 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class InterfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "description": {"type": "str"},
+ "duplex": {"choices": ["full", "half", "auto"], "type": "str"},
+ "enabled": {"type": "bool"},
+ "fabric_forwarding_anycast_gateway": {"type": "bool"},
+ "ip_forward": {"type": "bool"},
+ "mode": {"choices": ["layer2", "layer3"], "type": "str"},
+ "mtu": {"type": "str"},
+ "name": {"required": True, "type": "str"},
+ "speed": {"type": "str"},
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py
new file mode 100644
index 00000000..0559555c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,76 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_l2_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class L2_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_l2_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "access": {
+ "options": {"vlan": {"type": "int"}},
+ "type": "dict",
+ },
+ "mode": {
+ "type": "str",
+ "choices": ["access", "trunk", "fex-fabric", "fabricpath"],
+ },
+ "name": {"required": True, "type": "str"},
+ "trunk": {
+ "options": {
+ "allowed_vlans": {"type": "str"},
+ "native_vlan": {"type": "int"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "parsed",
+ "gathered",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py
new file mode 100644
index 00000000..299c5133
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/l3_interfaces/l3_interfaces.py
@@ -0,0 +1,85 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_l3_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class L3_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_l3_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "dot1q": {"type": "int"},
+ "ipv4": {
+ "elements": "dict",
+ "options": {
+ "address": {"type": "str"},
+ "secondary": {"type": "bool"},
+ "tag": {"type": "int"},
+ },
+ "type": "list",
+ },
+ "ipv6": {
+ "elements": "dict",
+ "options": {
+ "address": {"type": "str"},
+ "tag": {"type": "int"},
+ },
+ "type": "list",
+ },
+ "name": {"required": True, "type": "str"},
+ "redirects": {"type": "bool"},
+ "unreachables": {"type": "bool"},
+ "evpn_multisite_tracking": {
+ "type": "str",
+ "choices": ["fabric-tracking", "dci-tracking"],
+ },
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/lacp.py
new file mode 100644
index 00000000..dd371005
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp/lacp.py
@@ -0,0 +1,74 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_lacp module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class LacpArgs(object):
+ """The arg spec for the nxos_lacp module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "options": {
+ "system": {
+ "options": {
+ "mac": {
+ "options": {
+ "address": {"type": "str"},
+ "role": {
+ "choices": ["primary", "secondary"],
+ "type": "str",
+ },
+ },
+ "type": "dict",
+ },
+ "priority": {"type": "int"},
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "deleted",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py
new file mode 100644
index 00000000..1e080a98
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py
@@ -0,0 +1,79 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_lacp_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class Lacp_interfacesArgs(object):
+ """The arg spec for the nxos_lacp_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "convergence": {
+ "options": {
+ "graceful": {"type": "bool"},
+ "vpc": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "links": {
+ "options": {
+ "max": {"type": "int"},
+ "min": {"type": "int"},
+ },
+ "type": "dict",
+ },
+ "mode": {"choices": ["delay"], "type": "str"},
+ "name": {"required": True, "type": "str"},
+ "port_priority": {"type": "int"},
+ "rate": {"choices": ["fast", "normal"], "type": "str"},
+ "suspend_individual": {"type": "bool"},
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "gathered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py
new file mode 100644
index 00000000..415f41b6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lag_interfaces/lag_interfaces.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The arg spec for the nxos_lag_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class Lag_interfacesArgs(object):
+ """The arg spec for the nxos_lag_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "members": {
+ "elements": "dict",
+ "options": {
+ "member": {"type": "str"},
+ "mode": {
+ "type": "str",
+ "choices": ["active", "on", "passive"],
+ },
+ "force": {"type": "bool"},
+ },
+ "type": "list",
+ },
+ "name": {"required": True, "type": "str"},
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "parsed",
+ "gathered",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/lldp_global.py
new file mode 100644
index 00000000..92ccc9b5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_global/lldp_global.py
@@ -0,0 +1,91 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_lldp_global module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class Lldp_globalArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_lldp_global module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "options": {
+ "holdtime": {"type": "int"},
+ "port_id": {"choices": [0, 1], "type": "int"},
+ "reinit": {"type": "int"},
+ "timer": {"type": "int"},
+ "tlv_select": {
+ "options": {
+ "dcbxp": {"type": "bool"},
+ "management_address": {
+ "options": {
+ "v4": {"type": "bool"},
+ "v6": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "port": {
+ "options": {
+ "description": {"type": "bool"},
+ "vlan": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "power_management": {"type": "bool"},
+ "system": {
+ "options": {
+ "capabilities": {"type": "bool"},
+ "description": {"type": "bool"},
+ "name": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "deleted",
+ "gathered",
+ "parsed",
+ "rendered",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py
new file mode 100644
index 00000000..4ea29a71
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/lldp_interfaces/lldp_interfaces.py
@@ -0,0 +1,70 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_lldp_interfaces module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class Lldp_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_lldp_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "name": {"required": True, "type": "str"},
+ "receive": {"type": "bool"},
+ "tlv_set": {
+ "options": {
+ "management_address": {"type": "str"},
+ "vlan": {"type": "int"},
+ },
+ "type": "dict",
+ },
+ "transmit": {"type": "bool"},
+ },
+ "type": "list",
+ },
+ "running_config": {"type": "str"},
+ "state": {
+ "choices": [
+ "deleted",
+ "gathered",
+ "merged",
+ "overridden",
+ "rendered",
+ "replaced",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/ospf_interfaces.py
new file mode 100644
index 00000000..c23a3238
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospf_interfaces/ospf_interfaces.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the
+# cli_rm_builder.
+#
+# Manually editing this file is not advised.
+#
+# To update the argspec make the desired changes
+# in the module docstring and re-run
+# cli_rm_builder.
+#
+#############################################
+
+"""
+The arg spec for the nxos_ospf_interfaces module
+"""
+
+
+class Ospf_interfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_ospf_interfaces module
+ """
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str", "required": True},
+ "address_family": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "afi": {
+ "type": "str",
+ "choices": ["ipv4", "ipv6"],
+ "required": True,
+ },
+ "processes": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "process_id": {
+ "type": "str",
+ "required": True,
+ },
+ "area": {
+ "type": "dict",
+ "options": {
+ "area_id": {
+ "type": "str",
+ "required": True,
+ },
+ "secondaries": {"type": "bool"},
+ },
+ },
+ "multi_areas": {
+ "type": "list",
+ "elements": "str",
+ },
+ },
+ },
+ "multi_areas": {"type": "list", "elements": "str"},
+ "authentication": {
+ "type": "dict",
+ "options": {
+ "key_chain": {"type": "str"},
+ "message_digest": {"type": "bool"},
+ "enable": {"type": "bool"},
+ "null_auth": {"type": "bool"},
+ },
+ },
+ "authentication_key": {
+ "type": "dict",
+ "options": {
+ "encryption": {"type": "int"},
+ "key": {"type": "str", "required": True},
+ },
+ },
+ "message_digest_key": {
+ "type": "dict",
+ "options": {
+ "key_id": {"type": "int", "required": True},
+ "encryption": {"type": "int"},
+ "key": {"type": "str", "required": True},
+ },
+ },
+ "cost": {"type": "int"},
+ "dead_interval": {"type": "int"},
+ "hello_interval": {"type": "int"},
+ "instance": {"type": "int"},
+ "mtu_ignore": {"type": "bool"},
+ "network": {
+ "type": "str",
+ "choices": ["broadcast", "point-to-point"],
+ },
+ "passive_interface": {"type": "bool"},
+ "priority": {"type": "int"},
+ "retransmit_interval": {"type": "int"},
+ "shutdown": {"type": "bool"},
+ "transmit_delay": {"type": "int"},
+ },
+ },
+ },
+ },
+ "state": {
+ "type": "str",
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "parsed",
+ "rendered",
+ ],
+ "default": "merged",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/ospfv2.py
new file mode 100644
index 00000000..09c61fc0
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv2/ospfv2.py
@@ -0,0 +1,697 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+"""
+The arg spec for the nxos_ospfv2 module
+"""
+
+
+class Ospfv2Args(object): # pylint: disable=R0903
+ """The arg spec for the nxos_ospfv2 module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "options": {
+ "processes": {
+ "elements": "dict",
+ "options": {
+ "areas": {
+ "required_one_of": [
+ [
+ "authentication",
+ "default_cost",
+ "filter_list",
+ "nssa",
+ "ranges",
+ "stub",
+ ]
+ ],
+ "elements": "dict",
+ "options": {
+ "area_id": {"type": "str", "required": True},
+ "authentication": {
+ "options": {
+ "set": {"type": "bool"},
+ "message_digest": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "default_cost": {"type": "int"},
+ "filter_list": {
+ "options": {
+ "direction": {
+ "choices": ["in", "out"],
+ "type": "str",
+ "required": True,
+ },
+ "route_map": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "list",
+ "elements": "dict",
+ },
+ "nssa": {
+ "options": {
+ "default_information_originate": {
+ "type": "bool"
+ },
+ "no_redistribution": {"type": "bool"},
+ "no_summary": {"type": "bool"},
+ "set": {"type": "bool"},
+ "translate": {
+ "options": {
+ "type7": {
+ "mutually_exclusive": [
+ ["always", "never"]
+ ],
+ "options": {
+ "always": {
+ "type": "bool"
+ },
+ "never": {
+ "type": "bool"
+ },
+ "supress_fa": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "ranges": {
+ "elements": "dict",
+ "options": {
+ "cost": {"type": "int"},
+ "not_advertise": {"type": "bool"},
+ "prefix": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "list",
+ },
+ "stub": {
+ "options": {
+ "no_summary": {"type": "bool"},
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "list",
+ },
+ "auto_cost": {
+ "options": {
+ "reference_bandwidth": {
+ "type": "int",
+ "required": True,
+ },
+ "unit": {
+ "choices": ["Gbps", "Mbps"],
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "dict",
+ },
+ "bfd": {"type": "bool"},
+ "default_information": {
+ "options": {
+ "originate": {
+ "options": {
+ "always": {"type": "bool"},
+ "route_map": {"type": "str"},
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ "default_metric": {"type": "int"},
+ "distance": {"type": "int"},
+ "flush_routes": {"type": "bool"},
+ "graceful_restart": {
+ "options": {
+ "grace_period": {"type": "int"},
+ "helper_disable": {"type": "bool"},
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "isolate": {"type": "bool"},
+ "log_adjacency_changes": {
+ "options": {
+ "detail": {"type": "bool"},
+ "log": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "max_lsa": {
+ "options": {
+ "ignore_count": {"type": "int"},
+ "ignore_time": {"type": "int"},
+ "max_non_self_generated_lsa": {
+ "type": "int",
+ "required": True,
+ },
+ "reset_time": {"type": "int"},
+ "threshold": {"type": "int"},
+ "warning_only": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "max_metric": {
+ "options": {
+ "router_lsa": {
+ "options": {
+ "external_lsa": {
+ "options": {
+ "max_metric_value": {
+ "type": "int"
+ },
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "include_stub": {"type": "bool"},
+ "on_startup": {
+ "options": {
+ "set": {"type": "bool"},
+ "wait_for_bgp_asn": {
+ "type": "int"
+ },
+ "wait_period": {"type": "int"},
+ },
+ "type": "dict",
+ },
+ "set": {"type": "bool"},
+ "summary_lsa": {
+ "options": {
+ "max_metric_value": {
+ "type": "int"
+ },
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ "maximum_paths": {"type": "int"},
+ "mpls": {
+ "options": {
+ "traffic_eng": {
+ "options": {
+ "areas": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "area_id": {"type": "str"}
+ },
+ },
+ "multicast_intact": {"type": "bool"},
+ "router_id": {"type": "str"},
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ "name_lookup": {"type": "bool"},
+ "passive_interface": {
+ "options": {"default": {"type": "bool"}},
+ "type": "dict",
+ },
+ "process_id": {"required": True, "type": "str"},
+ "redistribute": {
+ "elements": "dict",
+ "options": {
+ "id": {"type": "str"},
+ "protocol": {
+ "choices": [
+ "bgp",
+ "direct",
+ "eigrp",
+ "isis",
+ "lisp",
+ "ospf",
+ "rip",
+ "static",
+ ],
+ "required": True,
+ "type": "str",
+ },
+ "route_map": {"type": "str", "required": True},
+ },
+ "type": "list",
+ },
+ "rfc1583compatibility": {"type": "bool"},
+ "router_id": {"type": "str"},
+ "shutdown": {"type": "bool"},
+ "summary_address": {
+ "elements": "dict",
+ "mutually_exclusive": [["not_advertise", "tag"]],
+ "options": {
+ "not_advertise": {"type": "bool"},
+ "prefix": {"type": "str", "required": True},
+ "tag": {"type": "int"},
+ },
+ "type": "list",
+ },
+ "table_map": {
+ "options": {
+ "filter": {"type": "bool"},
+ "name": {"type": "str", "required": True},
+ },
+ "type": "dict",
+ },
+ "timers": {
+ "options": {
+ "lsa_arrival": {"type": "int"},
+ "lsa_group_pacing": {"type": "int"},
+ "throttle": {
+ "options": {
+ "lsa": {
+ "options": {
+ "hold_interval": {
+ "type": "int"
+ },
+ "max_interval": {
+ "type": "int"
+ },
+ "start_interval": {
+ "type": "int"
+ },
+ },
+ "type": "dict",
+ },
+ "spf": {
+ "options": {
+ "initial_spf_delay": {
+ "type": "int"
+ },
+ "max_wait_time": {
+ "type": "int"
+ },
+ "min_hold_time": {
+ "type": "int"
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "vrfs": {
+ "elements": "dict",
+ "options": {
+ "areas": {
+ "required_one_of": [
+ [
+ "authentication",
+ "default_cost",
+ "filter_list",
+ "nssa",
+ "ranges",
+ "stub",
+ ]
+ ],
+ "elements": "dict",
+ "options": {
+ "area_id": {
+ "type": "str",
+ "required": True,
+ },
+ "authentication": {
+ "options": {
+ "set": {"type": "bool"},
+ "message_digest": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ },
+ "default_cost": {"type": "int"},
+ "filter_list": {
+ "options": {
+ "direction": {
+ "choices": ["in", "out"],
+ "type": "str",
+ "required": True,
+ },
+ "route_map": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "list",
+ "elements": "dict",
+ },
+ "nssa": {
+ "options": {
+ "default_information_originate": {
+ "type": "bool"
+ },
+ "no_redistribution": {
+ "type": "bool"
+ },
+ "no_summary": {"type": "bool"},
+ "set": {"type": "bool"},
+ "translate": {
+ "options": {
+ "type7": {
+ "mutually_exclusive": [
+ [
+ "always",
+ "never",
+ ]
+ ],
+ "options": {
+ "always": {
+ "type": "bool"
+ },
+ "never": {
+ "type": "bool"
+ },
+ "supress_fa": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "ranges": {
+ "elements": "dict",
+ "options": {
+ "cost": {"type": "int"},
+ "not_advertise": {
+ "type": "bool"
+ },
+ "prefix": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "list",
+ },
+ "stub": {
+ "options": {
+ "no_summary": {"type": "bool"},
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "list",
+ },
+ "auto_cost": {
+ "options": {
+ "reference_bandwidth": {
+ "type": "int",
+ "required": True,
+ },
+ "unit": {
+ "choices": ["Gbps", "Mbps"],
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "dict",
+ },
+ "bfd": {"type": "bool"},
+ "capability": {
+ "type": "dict",
+ "options": {
+ "vrf_lite": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "evpn": {"type": "bool"},
+ },
+ }
+ },
+ },
+ "default_information": {
+ "options": {
+ "originate": {
+ "options": {
+ "always": {"type": "bool"},
+ "route_map": {"type": "str"},
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ "default_metric": {"type": "int"},
+ "distance": {"type": "int"},
+ "down_bit_ignore": {"type": "bool"},
+ "graceful_restart": {
+ "options": {
+ "grace_period": {"type": "int"},
+ "helper_disable": {"type": "bool"},
+ "set": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "log_adjacency_changes": {
+ "options": {
+ "detail": {"type": "bool"},
+ "log": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "max_lsa": {
+ "options": {
+ "ignore_count": {"type": "int"},
+ "ignore_time": {"type": "int"},
+ "max_non_self_generated_lsa": {
+ "type": "int",
+ "required": True,
+ },
+ "reset_time": {"type": "int"},
+ "threshold": {"type": "int"},
+ "warning_only": {"type": "bool"},
+ },
+ "type": "dict",
+ },
+ "max_metric": {
+ "options": {
+ "router_lsa": {
+ "options": {
+ "external_lsa": {
+ "options": {
+ "max_metric_value": {
+ "type": "int"
+ },
+ "set": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ },
+ "include_stub": {
+ "type": "bool"
+ },
+ "on_startup": {
+ "options": {
+ "set": {
+ "type": "bool"
+ },
+ "wait_for_bgp_asn": {
+ "type": "int"
+ },
+ "wait_period": {
+ "type": "int"
+ },
+ },
+ "type": "dict",
+ },
+ "set": {"type": "bool"},
+ "summary_lsa": {
+ "options": {
+ "max_metric_value": {
+ "type": "int"
+ },
+ "set": {
+ "type": "bool"
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ }
+ },
+ "type": "dict",
+ },
+ "maximum_paths": {"type": "int"},
+ "name_lookup": {"type": "bool"},
+ "passive_interface": {
+ "options": {"default": {"type": "bool"}},
+ "type": "dict",
+ },
+ "redistribute": {
+ "elements": "dict",
+ "options": {
+ "id": {"type": "str"},
+ "protocol": {
+ "choices": [
+ "bgp",
+ "direct",
+ "eigrp",
+ "isis",
+ "lisp",
+ "ospf",
+ "rip",
+ "static",
+ ],
+ "required": True,
+ "type": "str",
+ },
+ "route_map": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "list",
+ },
+ "rfc1583compatibility": {"type": "bool"},
+ "router_id": {"type": "str"},
+ "shutdown": {"type": "bool"},
+ "summary_address": {
+ "elements": "dict",
+ "options": {
+ "not_advertise": {"type": "bool"},
+ "prefix": {
+ "type": "str",
+ "required": True,
+ },
+ "tag": {"type": "int"},
+ },
+ "type": "list",
+ },
+ "table_map": {
+ "options": {
+ "filter": {"type": "bool"},
+ "name": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ "type": "dict",
+ },
+ "timers": {
+ "options": {
+ "lsa_arrival": {"type": "int"},
+ "lsa_group_pacing": {"type": "int"},
+ "throttle": {
+ "options": {
+ "lsa": {
+ "options": {
+ "hold_interval": {
+ "type": "int"
+ },
+ "max_interval": {
+ "type": "int"
+ },
+ "start_interval": {
+ "type": "int"
+ },
+ },
+ "type": "dict",
+ },
+ "spf": {
+ "options": {
+ "initial_spf_delay": {
+ "type": "int"
+ },
+ "max_wait_time": {
+ "type": "int"
+ },
+ "min_hold_time": {
+ "type": "int"
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ },
+ "type": "dict",
+ },
+ "vrf": {"required": True, "type": "str"},
+ },
+ "type": "list",
+ },
+ },
+ "type": "list",
+ }
+ },
+ "type": "dict",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/ospfv3.py
new file mode 100644
index 00000000..e4aadfb3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/ospfv3/ospfv3.py
@@ -0,0 +1,551 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The arg spec for the nxos_ospfv3 module
+"""
+
+
+class Ospfv3Args(object): # pylint: disable=R0903
+ """The arg spec for the nxos_ospfv3 module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "type": "dict",
+ "options": {
+ "processes": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "address_family": {
+ "type": "dict",
+ "options": {
+ "afi": {"type": "str", "choices": ["ipv6"]},
+ "safi": {
+ "type": "str",
+ "choices": ["unicast"],
+ },
+ "areas": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "area_id": {
+ "type": "str",
+ "required": True,
+ },
+ "default_cost": {"type": "int"},
+ "filter_list": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "route_map": {
+ "type": "str",
+ "required": True,
+ },
+ "direction": {
+ "type": "str",
+ "choices": ["in", "out"],
+ "required": True,
+ },
+ },
+ },
+ "ranges": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "prefix": {
+ "type": "str",
+ "required": True,
+ },
+ "cost": {"type": "int"},
+ "not_advertise": {
+ "type": "bool"
+ },
+ },
+ },
+ },
+ },
+ "default_information": {
+ "type": "dict",
+ "options": {
+ "originate": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "always": {"type": "bool"},
+ "route_map": {"type": "str"},
+ },
+ }
+ },
+ },
+ "distance": {"type": "int"},
+ "maximum_paths": {"type": "int"},
+ "redistribute": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "protocol": {
+ "type": "str",
+ "choices": [
+ "bgp",
+ "direct",
+ "eigrp",
+ "isis",
+ "lisp",
+ "ospfv3",
+ "rip",
+ "static",
+ ],
+ "required": True,
+ },
+ "id": {"type": "str"},
+ "route_map": {
+ "type": "str",
+ "required": True,
+ },
+ },
+ },
+ "summary_address": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "prefix": {
+ "type": "str",
+ "required": True,
+ },
+ "not_advertise": {"type": "bool"},
+ "tag": {"type": "int"},
+ },
+ },
+ "table_map": {
+ "type": "dict",
+ "options": {
+ "name": {
+ "type": "str",
+ "required": True,
+ },
+ "filter": {"type": "bool"},
+ },
+ },
+ "timers": {
+ "type": "dict",
+ "options": {
+ "throttle": {
+ "type": "dict",
+ "options": {
+ "spf": {
+ "type": "dict",
+ "options": {
+ "initial_spf_delay": {
+ "type": "int"
+ },
+ "min_hold_time": {
+ "type": "int"
+ },
+ "max_wait_time": {
+ "type": "int"
+ },
+ },
+ }
+ },
+ }
+ },
+ },
+ },
+ },
+ "areas": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "area_id": {"type": "str", "required": True},
+ "nssa": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "default_information_originate": {
+ "type": "bool"
+ },
+ "no_redistribution": {"type": "bool"},
+ "no_summary": {"type": "bool"},
+ "route_map": {"type": "str"},
+ "translate": {
+ "type": "dict",
+ "options": {
+ "type7": {
+ "type": "dict",
+ "options": {
+ "always": {
+ "type": "bool"
+ },
+ "never": {
+ "type": "bool"
+ },
+ "supress_fa": {
+ "type": "bool"
+ },
+ },
+ }
+ },
+ },
+ },
+ },
+ "stub": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "no_summary": {"type": "bool"},
+ },
+ },
+ },
+ },
+ "auto_cost": {
+ "type": "dict",
+ "options": {
+ "reference_bandwidth": {
+ "type": "int",
+ "required": True,
+ },
+ "unit": {
+ "type": "str",
+ "required": True,
+ "choices": ["Gbps", "Mbps"],
+ },
+ },
+ },
+ "flush_routes": {"type": "bool"},
+ "graceful_restart": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "grace_period": {"type": "int"},
+ "helper_disable": {"type": "bool"},
+ "planned_only": {"type": "bool"},
+ },
+ },
+ "isolate": {"type": "bool"},
+ "log_adjacency_changes": {
+ "type": "dict",
+ "options": {
+ "log": {"type": "bool"},
+ "detail": {"type": "bool"},
+ },
+ },
+ "max_lsa": {
+ "type": "dict",
+ "options": {
+ "max_non_self_generated_lsa": {
+ "type": "int",
+ "required": True,
+ },
+ "threshold": {"type": "int"},
+ "ignore_count": {"type": "int"},
+ "ignore_time": {"type": "int"},
+ "reset_time": {"type": "int"},
+ "warning_only": {"type": "bool"},
+ },
+ },
+ "max_metric": {
+ "type": "dict",
+ "options": {
+ "router_lsa": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "external_lsa": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "max_metric_value": {
+ "type": "int"
+ },
+ },
+ },
+ "stub_prefix_lsa": {"type": "bool"},
+ "on_startup": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "wait_period": {"type": "int"},
+ "wait_for_bgp_asn": {
+ "type": "int"
+ },
+ },
+ },
+ "inter_area_prefix_lsa": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "max_metric_value": {
+ "type": "int"
+ },
+ },
+ },
+ },
+ }
+ },
+ },
+ "name_lookup": {"type": "bool"},
+ "passive_interface": {
+ "type": "dict",
+ "options": {"default": {"type": "bool"}},
+ },
+ "process_id": {"type": "str", "required": True},
+ "router_id": {"type": "str"},
+ "shutdown": {"type": "bool"},
+ "timers": {
+ "type": "dict",
+ "options": {
+ "lsa_arrival": {"type": "int"},
+ "lsa_group_pacing": {"type": "int"},
+ "throttle": {
+ "type": "dict",
+ "options": {
+ "lsa": {
+ "type": "dict",
+ "options": {
+ "start_interval": {
+ "type": "int"
+ },
+ "hold_interval": {
+ "type": "int"
+ },
+ "max_interval": {
+ "type": "int"
+ },
+ },
+ }
+ },
+ },
+ },
+ },
+ "vrfs": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "areas": {
+ "type": "list",
+ "elements": "dict",
+ "options": {
+ "area_id": {
+ "type": "str",
+ "required": True,
+ },
+ "nssa": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "default_information_originate": {
+ "type": "bool"
+ },
+ "no_redistribution": {
+ "type": "bool"
+ },
+ "no_summary": {"type": "bool"},
+ "route_map": {"type": "str"},
+ "translate": {
+ "type": "dict",
+ "options": {
+ "type7": {
+ "type": "dict",
+ "options": {
+ "always": {
+ "type": "bool"
+ },
+ "never": {
+ "type": "bool"
+ },
+ "supress_fa": {
+ "type": "bool"
+ },
+ },
+ }
+ },
+ },
+ },
+ },
+ "stub": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "no_summary": {"type": "bool"},
+ },
+ },
+ },
+ },
+ "auto_cost": {
+ "type": "dict",
+ "options": {
+ "reference_bandwidth": {
+ "type": "int",
+ "required": True,
+ },
+ "unit": {
+ "type": "str",
+ "required": True,
+ "choices": ["Gbps", "Mbps"],
+ },
+ },
+ },
+ "graceful_restart": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "grace_period": {"type": "int"},
+ "helper_disable": {"type": "bool"},
+ "planned_only": {"type": "bool"},
+ },
+ },
+ "log_adjacency_changes": {
+ "type": "dict",
+ "options": {
+ "log": {"type": "bool"},
+ "detail": {"type": "bool"},
+ },
+ },
+ "max_lsa": {
+ "type": "dict",
+ "options": {
+ "max_non_self_generated_lsa": {
+ "type": "int",
+ "required": True,
+ },
+ "threshold": {"type": "int"},
+ "ignore_count": {"type": "int"},
+ "ignore_time": {"type": "int"},
+ "reset_time": {"type": "int"},
+ "warning_only": {"type": "bool"},
+ },
+ },
+ "max_metric": {
+ "type": "dict",
+ "options": {
+ "router_lsa": {
+ "type": "dict",
+ "options": {
+ "set": {"type": "bool"},
+ "external_lsa": {
+ "type": "dict",
+ "options": {
+ "set": {
+ "type": "bool"
+ },
+ "max_metric_value": {
+ "type": "int"
+ },
+ },
+ },
+ "stub_prefix_lsa": {
+ "type": "bool"
+ },
+ "on_startup": {
+ "type": "dict",
+ "options": {
+ "set": {
+ "type": "bool"
+ },
+ "wait_period": {
+ "type": "int"
+ },
+ "wait_for_bgp_asn": {
+ "type": "int"
+ },
+ },
+ },
+ "inter_area_prefix_lsa": {
+ "type": "dict",
+ "options": {
+ "set": {
+ "type": "bool"
+ },
+ "max_metric_value": {
+ "type": "int"
+ },
+ },
+ },
+ },
+ }
+ },
+ },
+ "name_lookup": {"type": "bool"},
+ "passive_interface": {
+ "type": "dict",
+ "options": {"default": {"type": "bool"}},
+ },
+ "router_id": {"type": "str"},
+ "shutdown": {"type": "bool"},
+ "timers": {
+ "type": "dict",
+ "options": {
+ "lsa_arrival": {"type": "int"},
+ "lsa_group_pacing": {"type": "int"},
+ "throttle": {
+ "type": "dict",
+ "options": {
+ "lsa": {
+ "type": "dict",
+ "options": {
+ "start_interval": {
+ "type": "int"
+ },
+ "hold_interval": {
+ "type": "int"
+ },
+ "max_interval": {
+ "type": "int"
+ },
+ },
+ }
+ },
+ },
+ },
+ },
+ "vrf": {"type": "str", "required": True},
+ },
+ },
+ },
+ }
+ },
+ },
+ "state": {
+ "type": "str",
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "gathered",
+ "parsed",
+ "rendered",
+ ],
+ "default": "merged",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/static_routes.py
new file mode 100644
index 00000000..95e548f3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/static_routes/static_routes.py
@@ -0,0 +1,94 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The arg spec for the nxos_static_routes module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class Static_routesArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_static_routes module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "address_families": {
+ "elements": "dict",
+ "options": {
+ "afi": {
+ "choices": ["ipv4", "ipv6"],
+ "required": True,
+ "type": "str",
+ },
+ "routes": {
+ "elements": "dict",
+ "options": {
+ "dest": {"required": True, "type": "str"},
+ "next_hops": {
+ "elements": "dict",
+ "options": {
+ "admin_distance": {"type": "int"},
+ "dest_vrf": {"type": "str"},
+ "forward_router_address": {
+ "type": "str"
+ },
+ "interface": {"type": "str"},
+ "route_name": {"type": "str"},
+ "tag": {"type": "int"},
+ "track": {"type": "int"},
+ },
+ "type": "list",
+ },
+ },
+ "type": "list",
+ },
+ },
+ "type": "list",
+ },
+ "vrf": {"type": "str"},
+ },
+ "type": "list",
+ },
+ "running_config": {"type": "str"},
+ "state": {
+ "choices": [
+ "deleted",
+ "merged",
+ "overridden",
+ "replaced",
+ "gathered",
+ "rendered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/telemetry.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/telemetry.py
new file mode 100644
index 00000000..1787f3ae
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/telemetry/telemetry.py
@@ -0,0 +1,118 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The arg spec for the nxos_telemetry module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class TelemetryArgs(object): # pylint: disable=R0903
+ """The arg spec for the nxos_telemetry module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "options": {
+ "certificate": {
+ "options": {
+ "hostname": {"type": "str"},
+ "key": {"type": "str"},
+ },
+ "type": "dict",
+ },
+ "compression": {"choices": ["gzip"], "type": "str"},
+ "source_interface": {"type": "str"},
+ "vrf": {"type": "str"},
+ "destination_groups": {
+ "options": {
+ "destination": {
+ "options": {
+ "encoding": {
+ "choices": ["GPB", "JSON"],
+ "type": "str",
+ },
+ "ip": {"type": "str"},
+ "port": {"type": "int"},
+ "protocol": {
+ "choices": ["HTTP", "TCP", "UDP", "gRPC"],
+ "type": "str",
+ },
+ },
+ "type": "dict",
+ },
+ "id": {"type": "int"},
+ },
+ "type": "list",
+ "elements": "raw",
+ },
+ "sensor_groups": {
+ "options": {
+ "data_source": {
+ "choices": ["NX-API", "DME", "YANG"],
+ "type": "str",
+ },
+ "id": {"type": "int"},
+ "path": {
+ "options": {
+ "depth": {"type": "str"},
+ "filter_condition": {"type": "str"},
+ "name": {"type": "str"},
+ "query_condition": {"type": "str"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "list",
+ "elements": "raw",
+ },
+ "subscriptions": {
+ "options": {
+ "destination_group": {"type": "int"},
+ "id": {"type": "int"},
+ "sensor_group": {
+ "options": {
+ "id": {"type": "int"},
+ "sample_interval": {"type": "int"},
+ },
+ "type": "dict",
+ },
+ },
+ "type": "list",
+ "elements": "raw",
+ },
+ },
+ "type": "dict",
+ },
+ "state": {
+ "choices": ["merged", "replaced", "deleted"],
+ "default": "merged",
+ "type": "str",
+ },
+ } # pylint: disable=C0301
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/vlans.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/vlans.py
new file mode 100644
index 00000000..539d9e2e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/argspec/vlans/vlans.py
@@ -0,0 +1,67 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The arg spec for the nxos_vlans module
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class VlansArgs(object):
+ """The arg spec for the nxos_vlans module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "running_config": {"type": "str"},
+ "config": {
+ "elements": "dict",
+ "options": {
+ "enabled": {"type": "bool"},
+ "mapped_vni": {"type": "int"},
+ "mode": {"choices": ["ce", "fabricpath"], "type": "str"},
+ "name": {"type": "str"},
+ "vlan_id": {"required": True, "type": "int"},
+ "state": {"choices": ["active", "suspend"], "type": "str"},
+ },
+ "type": "list",
+ },
+ "state": {
+ "choices": [
+ "merged",
+ "replaced",
+ "overridden",
+ "deleted",
+ "rendered",
+ "gathered",
+ "parsed",
+ ],
+ "default": "merged",
+ "type": "str",
+ },
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/telemetry.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/telemetry.py
new file mode 100644
index 00000000..187a2c42
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/cmdref/telemetry/telemetry.py
@@ -0,0 +1,146 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+# Telemetry Command Reference File
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+TMS_GLOBAL = """
+# The cmd_ref is a yaml formatted list of module commands.
+# A leading underscore denotes a non-command variable; e.g. _template.
+# TMS does not have convenient global json data so this cmd_ref uses raw cli configs.
+---
+_template: # _template holds common settings for all commands
+ # Enable feature telemetry if disabled
+ feature: telemetry
+ # Common get syntax for TMS commands
+ get_command: show run telemetry all
+ # Parent configuration for TMS commands
+ context:
+ - telemetry
+certificate:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ kind: dict
+ getval: certificate (?P<key>\\S+) (?P<hostname>\\S+)$
+ setval: certificate {key} {hostname}
+ default:
+ key: ~
+ hostname: ~
+compression:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ kind: str
+ getval: use-compression (\\S+)$
+ setval: 'use-compression {0}'
+ default: ~
+ context: &dpcontext
+ - telemetry
+ - destination-profile
+source_interface:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ kind: str
+ getval: source-interface (\\S+)$
+ setval: 'source-interface {0}'
+ default: ~
+ context: *dpcontext
+vrf:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ kind: str
+ getval: use-vrf (\\S+)$
+ setval: 'use-vrf {0}'
+ default: ~
+ context: *dpcontext
+"""
+
+TMS_DESTGROUP = """
+# The cmd_ref is a yaml formatted list of module commands.
+# A leading underscore denotes a non-command variable; e.g. _template.
+# TBD: Use Structured Where Possible
+---
+_template: # _template holds common settings for all commands
+ # Enable feature telemetry if disabled
+ feature: telemetry
+ # Common get syntax for TMS commands
+ get_command: show run telemetry all
+ # Parent configuration for TMS commands
+ context:
+ - telemetry
+destination:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ multiple: true
+ kind: dict
+ getval: ip address (?P<ip>\\S+) port (?P<port>\\S+) protocol (?P<protocol>\\S+) encoding (?P<encoding>\\S+)
+ setval: ip address {ip} port {port} protocol {protocol} encoding {encoding}
+ default:
+ ip: ~
+ port: ~
+ protocol: ~
+ encoding: ~
+"""
+
+TMS_SENSORGROUP = """
+# The cmd_ref is a yaml formatted list of module commands.
+# A leading underscore denotes a non-command variable; e.g. _template.
+# TBD: Use Structured Where Possible
+---
+_template: # _template holds common settings for all commands
+ # Enable feature telemetry if disabled
+ feature: telemetry
+ # Common get syntax for TMS commands
+ get_command: show run telemetry all
+ # Parent configuration for TMS commands
+ context:
+ - telemetry
+data_source:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ kind: str
+ getval: data-source (\\S+)$
+ setval: 'data-source {0}'
+ default: ~
+path:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ multiple: true
+ kind: dict
+ getval: path (?P<name>(\\S+|".*"))( depth (?P<depth>\\S+))?( query-condition (?P<query_condition>\\S+))?( filter-condition (?P<filter_condition>\\S+))?$
+ setval: path {name} depth {depth} query-condition {query_condition} filter-condition {filter_condition}
+ default:
+ name: ~
+ depth: ~
+ query_condition: ~
+ filter_condition: ~
+"""
+
+TMS_SUBSCRIPTION = """
+# The cmd_ref is a yaml formatted list of module commands.
+# A leading underscore denotes a non-command variable; e.g. _template.
+# TBD: Use Structured Where Possible
+---
+_template: # _template holds common settings for all commands
+ # Enable feature telemetry if disabled
+ feature: telemetry
+ # Common get syntax for TMS commands
+ get_command: show run telemetry all
+ # Parent configuration for TMS commands
+ context:
+ - telemetry
+destination_group:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ multiple: true
+ kind: int
+ getval: dst-grp (\\S+)$
+ setval: 'dst-grp {0}'
+ default: ~
+sensor_group:
+ _exclude: ['N3K', 'N5K', 'N6k', 'N7k']
+ multiple: true
+ kind: dict
+ getval: snsr-grp (?P<id>\\S+) sample-interval (?P<sample_interval>\\S+)$
+ setval: snsr-grp {id} sample-interval {sample_interval}
+ default:
+ id: ~
+ sample_interval: ~
+"""
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py
new file mode 100644
index 00000000..b6b208ad
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acl_interfaces/acl_interfaces.py
@@ -0,0 +1,350 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_acl_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+ dict_diff,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ search_obj_in_list,
+ get_interface_type,
+ normalize_interface,
+)
+
+
+class Acl_interfaces(ConfigBase):
+ """
+ The nxos_acl_interfaces class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["acl_interfaces"]
+
+ def __init__(self, module):
+ super(Acl_interfaces, self).__init__(module)
+
+ def get_acl_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ acl_interfaces_facts = facts["ansible_network_resources"].get(
+ "acl_interfaces"
+ )
+ if not acl_interfaces_facts:
+ return []
+ return acl_interfaces_facts
+
+ def edit_config(self, commands):
+ """Wrapper method for `_connection.edit_config()`
+ This exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ warnings = list()
+ commands = list()
+ self.state = self._module.params["state"]
+ action_states = ["merged", "replaced", "deleted", "overridden"]
+
+ if self.state == "gathered":
+ result["gathered"] = self.get_acl_interfaces_facts()
+ elif self.state == "rendered":
+ result["rendered"] = self.set_config({})
+ # no need to fetch facts for rendered
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.set_config({})
+ # no need to fetch facts for parsed
+ else:
+ existing_acl_interfaces_facts = self.get_acl_interfaces_facts()
+ commands.extend(self.set_config(existing_acl_interfaces_facts))
+ if commands and self.state in action_states:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+ result["before"] = existing_acl_interfaces_facts
+ result["commands"] = commands
+
+ changed_acl_interfaces_facts = self.get_acl_interfaces_facts()
+ if result["changed"]:
+ result["after"] = changed_acl_interfaces_facts
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_acl_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params["config"]
+ want = []
+ if config:
+ for w in config:
+ if get_interface_type(w["name"]) == "loopback":
+ self._module.fail_json(
+ msg="This module works with ethernet, management or port-channe"
+ )
+ w.update({"name": normalize_interface(w["name"])})
+ want.append(remove_empties(w))
+ have = existing_acl_interfaces_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ if (
+ self.state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ self.state
+ )
+ )
+
+ commands = []
+ if self.state == "overridden":
+ commands = self._state_overridden(want, have)
+ elif self.state == "deleted":
+ commands = self._state_deleted(want, have)
+ elif self.state == "rendered":
+ commands = self._state_rendered(want)
+ elif self.state == "parsed":
+ want = self._module.params["running_config"]
+ commands = self._state_parsed(want)
+ else:
+ for w in want:
+ if self.state == "merged":
+ commands.extend(self._state_merged(w, have))
+ elif self.state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_parsed(self, want):
+ return self.get_acl_interfaces_facts(want)
+
+ def _state_rendered(self, want):
+ commands = []
+ for w in want:
+ commands.extend(self.set_commands(w, {}))
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ new_commands = []
+ del_dict = {"name": want["name"], "access_groups": []}
+ obj_in_have = search_obj_in_list(want["name"], have, "name")
+ if obj_in_have != want:
+ commands = []
+ if obj_in_have and "access_groups" in obj_in_have.keys():
+ for ag in obj_in_have["access_groups"]:
+ want_afi = []
+ if want.get("access_groups"):
+ want_afi = search_obj_in_list(
+ ag["afi"], want["access_groups"], "afi"
+ )
+ if not want_afi:
+ # whatever in have is not in want
+ del_dict["access_groups"].append(ag)
+ else:
+ del_acl = []
+ for acl in ag["acls"]:
+ if want_afi.get("acls"):
+ if acl not in want_afi["acls"]:
+ del_acl.append(acl)
+ else:
+ del_acl.append(acl)
+ afi = want_afi["afi"]
+ del_dict["access_groups"].append(
+ {"afi": afi, "acls": del_acl}
+ )
+
+ commands.extend(self._state_deleted([del_dict], have))
+ commands.extend(self._state_merged(want, have))
+ new_commands.append(commands[0])
+ commands = [
+ commands[i]
+ for i in range(1, len(commands))
+ if commands[i] != commands[0]
+ ]
+ new_commands.extend(commands)
+ return new_commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ want_intf = [w["name"] for w in want]
+ for h in have:
+ if h["name"] not in want_intf:
+ commands.extend(self._state_deleted([h], have))
+ for w in want:
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def set_commands(self, want, have, deleted=False):
+ commands = []
+ have_name = search_obj_in_list(want["name"], have, "name")
+ if have_name and have_name.get("access_groups"):
+ if want.get("access_groups"):
+ for w_afi in want["access_groups"]:
+ ip = "ipv6"
+ if w_afi["afi"] == "ipv4":
+ ip = "ip"
+ have_afi = search_obj_in_list(
+ w_afi["afi"], have_name["access_groups"], "afi"
+ )
+ if have_afi:
+ new_acls = []
+ if deleted:
+ if w_afi.get("acls") and have_afi.get("acls"):
+ new_acls = [
+ acl
+ for acl in w_afi.get("acls")
+ if acl in have_afi.get("acls")
+ ]
+ elif "acls" not in w_afi.keys():
+ new_acls = have_afi.get("acls")
+ else:
+ if w_afi.get("acls"):
+ new_acls = [
+ acl
+ for acl in w_afi["acls"]
+ if acl not in have_afi["acls"]
+ ]
+ commands.extend(
+ self.process_acl(new_acls, ip, deleted)
+ )
+ else:
+ if not deleted:
+ if w_afi.get("acls"):
+ commands.extend(
+ self.process_acl(w_afi["acls"], ip)
+ )
+ else:
+ # only name is given to delete
+ if deleted and "access_groups" in have_name.keys():
+ commands.extend(self.process_access_group(have_name, True))
+ else:
+ if not deleted: # and 'access_groups' in have_name.keys():
+ commands.extend(self.process_access_group(want))
+
+ if len(commands) > 0:
+ commands.insert(0, "interface " + want["name"])
+ return commands
+
+ def process_access_group(self, item, deleted=False):
+ commands = []
+ for ag in item["access_groups"]:
+ ip = "ipv6"
+ if ag["afi"] == "ipv4":
+ ip = "ip"
+ if ag.get("acls"):
+ commands.extend(self.process_acl(ag["acls"], ip, deleted))
+ return commands
+
+ def process_acl(self, acls, ip, deleted=False):
+ commands = []
+ no = ""
+ if deleted:
+ no = "no "
+ for acl in acls:
+ port = ""
+ if acl.get("port"):
+ port = " port"
+ ag = " access-group "
+ if ip == "ipv6":
+ ag = " traffic-filter "
+ commands.append(
+ no + ip + port + ag + acl["name"] + " " + acl["direction"]
+ )
+ return commands
+
+ def _state_deleted(self, main_want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if main_want:
+ if self.state == "deleted":
+ for w in main_want:
+ h = search_obj_in_list(w["name"], have, "name") or {}
+ commands.extend(self.set_commands(h, have, deleted=True))
+ else:
+ for want in main_want:
+ commands.extend(
+ self.set_commands(want, have, deleted=True)
+ )
+ else:
+ for h in have:
+ commands.extend(self.set_commands(h, have, deleted=True))
+
+ return commands
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/acls.py
new file mode 100644
index 00000000..64372df6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/acls/acls.py
@@ -0,0 +1,765 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_acls class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import socket
+import re
+from copy import deepcopy
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+ dict_diff,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acls.acls import (
+ AclsArgs,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ search_obj_in_list,
+ get_interface_type,
+ normalize_interface,
+)
+
+
+class Acls(ConfigBase):
+ """
+ The nxos_acls class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["acls"]
+
+ def __init__(self, module):
+ super(Acls, self).__init__(module)
+
+ def get_acls_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ acls_facts = facts["ansible_network_resources"].get("acls")
+ if not acls_facts:
+ return []
+ return acls_facts
+
+ def edit_config(self, commands):
+ """Wrapper method for `_connection.edit_config()`
+ This exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ warnings = list()
+ commands = list()
+ state = self._module.params["state"]
+ action_states = ["merged", "replaced", "deleted", "overridden"]
+
+ if state == "gathered":
+ result["gathered"] = self.get_acls_facts()
+ elif state == "rendered":
+ result["rendered"] = self.set_config({})
+ elif state == "parsed":
+ result["parsed"] = self.set_config({})
+ else:
+ existing_acls_facts = self.get_acls_facts()
+ commands.extend(self.set_config(existing_acls_facts))
+ if commands and state in action_states:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+ result["before"] = existing_acls_facts
+ result["commands"] = commands
+
+ changed_acls_facts = self.get_acls_facts()
+ if result["changed"]:
+ result["after"] = changed_acls_facts
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_acls_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params["config"]
+ want = []
+ if config:
+ for w in config:
+ want.append(remove_empties(w))
+ have = existing_acls_facts
+ if want:
+ want = self.convert_values(want)
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def convert_values(self, want):
+ """
+ This method is used to map and convert the user given values with what will actually be present in the device configuation
+ """
+ port_protocol = {
+ 515: "lpd",
+ 517: "talk",
+ 7: "echo",
+ 9: "discard",
+ 12: "exec",
+ 13: "login",
+ 14: "cmd",
+ 109: "pop2",
+ 19: "chargen",
+ 20: "ftp-data",
+ 21: "ftp",
+ 23: "telnet",
+ 25: "smtp",
+ 540: "uucp",
+ 543: "klogin",
+ 544: "kshell",
+ 37: "time",
+ 43: "whois",
+ 49: "tacacs",
+ 179: "bgp",
+ 53: "domain",
+ 194: "irc",
+ 70: "gopher",
+ 79: "finger",
+ 80: "www",
+ 101: "hostname",
+ 3949: "drip",
+ 110: "pop3",
+ 111: "sunrpc",
+ 496: "pim-auto-rp",
+ 113: "ident",
+ 119: "nntp",
+ }
+ protocol = {
+ 1: "icmp",
+ 2: "igmp",
+ 4: "ip",
+ 6: "tcp",
+ 103: "pim",
+ 108: "pcp",
+ 47: "gre",
+ 17: "udp",
+ 50: "esp",
+ 51: "ahp",
+ 88: "eigrp",
+ 89: "ospf",
+ 94: "nos",
+ }
+ precedence = {
+ 0: "routine",
+ 1: "priority",
+ 2: "immediate",
+ 3: "flash",
+ 4: "flash-override",
+ 5: "critical",
+ 6: "internet",
+ 7: "network",
+ }
+ dscp = {
+ 10: "AF11",
+ 12: "AF12",
+ 14: "AF13",
+ 18: "AF21",
+ 20: "AF22",
+ 22: "AF23",
+ 26: "AF31",
+ 28: "AF32",
+ 30: "AF33",
+ 34: "AF41",
+ 36: "AF42",
+ 38: "AF43",
+ 8: "CS1",
+ 16: "CS2",
+ 24: "CS3",
+ 32: "CS4",
+ 40: "CS5",
+ 48: "CS6",
+ 56: "CS7",
+ 0: "Default",
+ 46: "EF",
+ }
+ # port_pro_num = list(protocol.keys())
+ for afi in want:
+ if "acls" in afi.keys():
+ for acl in afi["acls"]:
+ if "aces" in acl.keys():
+ for ace in acl["aces"]:
+ if "dscp" in ace.keys():
+ if ace["dscp"].isdigit():
+ ace["dscp"] = dscp[int(ace["dscp"])]
+ ace["dscp"] = ace["dscp"].lower()
+ if "precedence" in ace.keys():
+ if ace["precedence"].isdigit():
+ ace["precedence"] = precedence[
+ int(ace["precedence"])
+ ]
+ if (
+ "protocol" in ace.keys()
+ and ace["protocol"].isdigit()
+ and int(ace["protocol"]) in protocol.keys()
+ ):
+ ace["protocol"] = protocol[
+ int(ace["protocol"])
+ ]
+ # convert number to name
+ if "protocol" in ace.keys() and ace[
+ "protocol"
+ ] in ["tcp", "udp"]:
+ for end in ["source", "destination"]:
+ if "port_protocol" in ace[end].keys():
+ key = list(
+ ace[end]["port_protocol"].keys()
+ )[0]
+ # key could be eq,gt,lt,neq or range
+ if key != "range":
+ val = ace[end]["port_protocol"][
+ key
+ ]
+ if (
+ val.isdigit()
+ and int(val)
+ in port_protocol.keys()
+ ):
+ ace[end]["port_protocol"][
+ key
+ ] = port_protocol[int(val)]
+ else:
+ st = int(
+ ace[end]["port_protocol"][
+ "range"
+ ]["start"]
+ )
+
+ end = int(
+ ace[end]["port_protocol"][
+ "range"
+ ]["end"]
+ )
+
+ if st in port_protocol.keys():
+ ace[end]["port_protocol"][
+ "range"
+ ]["start"] = port_protocol[st]
+ if end in port_protocol.keys():
+ ace[end]["port_protocol"][
+ "range"
+ ]["end"] = port_protocol[end]
+ return want
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ commands = []
+ if state == "overridden":
+ commands = self._state_overridden(want, have)
+ elif state == "deleted":
+ commands = self._state_deleted(want, have)
+ elif state == "rendered":
+ commands = self._state_rendered(want)
+ elif state == "parsed":
+ want = self._module.params["running_config"]
+ commands = self._state_parsed(want)
+ else:
+ for w in want:
+ if state == "merged":
+ commands.extend(self._state_merged(w, have))
+ elif state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+ if state != "parsed":
+ commands = [c.strip() for c in commands]
+ return commands
+
+ def _state_parsed(self, want):
+ return self.get_acls_facts(want)
+
+ def _state_rendered(self, want):
+ commands = []
+ for w in want:
+ commands.extend(self.set_commands(w, {}))
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ have_afi = search_obj_in_list(want["afi"], have, "afi")
+ del_dict = {"acls": []}
+ want_names = []
+ if have_afi != want:
+ if have_afi:
+ del_dict.update({"afi": have_afi["afi"], "acls": []})
+ if want.get("acls"):
+ want_names = [w["name"] for w in want["acls"]]
+ have_names = [h["name"] for h in have_afi["acls"]]
+ want_acls = want.get("acls")
+ for w in want_acls:
+ acl_commands = []
+ if w["name"] not in have_names:
+ # creates new ACL in replaced state
+ merge_dict = {"afi": want["afi"], "acls": [w]}
+ commands.extend(
+ self._state_merged(merge_dict, have)
+ )
+ else:
+ # acl in want exists in have
+ have_name = search_obj_in_list(
+ w["name"], have_afi["acls"], "name"
+ )
+ have_aces = (
+ have_name.get("aces")
+ if have_name.get("aces")
+ else []
+ )
+ merge_aces = []
+ del_aces = []
+ w_aces = w.get("aces") if w.get("aces") else []
+
+ for ace in have_aces:
+ if ace not in w_aces:
+ del_aces.append(ace)
+ for ace in w_aces:
+ if ace not in have_aces:
+ merge_aces.append(ace)
+ merge_dict = {
+ "afi": want["afi"],
+ "acls": [
+ {"name": w["name"], "aces": merge_aces}
+ ],
+ }
+ del_dict = {
+ "afi": want["afi"],
+ "acls": [
+ {"name": w["name"], "aces": del_aces}
+ ],
+ }
+ if del_dict["acls"]:
+ acl_commands.extend(
+ self._state_deleted([del_dict], have)
+ )
+ acl_commands.extend(
+ self._state_merged(merge_dict, have)
+ )
+
+ for i in range(1, len(acl_commands)):
+ if acl_commands[i] == acl_commands[0]:
+ acl_commands[i] = ""
+ commands.extend(acl_commands)
+ else:
+ acls = []
+ # no acls given in want, so delete all have acls
+ for acl in have_afi["acls"]:
+ acls.append({"name": acl["name"]})
+ del_dict["acls"] = acls
+ if del_dict["acls"]:
+ commands.extend(self._state_deleted([del_dict], have))
+
+ else:
+ # want_afi is not present in have
+ commands.extend(self._state_merged(want, have))
+
+ commands = list(filter(None, commands))
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ want_afi = [w["afi"] for w in want]
+ for h in have:
+ if h["afi"] in want_afi:
+ w = search_obj_in_list(h["afi"], want, "afi")
+ for h_acl in h["acls"]:
+ w_acl = search_obj_in_list(
+ h_acl["name"], w["acls"], "name"
+ )
+ if not w_acl:
+ del_dict = {
+ "afi": h["afi"],
+ "acls": [{"name": h_acl["name"]}],
+ }
+ commands.extend(self._state_deleted([del_dict], have))
+ else:
+ # if afi is not in want
+ commands.extend(self._state_deleted([{"afi": h["afi"]}], have))
+ for w in want:
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want: # and have != want:
+ for w in want:
+ ip = "ipv6" if w["afi"] == "ipv6" else "ip"
+ acl_names = []
+ have_afi = search_obj_in_list(w["afi"], have, "afi")
+ # if want['afi] not in have, ignore
+ if have_afi:
+ if w.get("acls"):
+ for acl in w["acls"]:
+ if (
+ "aces" in acl.keys()
+ and self._module.params["state"] != "deleted"
+ ):
+ have_name = search_obj_in_list(
+ acl["name"], have_afi["acls"], "name"
+ )
+ if have_name:
+ ace_commands = []
+ flag = 0
+ for ace in acl["aces"]:
+ if list(ace.keys()) == ["sequence"]:
+ # only sequence number is specified to be deleted
+ if "aces" in have_name.keys():
+ for h_ace in have_name["aces"]:
+ if (
+ h_ace["sequence"]
+ == ace["sequence"]
+ ):
+ ace_commands.append(
+ "no "
+ + str(
+ ace["sequence"]
+ )
+ )
+ flag = 1
+ else:
+ if "aces" in have_name.keys():
+ for h_ace in have_name["aces"]:
+ # when want['ace'] does not have seq number
+ if (
+ "sequence"
+ not in ace.keys()
+ ):
+ del h_ace["sequence"]
+ if ace == h_ace:
+ ace_commands.append(
+ "no "
+ + self.process_ace(
+ ace
+ )
+ )
+ flag = 1
+ if flag:
+ ace_commands.insert(
+ 0,
+ ip + " access-list " + acl["name"],
+ )
+ commands.extend(ace_commands)
+ else:
+ # only name given
+ for h in have_afi["acls"]:
+ if h["name"] == acl["name"]:
+ acl_names.append(acl["name"])
+ for name in acl_names:
+ commands.append(
+ "no " + ip + " access-list " + name
+ )
+
+ else:
+ # 'only afi is given'
+ if have_afi.get("acls"):
+ for h in have_afi["acls"]:
+ acl_names.append(h["name"])
+ for name in acl_names:
+ commands.append(
+ "no " + ip + " access-list " + name
+ )
+ else:
+ v6 = []
+ v4 = []
+ v6_local = v4_local = None
+ for h in have:
+ if h["afi"] == "ipv6":
+ v6 = (acl["name"] for acl in h["acls"])
+ if "match_local_traffic" in h.keys():
+ v6_local = True
+ else:
+ v4 = (acl["name"] for acl in h["acls"])
+ if "match_local_traffic" in h.keys():
+ v4_local = True
+
+ self.no_commands(v4, commands, v4_local, "ip")
+ self.no_commands(v6, commands, v6_local, "ipv6")
+
+ for name in v6:
+ commands.append("no ipv6 access-list " + name)
+ if v4_local:
+ commands.append("no ipv6 access-list match-local-traffic")
+
+ return commands
+
+ def no_commands(self, v_list, commands, match_local, ip):
+ for name in v_list:
+ commands.append("no " + ip + " access-list " + name)
+ if match_local:
+ commands.append("no " + ip + " access-list match-local-traffic")
+
+ def set_commands(self, want, have):
+ commands = []
+ have_afi = search_obj_in_list(want["afi"], have, "afi")
+ ip = ""
+ if "v6" in want["afi"]:
+ ip = "ipv6 "
+ else:
+ ip = "ip "
+
+ if have_afi:
+ if want.get("acls"):
+ for w_acl in want["acls"]:
+ have_acl = search_obj_in_list(
+ w_acl["name"], have_afi["acls"], "name"
+ )
+ name = w_acl["name"]
+ flag = 0
+ ace_commands = []
+ if have_acl != w_acl:
+ if have_acl:
+ ace_list = []
+ if w_acl.get("aces") and have_acl.get("aces"):
+ # case 1 --> sequence number not given in want --> new ace
+ # case 2 --> new sequence number in want --> new ace
+ # case 3 --> existing sequence number given --> update rule (only for merged state.
+ # For replaced and overridden, rule is deleted in the state's config)
+
+ ace_list = [
+ item
+ for item in w_acl["aces"]
+ if "sequence" not in item.keys()
+ ] # case 1
+
+ want_seq = [
+ item["sequence"]
+ for item in w_acl["aces"]
+ if "sequence" in item.keys()
+ ]
+
+ have_seq = [
+ item["sequence"]
+ for item in have_acl["aces"]
+ ]
+
+ new_seq = list(set(want_seq) - set(have_seq))
+ common_seq = list(
+ set(want_seq).intersection(set(have_seq))
+ )
+
+ temp_list = [
+ item
+ for item in w_acl["aces"]
+ if "sequence" in item.keys()
+ and item["sequence"] in new_seq
+ ] # case 2
+ ace_list.extend(temp_list)
+ for w in w_acl["aces"]:
+ self.argument_spec = AclsArgs.argument_spec
+ params = utils.validate_config(
+ self.argument_spec,
+ {
+ "config": [
+ {
+ "afi": want["afi"],
+ "acls": [
+ {
+ "name": name,
+ "aces": ace_list,
+ }
+ ],
+ }
+ ]
+ },
+ )
+ if (
+ "sequence" in w.keys()
+ and w["sequence"] in common_seq
+ ):
+ temp_obj = search_obj_in_list(
+ w["sequence"],
+ have_acl["aces"],
+ "sequence",
+ ) # case 3
+ if temp_obj != w:
+ for key, val in w.items():
+ temp_obj[key] = val
+ ace_list.append(temp_obj)
+ if (
+ self._module.params["state"]
+ == "merged"
+ ):
+ ace_commands.append(
+ "no " + str(w["sequence"])
+ )
+ # remove existing rule to update it
+ elif w_acl.get("aces"):
+ # 'have' has ACL defined without any ACE
+ ace_list = [item for item in w_acl["aces"]]
+ for w_ace in ace_list:
+ ace_commands.append(
+ self.process_ace(w_ace).strip()
+ )
+ flag = 1
+
+ if flag:
+ ace_commands.insert(
+ 0, ip + "access-list " + name
+ )
+
+ else:
+ commands.append(ip + "access-list " + name)
+ if "aces" in w_acl.keys():
+ for w_ace in w_acl["aces"]:
+ commands.append(
+ self.process_ace(w_ace).strip()
+ )
+ commands.extend(ace_commands)
+ else:
+ if want.get("acls"):
+ for w_acl in want["acls"]:
+ name = w_acl["name"]
+ commands.append(ip + "access-list " + name)
+ if "aces" in w_acl.keys():
+ for w_ace in w_acl["aces"]:
+ commands.append(self.process_ace(w_ace).strip())
+
+ return commands
+
+ def process_ace(self, w_ace):
+ command = ""
+ ace_keys = w_ace.keys()
+ if "remark" in ace_keys:
+ command += "remark " + w_ace["remark"] + " "
+ else:
+ command += w_ace["grant"] + " "
+ if "protocol" in ace_keys:
+ command += w_ace["protocol"] + " "
+ src = self.get_address(w_ace["source"], w_ace["protocol"])
+ dest = self.get_address(
+ w_ace["destination"], w_ace["protocol"]
+ )
+ command += src + dest
+ if "protocol_options" in ace_keys:
+ pro = list(w_ace["protocol_options"].keys())[0]
+ if pro != w_ace["protocol"]:
+ self._module.fail_json(
+ msg="protocol and protocol_options mismatch"
+ )
+ flags = ""
+ for k in w_ace["protocol_options"][pro].keys():
+ k = re.sub("_", "-", k)
+ flags += k + " "
+ command += flags
+ if "dscp" in ace_keys:
+ command += "dscp " + w_ace["dscp"] + " "
+ if "fragments" in ace_keys:
+ command += "fragments "
+ if "precedence" in ace_keys:
+ command += "precedence " + w_ace["precedence"] + " "
+ if "log" in ace_keys:
+ command += "log "
+ if "sequence" in ace_keys:
+ command = str(w_ace["sequence"]) + " " + command
+ return command
+
+ def get_address(self, endpoint, pro=""):
+ ret_addr = ""
+ keys = list(endpoint.keys())
+ if "address" in keys:
+ if "wildcard_bits" not in keys:
+ self._module.fail_json(
+ msg="wildcard bits not specified for address"
+ )
+ else:
+ ret_addr = (
+ endpoint["address"] + " " + endpoint["wildcard_bits"] + " "
+ )
+ elif "any" in keys:
+ ret_addr = "any "
+ elif "host" in keys:
+ ret_addr = "host " + endpoint["host"] + " "
+ elif "prefix" in keys:
+ ret_addr = endpoint["prefix"] + " "
+
+ if pro in ["tcp", "udp"]:
+ if "port_protocol" in keys:
+ options = self.get_options(endpoint["port_protocol"])
+ ret_addr += options
+ return ret_addr
+
+ def get_options(self, item):
+ com = ""
+ subkey = list(item.keys())
+ if "range" in subkey:
+ com = (
+ "range "
+ + item["range"]["start"]
+ + " "
+ + item["range"]["end"]
+ + " "
+ )
+ else:
+ com = subkey[0] + " " + item[subkey[0]] + " "
+ return com
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py
new file mode 100644
index 00000000..b115ce49
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bfd_interfaces/bfd_interfaces.py
@@ -0,0 +1,323 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+nxos_bfd_interfaces class
+This class creates a command set to bring the current device configuration
+to a desired end-state. The command set is based on a comparison of the
+current configuration (as dict) and the provided configuration (as dict).
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_diff,
+ to_list,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ search_obj_in_list,
+)
+
+
+class Bfd_interfaces(ConfigBase):
+ """
+ The nxos_bfd_interfaces class
+ """
+
+ gather_subset = ["min"]
+ gather_network_resources = ["bfd_interfaces"]
+ # exclude_params = []
+
+ def __init__(self, module):
+ super(Bfd_interfaces, self).__init__(module)
+
+ def get_bfd_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :returns: A list of interface configs and a platform string
+ """
+ if self.state not in self.ACTION_STATES:
+ self.gather_subset = ["!all", "!min"]
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ bfd_interfaces_facts = facts["ansible_network_resources"].get(
+ "bfd_interfaces", []
+ )
+
+ platform = facts.get("ansible_net_platform", "")
+ return bfd_interfaces_facts, platform
+
+ def edit_config(self, commands):
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ warnings = list()
+ commands = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_bfd_interfaces_facts, platform = (
+ self.get_bfd_interfaces_facts()
+ )
+ else:
+ existing_bfd_interfaces_facts, platform = [], ""
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(
+ self.set_config(existing_bfd_interfaces_facts, platform)
+ )
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_bfd_interfaces_facts, platform = (
+ self.get_bfd_interfaces_facts()
+ )
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"], platform = self.get_bfd_interfaces_facts(
+ data=running_config
+ )
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_bfd_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_bfd_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_bfd_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_bfd_interfaces_facts, platform):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ if re.search("N[56]K", platform):
+ # Some platforms do not support the 'bfd' interface keyword;
+ # remove the 'bfd' key from each want/have interface.
+ orig_want = self._module.params["config"]
+ want = []
+ for w in orig_want:
+ del w["bfd"]
+ want.append(w)
+ orig_have = existing_bfd_interfaces_facts
+ have = []
+ for h in orig_have:
+ del h["bfd"]
+ have.append(h)
+ else:
+ want = self._module.params["config"]
+ have = existing_bfd_interfaces_facts
+
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ cmds = list()
+ if state == "overridden":
+ cmds.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ cmds.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ cmds.extend(self._state_merged(flatten_dict(w), have))
+ elif state == "replaced":
+ cmds.extend(self._state_replaced(flatten_dict(w), have))
+ return cmds
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ cmds = []
+ obj_in_have = search_obj_in_list(want["name"], have, "name")
+ if obj_in_have:
+ diff = dict_diff(want, obj_in_have)
+ else:
+ diff = want
+ merged_cmds = self.set_commands(want, have)
+ if "name" not in diff:
+ diff["name"] = want["name"]
+
+ replaced_cmds = []
+ if obj_in_have:
+ replaced_cmds = self.del_attribs(diff)
+ if replaced_cmds or merged_cmds:
+ for cmd in set(replaced_cmds).intersection(set(merged_cmds)):
+ merged_cmds.remove(cmd)
+ cmds.extend(replaced_cmds)
+ cmds.extend(merged_cmds)
+ return cmds
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ cmds = []
+ for h in have:
+ # Clean up bfd attrs for any interfaces not listed in the play
+ h = flatten_dict(h)
+ obj_in_want = flatten_dict(
+ search_obj_in_list(h["name"], want, "name")
+ )
+ if obj_in_want:
+ # Let the 'want' loop handle all vals for this interface
+ continue
+ cmds.extend(self.del_attribs(h))
+ for w in want:
+ # Update any want attrs if needed. The overridden state considers
+ # the play as the source of truth for the entire device, therefore
+ # set any unspecified attrs to their default state.
+ w = self.set_none_vals_to_defaults(flatten_dict(w))
+ cmds.extend(self.set_commands(w, have))
+ return cmds
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ if not (want or have):
+ return []
+ cmds = []
+ if want:
+ for w in want:
+ obj_in_have = flatten_dict(
+ search_obj_in_list(w["name"], have, "name")
+ )
+ cmds.extend(self.del_attribs(obj_in_have))
+ else:
+ for h in have:
+ cmds.extend(self.del_attribs(flatten_dict(h)))
+ return cmds
+
+ def del_attribs(self, obj):
+ if not obj or len(obj.keys()) == 1:
+ return []
+ cmds = []
+ # 'bfd' and 'bfd echo' are enabled by default so the handling is
+ # counter-intuitive; we are enabling them to remove them. The end result
+ # is that they are removed from the interface config on the device.
+ if "bfd" in obj and "disable" in obj["bfd"]:
+ cmds.append("bfd")
+ if "echo" in obj and "disable" in obj["echo"]:
+ cmds.append("bfd echo")
+ if cmds:
+ cmds.insert(0, "interface " + obj["name"])
+ return cmds
+
+ def set_none_vals_to_defaults(self, want):
+ # Set dict None values to default states
+ if "bfd" in want and want["bfd"] is None:
+ want["bfd"] = "enable"
+ if "echo" in want and want["echo"] is None:
+ want["echo"] = "enable"
+ return want
+
+ def diff_of_dicts(self, want, obj_in_have):
+ diff = set(want.items()) - set(obj_in_have.items())
+ diff = dict(diff)
+ if diff and want["name"] == obj_in_have["name"]:
+ diff.update({"name": want["name"]})
+ return diff
+
+ def add_commands(self, want):
+ if not want:
+ return []
+ cmds = []
+ if "bfd" in want and want["bfd"] is not None:
+ cmd = "bfd" if want["bfd"] == "enable" else "no bfd"
+ cmds.append(cmd)
+ if "echo" in want and want["echo"] is not None:
+ cmd = "bfd echo" if want["echo"] == "enable" else "no bfd echo"
+ cmds.append(cmd)
+
+ if cmds:
+ cmds.insert(0, "interface " + want["name"])
+ return cmds
+
+ def set_commands(self, want, have):
+ cmds = []
+ obj_in_have = flatten_dict(
+ search_obj_in_list(want["name"], have, "name")
+ )
+ if not obj_in_have:
+ cmds = self.add_commands(want)
+ else:
+ diff = self.diff_of_dicts(want, obj_in_have)
+ cmds = self.add_commands(diff)
+ return cmds
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/bgp_global.py
new file mode 100644
index 00000000..6481c9b6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/bgp_global/bgp_global.py
@@ -0,0 +1,412 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The nxos_bgp_global config file.
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to its desired end-state is
+created.
+"""
+
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_merge,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import (
+ ResourceModule,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.bgp_global import (
+ Bgp_globalTemplate,
+)
+
+
+class Bgp_global(ResourceModule):
+ """
+ The nxos_bgp_global config class
+ """
+
+ def __init__(self, module):
+ super(Bgp_global, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="bgp_global",
+ tmplt=Bgp_globalTemplate(),
+ )
+ # VRF parsers = 29
+ self.parsers = [
+ "allocate_index",
+ "affinity_group.group_id",
+ "bestpath.always_compare_med",
+ "bestpath.as_path.ignore",
+ "bestpath.as_path.multipath_relax",
+ "bestpath.compare_neighborid",
+ "bestpath.compare_routerid",
+ "bestpath.cost_community_ignore",
+ "bestpath.igp_metric_ignore",
+ "bestpath.med.confed",
+ "bestpath.med.missing_as_worst",
+ "bestpath.med.non_deterministic",
+ "cluster_id",
+ "local_as",
+ "confederation.identifier",
+ "graceful_restart",
+ "graceful_restart.restart_time",
+ "graceful_restart.stalepath_time",
+ "graceful_restart.helper",
+ "log_neighbor_changes",
+ "maxas_limit",
+ "neighbor_down.fib_accelerate",
+ "reconnect_interval",
+ "router_id",
+ "timers.bestpath_limit",
+ "timers.bgp",
+ "timers.prefix_peer_timeout",
+ "timers.prefix_peer_wait",
+ # end VRF parsers
+ "disable_policy_batching",
+ "disable_policy_batching.ipv4.prefix_list",
+ "disable_policy_batching.ipv6.prefix_list",
+ "disable_policy_batching.nexthop",
+ "dynamic_med_interval",
+ "enforce_first_as",
+ "enhanced_error",
+ "fast_external_fallover",
+ "flush_routes",
+ "graceful_shutdown.activate",
+ "graceful_shutdown.aware",
+ "isolate",
+ "nexthop.suppress_default_resolution",
+ "shutdown",
+ "suppress_fib_pending",
+ "fabric_soo",
+ "rd",
+ ]
+ self._af_data = {}
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ if self.state not in ["parsed", "gathered"]:
+ self.generate_commands()
+ self.run_commands()
+ return self.result
+
+ def generate_commands(self):
+ """ Generate configuration commands to send based on
+ want, have and desired state.
+ """
+ if self.state in ["deleted", "replaced"]:
+ self._build_af_data()
+
+ for entry in self.want, self.have:
+ self._bgp_list_to_dict(entry)
+
+ # if state is deleted, clean up global params
+ if self.state == "deleted":
+ if not self.want or (
+ self.have.get("as_number") == self.want.get("as_number")
+ ):
+ self._compare(want={}, have=self.have)
+
+ elif self.state == "purged":
+ if not self.want or (
+ self.have.get("as_number") == self.want.get("as_number")
+ ):
+ self.addcmd(self.have or {}, "as_number", True)
+
+ else:
+ wantd = self.want
+ # if state is merged, merge want onto have and then compare
+ if self.state == "merged":
+ wantd = dict_merge(self.have, self.want)
+
+ self._compare(want=wantd, have=self.have)
+
+ def _compare(self, want, have, vrf=None):
+ """Leverages the base class `compare()` method and
+ populates the list of commands to be run by comparing
+ the `want` and `have` data with the `parsers` defined
+ for the Bgp_global network resource.
+ """
+ begin = len(self.commands)
+ self.compare(parsers=self.parsers, want=want, have=have)
+ self._compare_confederation_peers(want, have)
+ self._compare_neighbors(want, have, vrf=vrf)
+ self._vrfs_compare(want=want, have=have)
+
+ if len(self.commands) != begin:
+ self.commands.insert(
+ begin,
+ self._tmplt.render(
+ want or have,
+ "vrf"
+ if "vrf" in (want.keys() or have.keys())
+ else "as_number",
+ False,
+ ),
+ )
+
+ def _compare_confederation_peers(self, want, have):
+ """Custom handling of confederation.peers option
+
+ :params want: the want BGP dictionary
+ :params have: the have BGP dictionary
+ """
+ w_cpeers = want.get("confederation", {}).get("peers", [])
+ h_cpeers = have.get("confederation", {}).get("peers", [])
+
+ if set(w_cpeers) != set(h_cpeers):
+ if self.state in ["replaced", "deleted"]:
+ # if there are peers already configured
+ # we need to remove those before we pass
+ # the new ones otherwise the device appends
+ # them to the existing ones
+ if h_cpeers:
+ self.addcmd(have, "confederation.peers", True)
+ if w_cpeers:
+ self.addcmd(want, "confederation.peers", False)
+
+ def _compare_neighbors(self, want, have, vrf=None):
+ """Custom handling of neighbors option
+
+ :params want: the want BGP dictionary
+ :params have: the have BGP dictionary
+ """
+ nbr_parsers = [
+ "remote_as",
+ "neighbor_affinity_group.group_id",
+ "bmp_activate_server",
+ "capability",
+ "description",
+ "disable_connected_check",
+ "dont_capability_negotiate",
+ "dscp",
+ "dynamic_capability",
+ "ebgp_multihop",
+ "graceful_shutdown",
+ "inherit.peer",
+ "inherit.peer_session",
+ "local_as",
+ "log_neighbor_changes",
+ "low_memory",
+ "password",
+ "peer_type",
+ "remove_private_as",
+ "shutdown",
+ "timers",
+ "transport",
+ "ttl_security",
+ "update_source",
+ ]
+ wnbrs = want.get("neighbors", {})
+ hnbrs = have.get("neighbors", {})
+
+ # neighbors have separate contexts in NX-OS
+ for name, entry in iteritems(wnbrs):
+ begin = len(self.commands)
+ have_nbr = hnbrs.pop(name, {})
+
+ self.compare(parsers=nbr_parsers, want=entry, have=have_nbr)
+ self._compare_path_attribute(entry, have_nbr)
+
+ if len(self.commands) != begin:
+ self.commands.insert(
+ begin, self._tmplt.render(entry, "neighbor_address", False)
+ )
+
+ # cleanup remaining neighbors
+ # but do not negate it entirely
+ # instead remove only those attributes
+ # that this module manages
+ for name, entry in iteritems(hnbrs):
+ if self._has_af(vrf=vrf, neighbor=name):
+ self._module.fail_json(
+ msg="Neighbor {0} has address-family configurations. "
+ "Please use the nxos_bgp_neighbor_af module to remove those first.".format(
+ name
+ )
+ )
+ else:
+ self.addcmd(entry, "neighbor_address", True)
+
+ def _compare_path_attribute(self, want, have):
+ """Custom handling of neighbor path_attribute
+ option.
+
+ :params want: the want neighbor dictionary
+ :params have: the have neighbor dictionary
+ """
+ w_p_attr = want.get("path_attribute", {})
+ h_p_attr = have.get("path_attribute", {})
+
+ for wkey, wentry in iteritems(w_p_attr):
+ if wentry != h_p_attr.pop(wkey, {}):
+ self.addcmd(wentry, "path_attribute", False)
+
+ # remove remaining items in have for replaced
+ for hkey, hentry in iteritems(h_p_attr):
+ self.addcmd(hentry, "path_attribute", True)
+
+ def _vrfs_compare(self, want, have):
+ """Custom handling of VRFs option
+
+ :params want: the want BGP dictionary
+ :params have: the have BGP dictionary
+ """
+ wvrfs = want.get("vrfs", {})
+ hvrfs = have.get("vrfs", {})
+ for name, entry in iteritems(wvrfs):
+ self._compare(want=entry, have=hvrfs.pop(name, {}), vrf=name)
+ # cleanup remaining VRFs
+ # but do not negate it entirely
+ # instead remove only those attributes
+ # that this module manages
+ for name, entry in iteritems(hvrfs):
+ if self._has_af(vrf=name):
+ self._module.fail_json(
+ msg="VRF {0} has address-family configurations. "
+ "Please use the nxos_bgp_af module to remove those first.".format(
+ name
+ )
+ )
+ else:
+ self.addcmd(entry, "vrf", True)
+
+ def _bgp_list_to_dict(self, entry):
+ """Convert list of items to dict of items
+ for efficient diff calculation.
+
+ :params entry: data dictionary
+ """
+
+ def _build_key(x):
+ """Build primary key for path_attribute
+ option.
+ :params x: path_attribute dictionary
+ :returns: primary key as tuple
+ """
+ key_1 = "start_{0}".format(x.get("range", {}).get("start", ""))
+ key_2 = "end_{0}".format(x.get("range", {}).get("end", ""))
+ key_3 = "type_{0}".format(x.get("type", ""))
+ key_4 = x["action"]
+
+ return (key_1, key_2, key_3, key_4)
+
+ if "neighbors" in entry:
+ for x in entry["neighbors"]:
+ if "path_attribute" in x:
+ x["path_attribute"] = {
+ _build_key(item): item
+ for item in x.get("path_attribute", [])
+ }
+
+ entry["neighbors"] = {
+ x["neighbor_address"]: x for x in entry.get("neighbors", [])
+ }
+
+ if "vrfs" in entry:
+ entry["vrfs"] = {x["vrf"]: x for x in entry.get("vrfs", [])}
+ for _k, vrf in iteritems(entry["vrfs"]):
+ self._bgp_list_to_dict(vrf)
+
+ def _get_config(self):
+ return self._connection.get(
+ "show running-config | section '^router bgp'"
+ )
+
+ def _build_af_data(self):
+ """Build a dictionary with AF related information
+ from fetched BGP config.
+ _af_data = {
+ gbl_data = {'192.168.1.100', '192.168.1.101'},
+ vrf_data = {
+ 'vrf_1': {
+ 'has_af': True,
+ 'nbrs': {'192.0.1.1', '192.8.1.1'}
+ },
+ 'vrf_2': {
+ 'has_af': False,
+ 'nbrs': set()
+ }
+ }
+ }
+ """
+ data = self._get_config().split("\n")
+ cur_nbr = None
+ cur_vrf = None
+ gbl_data = set()
+ vrf_data = {}
+
+ for x in data:
+ if x.strip().startswith("vrf"):
+ cur_nbr = None
+ cur_vrf = x.split(" ")[-1]
+ vrf_data[cur_vrf] = {"nbrs": set(), "has_af": False}
+
+ elif x.strip().startswith("neighbor"):
+ cur_nbr = x.split(" ")[-1]
+
+ elif x.strip().startswith("address-family"):
+ if cur_nbr:
+ if cur_vrf:
+ vrf_data[cur_vrf]["nbrs"].add(cur_nbr)
+ else:
+ gbl_data.add(cur_nbr)
+ else:
+ if cur_vrf:
+ vrf_data[cur_vrf]["has_af"] = True
+
+ self._af_data["global"] = gbl_data
+ self._af_data["vrf"] = vrf_data
+
+ def _has_af(self, vrf=None, neighbor=None):
+ """Determine if the given vrf + neighbor
+ combination has AF configurations.
+
+ :params vrf: vrf name
+ :params neighbor: neighbor name
+ :returns: bool
+ """
+ has_af = False
+
+ if self._af_data:
+ vrf_af_data = self._af_data.get("vrf", {})
+ global_af_data = self._af_data.get("global", set())
+ if vrf:
+ vrf_nbr_has_af = vrf_af_data.get(vrf, {}).get("nbrs", set())
+ vrf_has_af = vrf_af_data.get(vrf, {}).get("has_af", False)
+ if neighbor and neighbor in vrf_nbr_has_af:
+ # we are inspecting neighbor within a VRF
+ # if the given neighbor has AF we return True
+ has_af = True
+ else:
+ # we are inspecting VRF as a whole
+ # if there is at least one neighbor
+ # with AF or VRF has AF itself return True
+ if vrf_nbr_has_af or vrf_has_af:
+ has_af = True
+ else:
+ # we are inspecting top level neighbors
+ # if the given neighbor has AF we return True
+ if neighbor and neighbor in global_af_data:
+ has_af = True
+
+ return has_af
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py
new file mode 100644
index 00000000..04df23f3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/hsrp_interfaces/hsrp_interfaces.py
@@ -0,0 +1,302 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos hsrp_interfaces class
+This class creates a command set to bring the current device configuration
+to a desired end-state. The command set is based on a comparison of the
+current configuration (as dict) and the provided configuration (as dict).
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_diff,
+ to_list,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ get_interface_type,
+ normalize_interface,
+ search_obj_in_list,
+ vlan_range_to_list,
+)
+
+
+class Hsrp_interfaces(ConfigBase):
+ """
+ The nxos_hsrp_interfaces class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["hsrp_interfaces"]
+
+ def __init__(self, module):
+ super(Hsrp_interfaces, self).__init__(module)
+
+ def get_hsrp_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ hsrp_interfaces_facts = facts["ansible_network_resources"].get(
+ "hsrp_interfaces", []
+ )
+ return hsrp_interfaces_facts
+
+ def edit_config(self, commands):
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ warnings = []
+ commands = []
+
+ if self.state in self.ACTION_STATES:
+ existing_hsrp_interfaces_facts = self.get_hsrp_interfaces_facts()
+ else:
+ existing_hsrp_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_hsrp_interfaces_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_hsrp_interfaces_facts = self.get_hsrp_interfaces_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_hsrp_interfaces_facts(
+ data=running_config
+ )
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_hsrp_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_hsrp_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_hsrp_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_hsrp_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params["config"]
+ want = []
+ if config:
+ for w in config:
+ w.update({"name": normalize_interface(w["name"])})
+ want.append(w)
+ have = existing_hsrp_interfaces_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ # check for 'config' keyword in play
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ cmds = list()
+ if state == "overridden":
+ cmds.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ cmds.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ cmds.extend(self._state_merged(flatten_dict(w), have))
+ elif state == "replaced":
+ cmds.extend(self._state_replaced(flatten_dict(w), have))
+ return cmds
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ cmds = []
+ obj_in_have = search_obj_in_list(want["name"], have, "name")
+ if obj_in_have:
+ diff = dict_diff(want, obj_in_have)
+ else:
+ diff = want
+ merged_cmds = self.set_commands(want, have)
+ if "name" not in diff:
+ diff["name"] = want["name"]
+
+ replaced_cmds = []
+ if obj_in_have:
+ replaced_cmds = self.del_attribs(diff)
+ if replaced_cmds or merged_cmds:
+ for cmd in set(replaced_cmds).intersection(set(merged_cmds)):
+ merged_cmds.remove(cmd)
+ cmds.extend(replaced_cmds)
+ cmds.extend(merged_cmds)
+ return cmds
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ cmds = []
+ for h in have:
+ # Check existing states, set to default if not in want or different than want
+ h = flatten_dict(h)
+ obj_in_want = search_obj_in_list(h["name"], want, "name")
+ if obj_in_want:
+ # Let the 'want' loop handle all vals for this interface
+ continue
+ cmds.extend(self.del_attribs(h))
+ for w in want:
+ # Update any want attrs if needed. The overridden state considers
+ # the play as the source of truth for the entire device, therefore
+ # set any unspecified attrs to their default state.
+ w = self.set_none_vals_to_defaults(flatten_dict(w))
+ cmds.extend(self.set_commands(w, have))
+ return cmds
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ if not (want or have):
+ return []
+ cmds = []
+ if want:
+ for w in want:
+ obj_in_have = flatten_dict(
+ search_obj_in_list(w["name"], have, "name")
+ )
+ cmds.extend(self.del_attribs(obj_in_have))
+ else:
+ for h in have:
+ cmds.extend(self.del_attribs(flatten_dict(h)))
+ return cmds
+
+ def del_attribs(self, obj):
+ if not obj or len(obj.keys()) == 1:
+ return []
+ cmds = []
+ if "bfd" in obj:
+ cmds.append("no hsrp bfd")
+ if cmds:
+ cmds.insert(0, "interface " + obj["name"])
+ return cmds
+
+ def set_none_vals_to_defaults(self, want):
+ # Set dict None values to default states
+ if "bfd" in want and want["bfd"] is None:
+ want["bfd"] = "disable"
+ return want
+
+ def diff_of_dicts(self, want, obj_in_have):
+ diff = set(want.items()) - set(obj_in_have.items())
+ diff = dict(diff)
+ if diff and want["name"] == obj_in_have["name"]:
+ diff.update({"name": want["name"]})
+ return diff
+
+ def add_commands(self, want, obj_in_have):
+ if not want:
+ return []
+ cmds = []
+ if "bfd" in want and want["bfd"] is not None:
+ if want["bfd"] == "enable":
+ cmd = "hsrp bfd"
+ cmds.append(cmd)
+ elif (
+ want["bfd"] == "disable"
+ and obj_in_have
+ and obj_in_have.get("bfd") == "enable"
+ ):
+ cmd = "no hsrp bfd"
+ cmds.append(cmd)
+
+ if cmds:
+ cmds.insert(0, "interface " + want["name"])
+ return cmds
+
+ def set_commands(self, want, have):
+ cmds = []
+ obj_in_have = search_obj_in_list(want["name"], have, "name")
+ if not obj_in_have:
+ cmds = self.add_commands(want, obj_in_have)
+ else:
+ diff = self.diff_of_dicts(want, obj_in_have)
+ cmds = self.add_commands(diff, obj_in_have)
+ return cmds
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/interfaces.py
new file mode 100644
index 00000000..6d17421a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/interfaces/interfaces.py
@@ -0,0 +1,495 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from copy import deepcopy
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_diff,
+ to_list,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ normalize_interface,
+ search_obj_in_list,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ remove_rsvd_interfaces,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ default_intf_enabled,
+)
+
+
+class Interfaces(ConfigBase):
+ """
+ The nxos_interfaces class
+ """
+
+ gather_subset = ["min"]
+
+ gather_network_resources = ["interfaces"]
+
+ exclude_params = ["description", "mtu", "speed", "duplex"]
+
+ def __init__(self, module):
+ super(Interfaces, self).__init__(module)
+
+ def get_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :data: Mocked running-config data for state `parsed`
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ self.facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ interfaces_facts = self.facts["ansible_network_resources"].get(
+ "interfaces"
+ )
+
+ return interfaces_facts
+
+ def get_platform(self):
+ """Wrapper method for getting platform info
+ This method exists solely to allow the unit test framework to mock calls.
+ """
+ return self.facts.get("ansible_net_platform", "")
+
+ def get_system_defaults(self):
+ """Wrapper method for `_connection.get()`
+ This method exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.get(
+ "show running-config all | incl 'system default switchport'"
+ )
+
+ def edit_config(self, commands):
+ """Wrapper method for `_connection.edit_config()`
+ This method exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = []
+ warnings = []
+
+ if self.state in self.ACTION_STATES:
+ existing_interfaces_facts = self.get_interfaces_facts()
+ else:
+ existing_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES:
+ self.intf_defs = self.render_interface_defaults(
+ self.get_system_defaults(), existing_interfaces_facts
+ )
+ commands.extend(self.set_config(existing_interfaces_facts))
+
+ if self.state == "rendered":
+ # Hardcode the system defaults for "rendered"
+ # This can be made a configurable option in the future
+ self.intf_defs = {
+ "sysdefs": {
+ "L2_enabled": False,
+ "L3_enabled": False,
+ "mode": "layer3",
+ }
+ }
+ commands.extend(self.set_config(existing_interfaces_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_interfaces_facts = self.get_interfaces_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_interfaces_facts(data=running_config)
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params.get("config")
+ want = []
+ if config:
+ for w in config:
+ w.update({"name": normalize_interface(w["name"])})
+ want.append(remove_empties(w))
+ have = deepcopy(existing_interfaces_facts)
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ commands = list()
+ if state == "overridden":
+ commands.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(w, have))
+ elif state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_replaced(self, w, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ name = w["name"]
+ obj_in_have = search_obj_in_list(name, have, "name")
+ if obj_in_have:
+ # If 'w' does not specify mode then intf may need to change to its
+ # default mode, however default mode may depend on sysdef.
+ if not w.get("mode") and re.search("Ethernet|port-channel", name):
+ sysdefs = self.intf_defs["sysdefs"]
+ sysdef_mode = sysdefs["mode"]
+ if obj_in_have.get("mode") != sysdef_mode:
+ w["mode"] = sysdef_mode
+ diff = dict_diff(w, obj_in_have)
+ else:
+ diff = w
+
+ merged_commands = self.set_commands(w, have)
+ # merged_commands:
+ # - These commands are changes specified by the playbook.
+ # - merged_commands apply to both existing and new objects
+ # replaced_commands:
+ # - These are the unspecified commands, used to reset any params
+ # that are not already set to default states
+ # - replaced_commands should only be used on 'have' objects
+ # (interfaces that already exist)
+ if obj_in_have:
+ if "name" not in diff:
+ diff["name"] = name
+ wkeys = w.keys()
+ dkeys = diff.keys()
+ for k in wkeys:
+ if k in self.exclude_params and k in dkeys:
+ del diff[k]
+ replaced_commands = self.del_attribs(diff)
+ cmds = set(replaced_commands).intersection(set(merged_commands))
+ for cmd in cmds:
+ merged_commands.remove(cmd)
+ commands.extend(replaced_commands)
+
+ commands.extend(merged_commands)
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ # overridden is the same as replaced behavior except for the scope.
+ cmds = []
+ existing_interfaces = []
+ for h in have:
+ existing_interfaces.append(h["name"])
+ obj_in_want = search_obj_in_list(h["name"], want, "name")
+ if obj_in_want:
+ if h != obj_in_want:
+ replaced_cmds = self._state_replaced(obj_in_want, [h])
+ if replaced_cmds:
+ cmds.extend(replaced_cmds)
+ else:
+ cmds.extend(self.del_attribs(h))
+
+ for w in want:
+ if w["name"] not in existing_interfaces:
+ # This is an object that was excluded from the 'have' list
+ # because all of its params are currently set to default states
+ # -OR- it's a new object that does not exist on the device yet.
+ cmds.extend(self.add_commands(w))
+ return cmds
+
+ def _state_merged(self, w, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(w, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = search_obj_in_list(w["name"], have, "name")
+ commands.extend(self.del_attribs(obj_in_have))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.extend(self.del_attribs(h))
+ return commands
+
+ def default_enabled(self, want=None, have=None, action=""):
+ # 'enabled' default state depends on the interface type and L2 state.
+ # Note that the current default could change when changing L2/L3 modes.
+ if self.state == "rendered":
+ # For "rendered", we always assume that
+ # the default enabled state is False
+ return False
+ if want is None:
+ want = {}
+ if have is None:
+ have = {}
+ name = have.get("name")
+ if name is None:
+ return None
+
+ sysdefs = self.intf_defs["sysdefs"]
+ sysdef_mode = sysdefs["mode"]
+
+ # Get the default enabled state for this interface. This was collected
+ # during Facts gathering.
+ intf_def_enabled = self.intf_defs.get(name)
+
+ have_mode = have.get("mode", sysdef_mode)
+ if action == "delete" and not want:
+ want_mode = sysdef_mode
+ else:
+ want_mode = want.get("mode", have_mode)
+ if (
+ (want_mode and have_mode) is None
+ or (want_mode != have_mode)
+ or intf_def_enabled is None
+ ):
+ # L2-L3 is changing or this is a new virtual intf. Get new default.
+ intf_def_enabled = default_intf_enabled(
+ name=name, sysdefs=sysdefs, mode=want_mode
+ )
+ return intf_def_enabled
+
+ def del_attribs(self, obj):
+ commands = []
+ if not obj or len(obj.keys()) == 1:
+ return commands
+ # mode/switchport changes should occur before other changes
+ sysdef_mode = self.intf_defs["sysdefs"]["mode"]
+ if "mode" in obj and obj["mode"] != sysdef_mode:
+ no_cmd = "no " if sysdef_mode == "layer3" else ""
+ commands.append(no_cmd + "switchport")
+ if "description" in obj:
+ commands.append("no description")
+ if "speed" in obj:
+ commands.append("no speed")
+ if "duplex" in obj:
+ commands.append("no duplex")
+ if "enabled" in obj:
+ sysdef_enabled = self.default_enabled(have=obj, action="delete")
+ if obj["enabled"] is False and sysdef_enabled is True:
+ commands.append("no shutdown")
+ elif obj["enabled"] is True and sysdef_enabled is False:
+ commands.append("shutdown")
+ if "mtu" in obj:
+ commands.append("no mtu")
+ if "ip_forward" in obj and obj["ip_forward"] is True:
+ commands.append("no ip forward")
+ if (
+ "fabric_forwarding_anycast_gateway" in obj
+ and obj["fabric_forwarding_anycast_gateway"] is True
+ ):
+ commands.append("no fabric forwarding mode anycast-gateway")
+ if commands:
+ commands.insert(0, "interface " + obj["name"])
+
+ return commands
+
+ def diff_of_dicts(self, w, obj):
+ diff = set(w.items()) - set(obj.items())
+ diff = dict(diff)
+ if diff and w["name"] == obj["name"]:
+ diff.update({"name": w["name"]})
+ return diff
+
+ def add_commands(self, d, obj_in_have=None):
+ commands = []
+ if not d:
+ return commands
+ if obj_in_have is None:
+ obj_in_have = {}
+ # mode/switchport changes should occur before other changes
+ if "mode" in d:
+ sysdef_mode = self.intf_defs["sysdefs"]["mode"]
+ have_mode = obj_in_have.get("mode", sysdef_mode)
+ want_mode = d["mode"]
+ if have_mode == "layer2":
+ if want_mode == "layer3":
+ commands.append("no switchport")
+ elif want_mode == "layer2":
+ commands.append("switchport")
+ if "description" in d:
+ commands.append("description " + d["description"])
+ if "speed" in d:
+ commands.append("speed " + str(d["speed"]))
+ if "duplex" in d:
+ commands.append("duplex " + d["duplex"])
+ if "enabled" in d:
+ have_enabled = obj_in_have.get(
+ "enabled", self.default_enabled(d, obj_in_have)
+ )
+ if d["enabled"] is False and have_enabled is True:
+ commands.append("shutdown")
+ elif d["enabled"] is True and have_enabled is False:
+ commands.append("no shutdown")
+ if "mtu" in d:
+ commands.append("mtu " + str(d["mtu"]))
+ if "ip_forward" in d:
+ if d["ip_forward"] is True:
+ commands.append("ip forward")
+ else:
+ commands.append("no ip forward")
+ if "fabric_forwarding_anycast_gateway" in d:
+ if d["fabric_forwarding_anycast_gateway"] is True:
+ commands.append("fabric forwarding mode anycast-gateway")
+ else:
+ commands.append("no fabric forwarding mode anycast-gateway")
+ if commands or not obj_in_have:
+ commands.insert(0, "interface" + " " + d["name"])
+ return commands
+
+ def set_commands(self, w, have):
+ commands = []
+ obj_in_have = search_obj_in_list(w["name"], have, "name")
+ if not obj_in_have:
+ commands = self.add_commands(w)
+ else:
+ diff = self.diff_of_dicts(w, obj_in_have)
+ commands = self.add_commands(diff, obj_in_have)
+ return commands
+
+ def render_interface_defaults(self, config, intfs):
+ """Collect user-defined-default states for 'system default switchport'
+ configurations. These configurations determine default L2/L3 modes
+ and enabled/shutdown states. The default values for user-defined-default
+ configurations may be different for legacy platforms.
+ Notes:
+ - L3 enabled default state is False on N9K,N7K but True for N3K,N6K
+ - Changing L2-L3 modes may change the default enabled value.
+ - '(no) system default switchport shutdown' only applies to L2 interfaces.
+ Run through the gathered interfaces and tag their default enabled state.
+ """
+ intf_defs = {}
+ L3_enabled = (
+ True if re.search("N[356]K", self.get_platform()) else False
+ )
+ intf_defs = {
+ "sysdefs": {
+ "mode": None,
+ "L2_enabled": None,
+ "L3_enabled": L3_enabled,
+ }
+ }
+ pat = "(no )*system default switchport$"
+ m = re.search(pat, config, re.MULTILINE)
+ if m:
+ intf_defs["sysdefs"]["mode"] = (
+ "layer3" if "no " in m.groups() else "layer2"
+ )
+
+ pat = "(no )*system default switchport shutdown$"
+ m = re.search(pat, config, re.MULTILINE)
+ if m:
+ intf_defs["sysdefs"]["L2_enabled"] = (
+ True if "no " in m.groups() else False
+ )
+
+ for item in intfs:
+ intf_defs[item["name"]] = default_intf_enabled(
+ name=item["name"],
+ sysdefs=intf_defs["sysdefs"],
+ mode=item.get("mode"),
+ )
+
+ return intf_defs
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py
new file mode 100644
index 00000000..9c947ba1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,355 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_l2_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_diff,
+ to_list,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ normalize_interface,
+ search_obj_in_list,
+ vlan_range_to_list,
+)
+
+
+class L2_interfaces(ConfigBase):
+ """
+ The nxos_l2_interfaces class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["l2_interfaces"]
+
+ exclude_params = ["vlan", "allowed_vlans", "native_vlans"]
+
+ def __init__(self, module):
+ super(L2_interfaces, self).__init__(module)
+
+ def get_l2_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ l2_interfaces_facts = facts["ansible_network_resources"].get(
+ "l2_interfaces"
+ )
+ if not l2_interfaces_facts:
+ return []
+ return l2_interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_l2_interfaces_facts = self.get_l2_interfaces_facts()
+ else:
+ existing_l2_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_l2_interfaces_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_l2_interfaces_facts = self.get_l2_interfaces_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_l2_interfaces_facts(
+ data=running_config
+ )
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_l2_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_l2_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_l2_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_l2_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params.get("config")
+ want = []
+ if config:
+ for w in config:
+ w.update({"name": normalize_interface(w["name"])})
+ self.expand_trunk_allowed_vlans(w)
+ want.append(remove_empties(w))
+ have = existing_l2_interfaces_facts
+ for h in have:
+ self.expand_trunk_allowed_vlans(h)
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def expand_trunk_allowed_vlans(self, d):
+ if not d:
+ return None
+ if "trunk" in d and d["trunk"]:
+ if "allowed_vlans" in d["trunk"]:
+ allowed_vlans = vlan_range_to_list(d["trunk"]["allowed_vlans"])
+ vlans_list = [str(l) for l in sorted(allowed_vlans)]
+ d["trunk"]["allowed_vlans"] = ",".join(vlans_list)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ commands = list()
+ if state == "overridden":
+ commands.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(flatten_dict(w), have))
+ elif state == "replaced":
+ commands.extend(
+ self._state_replaced(flatten_dict(w), have)
+ )
+ return commands
+
+ def _state_replaced(self, w, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ obj_in_have = flatten_dict(search_obj_in_list(w["name"], have, "name"))
+ if obj_in_have:
+ diff = dict_diff(w, obj_in_have)
+ else:
+ diff = w
+ merged_commands = self.set_commands(w, have, True)
+ if "name" not in diff:
+ diff["name"] = w["name"]
+
+ dkeys = diff.keys()
+ for k in w.copy():
+ if k in self.exclude_params and k in dkeys:
+ del diff[k]
+ replaced_commands = self.del_attribs(diff)
+
+ if merged_commands or replaced_commands:
+ cmds = set(replaced_commands).intersection(set(merged_commands))
+ for cmd in cmds:
+ merged_commands.remove(cmd)
+ commands.extend(replaced_commands)
+ commands.extend(merged_commands)
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ for h in have:
+ h = flatten_dict(h)
+ obj_in_want = flatten_dict(
+ search_obj_in_list(h["name"], want, "name")
+ )
+ if h == obj_in_want:
+ continue
+ for w in want:
+ w = flatten_dict(w)
+ if h["name"] == w["name"]:
+ wkeys = w.keys()
+ hkeys = h.keys()
+ for k in wkeys:
+ if k in self.exclude_params and k in hkeys:
+ del h[k]
+ commands.extend(self.del_attribs(h))
+ for w in want:
+ commands.extend(self.set_commands(flatten_dict(w), have, True))
+ return commands
+
+ def _state_merged(self, w, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(w, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = flatten_dict(
+ search_obj_in_list(w["name"], have, "name")
+ )
+ commands.extend(self.del_attribs(obj_in_have))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.extend(self.del_attribs(flatten_dict(h)))
+ return commands
+
+ def del_attribs(self, obj):
+ commands = []
+ if not obj or len(obj.keys()) == 1:
+ return commands
+
+ cmd = "no switchport "
+ if "vlan" in obj:
+ commands.append(cmd + "access vlan")
+ if "mode" in obj:
+ commands.append(cmd + "mode")
+ if "allowed_vlans" in obj:
+ commands.append(cmd + "trunk allowed vlan")
+ if "native_vlan" in obj:
+ commands.append(cmd + "trunk native vlan")
+ if commands:
+ commands.insert(0, "interface " + obj["name"])
+ return commands
+
+ def diff_of_dicts(self, w, obj):
+ diff = set(w.items()) - set(obj.items())
+ diff = dict(diff)
+ if diff and w["name"] == obj["name"]:
+ diff.update({"name": w["name"]})
+ return diff
+
+ def add_commands(self, d, vlan_exists=False):
+ commands = []
+ if not d:
+ return commands
+
+ cmd = "switchport "
+ if "mode" in d:
+ commands.append(cmd + "mode {0}".format(d["mode"]))
+ if "vlan" in d:
+ commands.append(cmd + "access vlan " + str(d["vlan"]))
+ if "allowed_vlans" in d:
+ if vlan_exists:
+ commands.append(
+ cmd + "trunk allowed vlan add " + str(d["allowed_vlans"])
+ )
+ else:
+ commands.append(
+ cmd + "trunk allowed vlan " + str(d["allowed_vlans"])
+ )
+ if "native_vlan" in d:
+ commands.append(cmd + "trunk native vlan " + str(d["native_vlan"]))
+ if commands:
+ commands.insert(0, "interface " + d["name"])
+ return commands
+
+ def set_commands(self, w, have, replace=False):
+ commands = []
+
+ obj_in_have = flatten_dict(search_obj_in_list(w["name"], have, "name"))
+ if not obj_in_have:
+ commands = self.add_commands(w)
+ else:
+ diff = self.diff_of_dicts(w, obj_in_have)
+ if diff and not replace:
+ if "mode" in diff.keys() and diff["mode"]:
+ commands = self.add_commands(diff)
+ if "allowed_vlans" in diff.keys() and diff["allowed_vlans"]:
+ vlan_tobe_added = diff["allowed_vlans"].split(",")
+ vlan_list = vlan_tobe_added[:]
+ if obj_in_have.get("allowed_vlans"):
+ have_vlans = obj_in_have["allowed_vlans"].split(",")
+ else:
+ have_vlans = []
+ for w_vlans in vlan_list:
+ if w_vlans in have_vlans:
+ vlan_tobe_added.pop(vlan_tobe_added.index(w_vlans))
+ if vlan_tobe_added:
+ diff.update(
+ {"allowed_vlans": ",".join(vlan_tobe_added)}
+ )
+ if have_vlans:
+ commands = self.add_commands(diff, True)
+ else:
+ commands = self.add_commands(diff)
+ return commands
+ commands = self.add_commands(diff)
+ return commands
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py
new file mode 100644
index 00000000..18c46e50
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/l3_interfaces/l3_interfaces.py
@@ -0,0 +1,568 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_l3_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+
+from copy import deepcopy
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ normalize_interface,
+ search_obj_in_list,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ remove_rsvd_interfaces,
+ get_interface_type,
+)
+
+
+class L3_interfaces(ConfigBase):
+ """
+ The nxos_l3_interfaces class
+ """
+
+ gather_subset = ["min"]
+
+ gather_network_resources = ["l3_interfaces"]
+
+ exclude_params = []
+
+ def __init__(self, module):
+ super(L3_interfaces, self).__init__(module)
+
+ def get_l3_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ if self.state not in self.ACTION_STATES:
+ self.gather_subset = ["!all", "!min"]
+
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+
+ l3_interfaces_facts = facts["ansible_network_resources"].get(
+ "l3_interfaces"
+ )
+ self.platform = facts.get("ansible_net_platform", "")
+
+ return l3_interfaces_facts
+
+ def edit_config(self, commands):
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = []
+ warnings = []
+
+ if self.state in self.ACTION_STATES:
+ existing_l3_interfaces_facts = self.get_l3_interfaces_facts()
+ else:
+ existing_l3_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_l3_interfaces_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_l3_interfaces_facts = self.get_l3_interfaces_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_l3_interfaces_facts(
+ data=running_config
+ )
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_l3_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_l3_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_l3_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_l3_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params.get("config")
+ want = []
+ if config:
+ for w in config:
+ w.update({"name": normalize_interface(w["name"])})
+ want.append(remove_empties(w))
+ have = deepcopy(existing_l3_interfaces_facts)
+ self.init_check_existing(have)
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ commands = []
+ if state == "overridden":
+ commands.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(w, have))
+ elif state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+ Scope is limited to interface objects defined in the playbook.
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ cmds = []
+ name = want["name"]
+ obj_in_have = search_obj_in_list(want["name"], have, "name")
+
+ have_v4 = obj_in_have.pop("ipv4", []) if obj_in_have else []
+ have_v6 = obj_in_have.pop("ipv6", []) if obj_in_have else []
+
+ # Process lists of dicts separately
+ v4_cmds = self._v4_cmds(
+ want.pop("ipv4", []), have_v4, state="replaced"
+ )
+ v6_cmds = self._v6_cmds(
+ want.pop("ipv6", []), have_v6, state="replaced"
+ )
+
+ # Process remaining attrs
+ if obj_in_have:
+ # Find 'want' changes first
+ diff = self.diff_of_dicts(want, obj_in_have)
+ rmv = {"name": name}
+ haves_not_in_want = (
+ set(obj_in_have.keys()) - set(want.keys()) - set(diff.keys())
+ )
+ for i in haves_not_in_want:
+ rmv[i] = obj_in_have[i]
+ cmds.extend(self.generate_delete_commands(rmv))
+ else:
+ diff = want
+
+ cmds.extend(self.add_commands(diff, name=name))
+ cmds.extend(v4_cmds)
+ cmds.extend(v6_cmds)
+ self.cmd_order_fixup(cmds, name)
+ return cmds
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+ Scope includes all interface objects on the device.
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ # overridden behavior is the same as replaced except for scope.
+ cmds = []
+ for i in have:
+ obj_in_want = search_obj_in_list(i["name"], want, "name")
+ if obj_in_want:
+ if i != obj_in_want:
+ v4_cmds = self._v4_cmds(
+ obj_in_want.pop("ipv4", []),
+ i.pop("ipv4", []),
+ state="overridden",
+ )
+ replaced_cmds = self._state_replaced(obj_in_want, [i])
+ replaced_cmds.extend(v4_cmds)
+ self.cmd_order_fixup(replaced_cmds, obj_in_want["name"])
+ cmds.extend(replaced_cmds)
+ else:
+ deleted_cmds = self.generate_delete_commands(i)
+ self.cmd_order_fixup(deleted_cmds, i["name"])
+ cmds.extend(deleted_cmds)
+
+ for i in want:
+ if [item for item in have if i["name"] == item["name"]]:
+ continue
+ cmds.extend(self.add_commands(i, name=i["name"]))
+
+ return cmds
+
+ def _state_merged(self, w, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(w, have)
+
+ def _v4_cmds(self, want, have, state=None):
+ """Helper method for processing ipv4 changes.
+ This is needed to handle primary/secondary address changes, which require a specific sequence when changing.
+ """
+ # The ip address cli does not allow removing primary addresses while
+ # secondaries are present, but it does allow changing a primary to a
+ # new address as long as the address is not a current secondary.
+ # Be aware of scenarios where a secondary is taking over
+ # the role of the primary, which must be changed in sequence.
+ # In general, primaries/secondaries should change in this order:
+ # Step 1. Remove secondaries that are being changed or removed
+ # Step 2. Change the primary if needed
+ # Step 3. Merge secondaries
+
+ # Normalize inputs (add tag key if not present)
+ for i in want:
+ i["tag"] = i.get("tag")
+ for i in have:
+ i["tag"] = i.get("tag")
+
+ merged = True if state == "merged" else False
+ replaced = True if state == "replaced" else False
+ overridden = True if state == "overridden" else False
+
+ # Create secondary and primary wants/haves
+ sec_w = [i for i in want if i.get("secondary")]
+ sec_h = [i for i in have if i.get("secondary")]
+ pri_w = [i for i in want if not i.get("secondary")]
+ pri_h = [i for i in have if not i.get("secondary")]
+ pri_w = pri_w[0] if pri_w else {}
+ pri_h = pri_h[0] if pri_h else {}
+ cmds = []
+
+ # Remove all addrs when no primary is specified in want (pri_w)
+ if pri_h and not pri_w and (replaced or overridden):
+ cmds.append("no ip address")
+ return cmds
+
+ # 1. Determine which secondaries are changing and remove them. Need a have/want
+ # diff instead of want/have because a have sec addr may be changing to a pri.
+ sec_to_rmv = []
+ sec_diff = self.diff_list_of_dicts(sec_h, sec_w)
+ for i in sec_diff:
+ if overridden or [
+ w for w in sec_w if w["address"] == i["address"]
+ ]:
+ sec_to_rmv.append(i["address"])
+
+ # Check if new primary is currently a secondary
+ if pri_w and [h for h in sec_h if h["address"] == pri_w["address"]]:
+ if not overridden:
+ sec_to_rmv.append(pri_w["address"])
+
+ # Remove the changing secondaries
+ cmds.extend(["no ip address %s secondary" % i for i in sec_to_rmv])
+
+ # 2. change primary
+ if pri_w:
+ diff = dict(set(pri_w.items()) - set(pri_h.items()))
+ if diff:
+ addr = diff.get("address") or pri_w.get("address")
+ cmd = "ip address %s" % addr
+ tag = diff.get("tag")
+ cmd += " tag %s" % tag if tag else ""
+ cmds.append(cmd)
+
+ # 3. process remaining secondaries last
+ sec_w_to_chg = self.diff_list_of_dicts(sec_w, sec_h)
+ for i in sec_w_to_chg:
+ cmd = "ip address %s secondary" % i["address"]
+ cmd += " tag %s" % i["tag"] if i["tag"] else ""
+ cmds.append(cmd)
+
+ return cmds
+
+ def _v6_cmds(self, want, have, state=""):
+ """Helper method for processing ipv6 changes.
+ This is needed to avoid unnecessary churn on the device when removing or changing multiple addresses.
+ """
+ # Normalize inputs (add tag key if not present)
+ for i in want:
+ i["tag"] = i.get("tag")
+ for i in have:
+ i["tag"] = i.get("tag")
+
+ cmds = []
+ # items to remove (items in 'have' only)
+ if state == "replaced":
+ for i in self.diff_list_of_dicts(have, want):
+ want_addr = [w for w in want if w["address"] == i["address"]]
+ if not want_addr:
+ cmds.append("no ipv6 address %s" % i["address"])
+ elif i["tag"] and not want_addr[0]["tag"]:
+ # Must remove entire cli when removing tag
+ cmds.append("no ipv6 address %s" % i["address"])
+
+ # items to merge/add
+ for i in self.diff_list_of_dicts(want, have):
+ addr = i["address"]
+ tag = i["tag"]
+ if not tag and state == "merged":
+ # When want is IP-no-tag and have is IP+tag it will show up in diff,
+ # but for merged nothing has changed, so ignore it for idempotence.
+ have_addr = [h for h in have if h["address"] == addr]
+ if have_addr and have_addr[0].get("tag"):
+ continue
+ cmd = "ipv6 address %s" % i["address"]
+ cmd += " tag %s" % tag if tag else ""
+ cmds.append(cmd)
+
+ return cmds
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = search_obj_in_list(w["name"], have, "name")
+ commands.extend(self.del_all_attribs(obj_in_have))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.extend(self.del_all_attribs(h))
+ return commands
+
+ def del_all_attribs(self, obj):
+ commands = []
+ if not obj or len(obj.keys()) == 1:
+ return commands
+ commands = self.generate_delete_commands(obj)
+ self.cmd_order_fixup(commands, obj["name"])
+ return commands
+
+ def generate_delete_commands(self, obj):
+ """Generate CLI commands to remove non-default settings.
+ obj: dict of attrs to remove
+ """
+ commands = []
+ name = obj.get("name")
+ if "dot1q" in obj:
+ commands.append("no encapsulation dot1q")
+ if "redirects" in obj:
+ if not self.check_existing(name, "has_secondary") or re.match(
+ "N[3567]", self.platform
+ ):
+ # device auto-enables redirects when secondaries are removed;
+ # auto-enable may fail on legacy platforms so always do explicit enable
+ commands.append("ip redirects")
+ if "unreachables" in obj:
+ commands.append("no ip unreachables")
+ if "ipv4" in obj:
+ commands.append("no ip address")
+ if "ipv6" in obj:
+ commands.append("no ipv6 address")
+ if "evpn_multisite_tracking" in obj:
+ have = self.existing_facts.get(name, {})
+ if have.get("evpn_multisite_tracking", False) is not False:
+ cmd = "no evpn multisite %s" % have.get(
+ "evpn_multisite_tracking"
+ )
+ commands.append(cmd)
+ return commands
+
+ def init_check_existing(self, have):
+ """Creates a class var dict for easier access to existing states
+ """
+ self.existing_facts = dict()
+ have_copy = deepcopy(have)
+ for intf in have_copy:
+ name = intf["name"]
+ self.existing_facts[name] = intf
+ # Check for presence of secondaries; used for ip redirects logic
+ if [i for i in intf.get("ipv4", []) if i.get("secondary")]:
+ self.existing_facts[name]["has_secondary"] = True
+
+ def check_existing(self, name, query):
+ """Helper method to lookup existing states on an interface.
+ This is needed for attribute changes that have additional dependencies;
+ e.g. 'ip redirects' may auto-enable when all secondary ip addrs are removed.
+ """
+ if name:
+ have = self.existing_facts.get(name, {})
+ if "has_secondary" in query:
+ return have.get("has_secondary", False)
+ if "redirects" in query:
+ return have.get("redirects", True)
+ if "unreachables" in query:
+ return have.get("unreachables", False)
+ return None
+
+ def diff_of_dicts(self, w, obj):
+ diff = set(w.items()) - set(obj.items())
+ diff = dict(diff)
+ if diff and w["name"] == obj["name"]:
+ diff.update({"name": w["name"]})
+ return diff
+
+ def diff_list_of_dicts(self, w, h):
+ diff = []
+ set_w = set(tuple(sorted(d.items())) for d in w) if w else set()
+ set_h = set(tuple(sorted(d.items())) for d in h) if h else set()
+ difference = set_w.difference(set_h)
+ for element in difference:
+ diff.append(dict((x, y) for x, y in element))
+ return diff
+
+ def add_commands(self, diff, name=""):
+ commands = []
+ if not diff:
+ return commands
+ if "dot1q" in diff:
+ commands.append("encapsulation dot1q " + str(diff["dot1q"]))
+ if "redirects" in diff:
+ # Note: device will auto-disable redirects when secondaries are present
+ if diff["redirects"] != self.check_existing(name, "redirects"):
+ no_cmd = "no " if diff["redirects"] is False else ""
+ commands.append(no_cmd + "ip redirects")
+ self.cmd_order_fixup(commands, name)
+ if "unreachables" in diff:
+ if diff["unreachables"] != self.check_existing(
+ name, "unreachables"
+ ):
+ no_cmd = "no " if diff["unreachables"] is False else ""
+ commands.append(no_cmd + "ip unreachables")
+ if "evpn_multisite_tracking" in diff:
+ commands.append(
+ "evpn multisite " + str(diff["evpn_multisite_tracking"])
+ )
+ if "ipv4" in diff:
+ commands.extend(self.generate_afi_commands(diff["ipv4"]))
+ if "ipv6" in diff:
+ commands.extend(self.generate_afi_commands(diff["ipv6"]))
+ self.cmd_order_fixup(commands, name)
+
+ return commands
+
+ def generate_afi_commands(self, diff):
+ cmds = []
+ for i in diff:
+ cmd = (
+ "ipv6 address "
+ if re.search("::", i["address"])
+ else "ip address "
+ )
+ cmd += i["address"]
+ if i.get("secondary"):
+ cmd += " secondary"
+ if i.get("tag"):
+ cmd += " tag " + str(i["tag"])
+ cmds.append(cmd)
+ return cmds
+
+ def set_commands(self, w, have):
+ commands = []
+ name = w["name"]
+ obj_in_have = search_obj_in_list(name, have, "name")
+ if not obj_in_have:
+ commands = self.add_commands(w, name=name)
+ else:
+ # lists of dicts must be processed separately from non-list attrs
+ v4_cmds = self._v4_cmds(
+ w.pop("ipv4", []), obj_in_have.pop("ipv4", []), state="merged"
+ )
+ v6_cmds = self._v6_cmds(
+ w.pop("ipv6", []), obj_in_have.pop("ipv6", []), state="merged"
+ )
+
+ # diff remaining attrs
+ diff = self.diff_of_dicts(w, obj_in_have)
+ commands = self.add_commands(diff, name=name)
+ commands.extend(v4_cmds)
+ commands.extend(v6_cmds)
+
+ self.cmd_order_fixup(commands, name)
+ return commands
+
+ def cmd_order_fixup(self, cmds, name):
+ """Inserts 'interface <name>' config at the beginning of populated command list; reorders dependent commands that must process after others.
+ """
+ if cmds:
+ if name and not [
+ item for item in cmds if item.startswith("interface")
+ ]:
+ cmds.insert(0, "interface " + name)
+
+ redirects = [
+ item for item in cmds if re.match("(no )*ip redirects", item)
+ ]
+ if redirects:
+ # redirects should occur after ipv4 commands, just move to end of list
+ redirects = redirects.pop()
+ cmds.remove(redirects)
+ cmds.append(redirects)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/lacp.py
new file mode 100644
index 00000000..84ead279
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp/lacp.py
@@ -0,0 +1,234 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_lacp class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_diff,
+ to_list,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+
+
+class Lacp(ConfigBase):
+ """
+ The nxos_lacp class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["lacp"]
+
+ exclude_params = ["priority", "mac"]
+
+ def __init__(self, module):
+ super(Lacp, self).__init__(module)
+
+ def get_lacp_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ lacp_facts = facts["ansible_network_resources"].get("lacp", {})
+
+ return lacp_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lacp_facts = self.get_lacp_facts()
+ else:
+ existing_lacp_facts = {}
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lacp_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lacp_facts = self.get_lacp_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_lacp_facts(data=running_config)
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lacp_facts
+ if result["changed"]:
+ result["after"] = changed_lacp_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_lacp_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_lacp_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ want = remove_empties(self._module.params["config"])
+ have = existing_lacp_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if state in ("merged", "replaced", "rendered") and not want:
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ commands = list()
+
+ if state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ elif state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(want, have))
+ elif state == "replaced":
+ commands.extend(self._state_replaced(want, have))
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ diff = dict_diff(want, have)
+ wkeys = want.keys()
+ dkeys = diff.keys()
+ for k in wkeys:
+ if k in self.exclude_params and k in dkeys:
+ del diff[k]
+ deleted_commands = self.del_all(diff)
+ merged_commands = self._state_merged(want, have)
+
+ commands.extend(deleted_commands)
+ if merged_commands:
+ commands.extend(merged_commands)
+
+ return commands
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if not have:
+ return commands
+ commands.extend(self.del_all(have))
+ return commands
+
+ def get_diff(self, comparable, base):
+ diff = {}
+ if not base:
+ diff = comparable
+ else:
+ diff = dict_diff(base, comparable)
+ return diff
+
+ def del_all(self, diff):
+ commands = []
+ base = "no lacp system-"
+ diff = diff.get("system")
+ if diff:
+ if "priority" in diff:
+ commands.append(base + "priority")
+ if "mac" in diff:
+ commands.append(base + "mac")
+ return commands
+
+ def add_commands(self, diff):
+ commands = []
+ base = "lacp system-"
+ diff = diff.get("system")
+ if diff and "priority" in diff:
+ cmd = base + "priority" + " " + str(diff["priority"])
+ commands.append(cmd)
+ if diff and "mac" in diff:
+ cmd = ""
+ if "address" in diff["mac"]:
+ cmd += base + "mac" + " " + diff["mac"]["address"]
+ if "role" in diff["mac"]:
+ cmd += " " + "role" + " " + diff["mac"]["role"]
+ if cmd:
+ commands.append(cmd)
+
+ return commands
+
+ def set_commands(self, want, have):
+ if not want:
+ return []
+ diff = self.get_diff(want, have)
+ return self.add_commands(diff)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py
new file mode 100644
index 00000000..d2518c29
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py
@@ -0,0 +1,336 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_lacp_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ dict_diff,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ search_obj_in_list,
+ get_interface_type,
+ normalize_interface,
+)
+
+
+class Lacp_interfaces(ConfigBase):
+ """
+ The nxos_lacp_interfaces class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["lacp_interfaces"]
+
+ exclude_params = ["port_priority", "rate", "min", "max"]
+
+ def __init__(self, module):
+ super(Lacp_interfaces, self).__init__(module)
+
+ def get_lacp_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ lacp_interfaces_facts = facts["ansible_network_resources"].get(
+ "lacp_interfaces"
+ )
+ if not lacp_interfaces_facts:
+ return []
+ return lacp_interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
+ else:
+ existing_lacp_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lacp_interfaces_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_lacp_interfaces_facts(
+ data=running_config
+ )
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lacp_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_lacp_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_lacp_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_lacp_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params.get("config")
+ want = []
+ if config:
+ for w in config:
+ if get_interface_type(w["name"]) not in (
+ "portchannel",
+ "ethernet",
+ ):
+ self._module.fail_json(
+ msg="This module works with either portchannel or ethernet"
+ )
+ w.update({"name": normalize_interface(w["name"])})
+ want.append(remove_empties(w))
+ have = existing_lacp_interfaces_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+ commands = list()
+
+ if state == "overridden":
+ commands.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(flatten_dict(w), have))
+ elif state == "replaced":
+ commands.extend(
+ self._state_replaced(flatten_dict(w), have)
+ )
+ return commands
+
+ def _state_replaced(self, w, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ obj_in_have = flatten_dict(search_obj_in_list(w["name"], have, "name"))
+ diff = dict_diff(w, obj_in_have)
+ merged_commands = self.set_commands(w, have)
+ if "name" not in diff:
+ diff["name"] = w["name"]
+ wkeys = w.keys()
+ dkeys = diff.keys()
+ for k in wkeys:
+ if k in self.exclude_params and k in dkeys:
+ del diff[k]
+ replaced_commands = self.del_attribs(diff)
+
+ if merged_commands:
+ cmds = set(replaced_commands).intersection(set(merged_commands))
+ for cmd in cmds:
+ merged_commands.remove(cmd)
+ commands.extend(replaced_commands)
+ commands.extend(merged_commands)
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ for h in have:
+ h = flatten_dict(h)
+ obj_in_want = flatten_dict(
+ search_obj_in_list(h["name"], want, "name")
+ )
+ if h == obj_in_want:
+ continue
+ for w in want:
+ w = flatten_dict(w)
+ if h["name"] == w["name"]:
+ wkeys = w.keys()
+ hkeys = h.keys()
+ for k in wkeys:
+ if k in self.exclude_params and k in hkeys:
+ del h[k]
+ commands.extend(self.del_attribs(h))
+ for w in want:
+ commands.extend(self.set_commands(flatten_dict(w), have))
+ return commands
+
+ def _state_merged(self, w, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(w, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = flatten_dict(
+ search_obj_in_list(w["name"], have, "name")
+ )
+ commands.extend(self.del_attribs(obj_in_have))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.extend(self.del_attribs(flatten_dict(h)))
+ return commands
+
+ def del_attribs(self, obj):
+ commands = []
+ if not obj or len(obj.keys()) == 1:
+ return commands
+ commands.append("interface " + obj["name"])
+ if "graceful" in obj:
+ commands.append("lacp graceful-convergence")
+ if "vpc" in obj:
+ commands.append("no lacp vpn-convergence")
+ if "suspend_individual" in obj:
+ commands.append("lacp suspend_individual")
+ if "mode" in obj:
+ commands.append("no lacp mode " + obj["mode"])
+ if "max" in obj:
+ commands.append("no lacp max-bundle")
+ if "min" in obj:
+ commands.append("no lacp min-links")
+ if "port_priority" in obj:
+ commands.append("no lacp port-priority")
+ if "rate" in obj:
+ commands.append("no lacp rate")
+ return commands
+
+ def diff_of_dicts(self, w, obj):
+ diff = set(w.items()) - set(obj.items())
+ diff = dict(diff)
+ if diff and w["name"] == obj["name"]:
+ diff.update({"name": w["name"]})
+ return diff
+
+ def add_commands(self, d):
+ commands = []
+ if not d:
+ return commands
+ commands.append("interface" + " " + d["name"])
+
+ if "port_priority" in d:
+ commands.append("lacp port-priority " + str(d["port_priority"]))
+ if "rate" in d:
+ commands.append("lacp rate " + str(d["rate"]))
+ if "min" in d:
+ commands.append("lacp min-links " + str(d["min"]))
+ if "max" in d:
+ commands.append("lacp max-bundle " + str(d["max"]))
+ if "mode" in d:
+ commands.append("lacp mode " + d["mode"])
+ if "suspend_individual" in d:
+ if d["suspend_individual"] is True:
+ commands.append("lacp suspend-individual")
+ else:
+ commands.append("no lacp suspend-individual")
+ if "graceful" in d:
+ if d["graceful"] is True:
+ commands.append("lacp graceful-convergence")
+ else:
+ commands.append("no lacp graceful-convergence")
+ if "vpc" in d:
+ if d["vpc"] is True:
+ commands.append("lacp vpc-convergence")
+ else:
+ commands.append("no lacp vpc-convergence")
+ return commands
+
+ def set_commands(self, w, have):
+ commands = []
+ obj_in_have = flatten_dict(search_obj_in_list(w["name"], have, "name"))
+ if not obj_in_have:
+ commands = self.add_commands(w)
+ else:
+ diff = self.diff_of_dicts(w, obj_in_have)
+ commands = self.add_commands(diff)
+ return commands
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py
new file mode 100644
index 00000000..c0808113
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lag_interfaces/lag_interfaces.py
@@ -0,0 +1,335 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_lag_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+ dict_diff,
+ search_obj_in_list,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ normalize_interface,
+)
+
+
+class Lag_interfaces(ConfigBase):
+ """
+ The nxos_lag_interfaces class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["lag_interfaces"]
+
+ def __init__(self, module):
+ super(Lag_interfaces, self).__init__(module)
+
+ def get_lag_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ lag_interfaces_facts = facts["ansible_network_resources"].get(
+ "lag_interfaces"
+ )
+ if not lag_interfaces_facts:
+ return []
+ return lag_interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
+ else:
+ existing_lag_interfaces_facts = []
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lag_interfaces_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ resp = self._connection.edit_config(commands)
+ if "response" in resp:
+ for item in resp["response"]:
+ if item:
+ err_str = item
+ if err_str.lower().startswith("cannot add"):
+ self._module.fail_json(msg=err_str)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_lag_interfaces_facts(
+ data=running_config
+ )
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lag_interfaces_facts
+ if result["changed"]:
+ result["after"] = changed_lag_interfaces_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_lag_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_lag_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ want = self._module.params.get("config")
+ if want:
+ for w in want:
+ w.update(remove_empties(w))
+ if "members" in w and w["members"]:
+ for item in w["members"]:
+ item.update(
+ {"member": normalize_interface(item["member"])}
+ )
+ have = existing_lag_interfaces_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+ commands = list()
+
+ if state == "overridden":
+ commands.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(w, have))
+ if state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_replaced(self, w, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ merged_commands = self.set_commands(w, have)
+ replaced_commands = self.del_intf_commands(w, have)
+ if merged_commands:
+ commands.extend(replaced_commands)
+ commands.extend(merged_commands)
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ for h in have:
+ obj_in_want = search_obj_in_list(h["name"], want, "name")
+ if obj_in_want:
+ diff = self.diff_list_of_dicts(
+ h["members"], obj_in_want["members"]
+ )
+ if not diff:
+ continue
+ commands.extend(self.del_all_commands(h))
+ for w in want:
+ commands.extend(self.set_commands(w, have))
+ return commands
+
+ def _state_merged(self, w, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(w, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = search_obj_in_list(w["name"], have, "name")
+ commands.extend(self.del_all_commands(obj_in_have))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.extend(self.del_all_commands(h))
+ return commands
+
+ def diff_list_of_dicts(self, want, have):
+ if not want:
+ want = []
+
+ if not have:
+ have = []
+
+ diff = []
+ for w_item in want:
+ h_item = (
+ search_obj_in_list(w_item["member"], have, key="member") or {}
+ )
+ delta = dict_diff(h_item, w_item)
+ if delta:
+ if h_item:
+ if (
+ "mode" in delta.keys()
+ and delta["mode"] == "on"
+ and "mode" not in h_item.keys()
+ ):
+ # mode = on will not be displayed in running-config
+ continue
+ if "member" not in delta.keys():
+ delta["member"] = w_item["member"]
+ diff.append(delta)
+
+ return diff
+
+ def intersect_list_of_dicts(self, w, h):
+ intersect = []
+ wmem = []
+ hmem = []
+ for d in w:
+ wmem.append({"member": d["member"]})
+ for d in h:
+ hmem.append({"member": d["member"]})
+ set_w = set(tuple(sorted(d.items())) for d in wmem)
+ set_h = set(tuple(sorted(d.items())) for d in hmem)
+ intersection = set_w.intersection(set_h)
+ for element in intersection:
+ intersect.append(dict((x, y) for x, y in element))
+ return intersect
+
+ def add_commands(self, diff, name):
+ commands = []
+ name = name.strip("port-channel")
+ for d in diff:
+ commands.append("interface" + " " + d["member"])
+ cmd = ""
+ group_cmd = "channel-group {0}".format(name)
+ if d.get("force"):
+ cmd = group_cmd + " force "
+ if "mode" in d:
+ if cmd:
+ cmd = cmd + " mode " + d["mode"]
+ else:
+ cmd = group_cmd + " mode " + d["mode"]
+ if not cmd:
+ cmd = group_cmd
+ commands.append(cmd)
+ return commands
+
+ def set_commands(self, w, have):
+ commands = []
+ obj_in_have = search_obj_in_list(w["name"], have, "name")
+ if not obj_in_have:
+ commands = self.add_commands(w["members"], w["name"])
+ else:
+ if "members" not in obj_in_have:
+ obj_in_have["members"] = None
+ diff = self.diff_list_of_dicts(
+ w["members"], obj_in_have["members"]
+ )
+ commands = self.add_commands(diff, w["name"])
+ return commands
+
+ def del_all_commands(self, obj_in_have):
+ commands = []
+ if not obj_in_have:
+ return commands
+ for m in obj_in_have.get("members", []):
+ commands.append("interface" + " " + m["member"])
+ commands.append("no channel-group")
+ return commands
+
+ def del_intf_commands(self, w, have):
+ commands = []
+ obj_in_have = search_obj_in_list(w["name"], have, "name")
+ if obj_in_have:
+ lst_to_del = self.intersect_list_of_dicts(
+ w["members"], obj_in_have["members"]
+ )
+ if lst_to_del:
+ for item in lst_to_del:
+ commands.append("interface" + " " + item["member"])
+ commands.append("no channel-group")
+ return commands
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/lldp_global.py
new file mode 100644
index 00000000..95b5f531
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_global/lldp_global.py
@@ -0,0 +1,285 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_lldp_global class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_empties,
+ dict_diff,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+
+
+class Lldp_global(ConfigBase):
+ """
+ The nxos_lldp_global class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["lldp_global"]
+
+ def __init__(self, module):
+ super(Lldp_global, self).__init__(module)
+
+ def get_lldp_global_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ lldp_global_facts = facts["ansible_network_resources"].get(
+ "lldp_global"
+ )
+ if not lldp_global_facts:
+ return {}
+ return lldp_global_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+
+ if self.state in self.ACTION_STATES:
+ existing_lldp_global_facts = self.get_lldp_global_facts()
+ else:
+ existing_lldp_global_facts = {}
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_lldp_global_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_lldp_global_facts = self.get_lldp_global_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_lldp_global_facts(data=running_config)
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_lldp_global_facts
+ if result["changed"]:
+ result["after"] = changed_lldp_global_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_lldp_global_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_lldp_global_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ want = self._module.params["config"]
+ have = existing_lldp_global_facts
+ resp = self.set_state(remove_empties(want), have)
+ return resp
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if state in ("merged", "replaced", "rendered") and not want:
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+ commands = list()
+
+ if state == "deleted":
+ commands = self._state_deleted(have)
+ elif state in ["merged", "rendered"]:
+ commands = self._state_merged(want, have)
+ elif state == "replaced":
+ commands = self._state_replaced(want, have)
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ merge_dict = dict_diff(have, want)
+ # merge_dict will contain new and unique values in want
+ delete_dict = self.find_delete_params(have, want)
+ self._module.params["state"] = "deleted"
+ commands.extend(self._state_deleted(delete_dict)) # delete
+ self._module.params["state"] = "merged"
+ commands.extend(self.set_commands(merge_dict)) # merge
+ self._module.params["state"] = "replaced"
+ return commands
+
+ def delete_nested_dict(self, have, want):
+ """
+ Returns tlv_select nested dict that needs to be defaulted
+ """
+ outer_dict = {}
+ for key, val in have.items():
+ inner_dict = {}
+ if not isinstance(val, dict):
+ if key not in want.keys():
+ inner_dict.update({key: val})
+ return inner_dict
+ else:
+ if key in want.keys():
+ outer_dict.update(
+ {key: self.delete_nested_dict(val, want[key])}
+ )
+ else:
+ outer_dict.update({key: val})
+ return outer_dict
+
+ def find_delete_params(self, have, want):
+ """
+ Returns parameters that are present in have and not in want, that need to be defaulted
+ """
+ delete_dict = {}
+ for key, val in have.items():
+ if key not in want.keys():
+ delete_dict.update({key: val})
+ else:
+ if key == "tlv_select":
+ delete_dict.update(
+ {
+ key: self.delete_nested_dict(
+ have["tlv_select"], want["tlv_select"]
+ )
+ }
+ )
+ return delete_dict
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = []
+ diff = dict_diff(have, want)
+ commands.extend(self.set_commands(diff))
+ return commands
+
+ def _state_deleted(self, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if have:
+ for key, val in have.items():
+ if "tlv_select" in key:
+ commands.extend(self.process_nested_dict(val))
+ else:
+ if key == "port_id":
+ key = "portid-subtype"
+ commands.append("no lldp " + key + " " + str(val))
+
+ return commands
+
+ def set_commands(self, diff):
+ commands = []
+ for key, val in diff.items():
+ commands.extend(self.add_commands(key, val))
+ return commands
+
+ def add_commands(self, key, val):
+ command = []
+ if "port_id" in key:
+ command.append("lldp portid-subtype " + str(val))
+ elif "tlv_select" in key:
+ command.extend(self.process_nested_dict(val))
+ else:
+ if val:
+ command.append("lldp " + key + " " + str(val))
+ return command
+
+ def process_nested_dict(self, val):
+ nested_commands = []
+ for k, v in val.items():
+ if isinstance(v, dict):
+ for k1, v1 in v.items():
+ com1 = "lldp tlv-select "
+ com2 = ""
+ if "system" in k:
+ com2 = "system-" + k1
+ elif "management_address" in k:
+ com2 = "management-address " + k1
+ elif "port" in k:
+ com2 = "port-" + k1
+
+ com1 += com2
+ com1 = self.negate_command(com1, v1)
+ nested_commands.append(com1)
+ else:
+ com1 = "lldp tlv-select "
+ if "power_management" in k:
+ com1 += "power-management"
+ else:
+ com1 += k
+
+ com1 = self.negate_command(com1, v)
+ nested_commands.append(com1)
+ return nested_commands
+
+ def negate_command(self, command, val):
+ # for merged, replaced vals need to be checked to add 'no'
+ if self._module.params["state"] == "merged":
+ if not val:
+ command = "no " + command
+ return command
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py
new file mode 100644
index 00000000..7bc2db0c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/lldp_interfaces/lldp_interfaces.py
@@ -0,0 +1,325 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_lldp_interfaces class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+ dict_diff,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ search_obj_in_list,
+ get_interface_type,
+ normalize_interface,
+)
+
+
+class Lldp_interfaces(ConfigBase):
+ """
+ The nxos_lldp_interfaces class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["lldp_interfaces"]
+
+ def __init__(self, module):
+ super(Lldp_interfaces, self).__init__(module)
+
+ def get_lldp_interfaces_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ lldp_interfaces_facts = facts["ansible_network_resources"].get(
+ "lldp_interfaces"
+ )
+ if not lldp_interfaces_facts:
+ return []
+ return lldp_interfaces_facts
+
+ def edit_config(self, commands):
+ """Wrapper method for `_connection.edit_config()`
+ This exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+ state = self._module.params["state"]
+ action_states = ["merged", "replaced", "deleted", "overridden"]
+
+ if state == "gathered":
+ result["gathered"] = self.get_lldp_interfaces_facts()
+ elif state == "rendered":
+ result["rendered"] = self.set_config({})
+ elif state == "parsed":
+ result["parsed"] = self.set_config({})
+ else:
+ existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
+ commands.extend(self.set_config(existing_lldp_interfaces_facts))
+ if commands and state in action_states:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+ result["before"] = existing_lldp_interfaces_facts
+ result["commands"] = commands
+ result["commands"] = commands
+
+ changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
+
+ if result["changed"]:
+ result["after"] = changed_lldp_interfaces_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_lldp_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params["config"]
+ want = []
+ if config:
+ for w in config:
+ if get_interface_type(w["name"]) not in (
+ "management",
+ "ethernet",
+ ):
+ self._module.fail_json(
+ msg="This module works with either management or ethernet"
+ )
+ w.update({"name": normalize_interface(w["name"])})
+ want.append(remove_empties(w))
+ have = existing_lldp_interfaces_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ state = self._module.params["state"]
+ if state == "overridden":
+ commands = self._state_overridden(want, have)
+ elif state == "deleted":
+ commands = self._state_deleted(want, have)
+ elif state == "rendered":
+ commands = self._state_rendered(want)
+ elif state == "parsed":
+ want = self._module.params["running_config"]
+ commands = self._state_parsed(want)
+ else:
+ for w in want:
+ if state == "merged":
+ commands.extend(self._state_merged(flatten_dict(w), have))
+ elif state == "replaced":
+ commands.extend(
+ self._state_replaced(flatten_dict(w), have)
+ )
+ return commands
+
+ def _state_parsed(self, want):
+ return self.get_lldp_interfaces_facts(want)
+
+ def _state_rendered(self, want):
+ commands = []
+ for w in want:
+ commands.extend(self.set_commands(w, {}))
+ return commands
+
+ def _state_gathered(self, have):
+ """ The command generator when state is gathered
+
+ :rtype: A list
+ :returns: the commands necessary to reproduce the current configuration
+ """
+ commands = []
+ want = {}
+ commands.append(self.set_commands(want, have))
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ del_commands = []
+ delete_dict = {}
+ obj_in_have = flatten_dict(
+ search_obj_in_list(want["name"], have, "name")
+ )
+ for k1 in obj_in_have.keys():
+ if k1 not in want.keys():
+ delete_dict.update({k1: obj_in_have[k1]})
+
+ if delete_dict:
+ delete_dict.update({"name": want["name"]})
+ del_commands = self.del_commands(delete_dict)
+ merged_commands = self.set_commands(want, have)
+
+ if merged_commands:
+ cmds = set(del_commands).intersection(set(merged_commands))
+ for cmd in cmds:
+ merged_commands.remove(cmd)
+
+ commands.extend(del_commands)
+ commands.extend(merged_commands)
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ want_intfs = [w["name"] for w in want]
+ for h in have:
+ h = flatten_dict(h)
+ delete_dict = {}
+ if h["name"] in want_intfs:
+ for w in want:
+ if w["name"] == h["name"]:
+ delete_keys = list(set(h) - set(flatten_dict(w)))
+ for k in delete_keys:
+ delete_dict.update({k: h[k]})
+ delete_dict.update({"name": h["name"]})
+ break
+ else:
+ delete_dict.update(h)
+ commands.extend(self.del_commands(delete_dict))
+ for w in want:
+ commands.extend(self.set_commands(flatten_dict(w), have))
+ return commands
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = flatten_dict(
+ search_obj_in_list(w["name"], have, "name")
+ )
+ commands.extend(self.del_commands(obj_in_have))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.extend(self.del_commands(flatten_dict(h)))
+ return commands
+
+ def set_commands(self, want, have):
+ commands = []
+ obj_in_have = flatten_dict(
+ search_obj_in_list(want["name"], have, "name")
+ )
+ if not obj_in_have:
+ commands = self.add_commands(flatten_dict(want))
+ else:
+ diff = dict_diff(obj_in_have, want)
+ if diff:
+ diff.update({"name": want["name"]})
+ commands = self.add_commands(diff)
+ return commands
+
+ def add_commands(self, d):
+ commands = []
+ if not d:
+ return commands
+ commands.append("interface " + d["name"])
+ if "transmit" in d:
+ if d["transmit"]:
+ commands.append("lldp transmit")
+ else:
+ commands.append("no lldp transmit")
+ if "receive" in d:
+ if d["receive"]:
+ commands.append("lldp receive")
+ else:
+ commands.append("no lldp receive")
+ if "management_address" in d:
+ commands.append(
+ "lldp tlv-set management-address " + d["management_address"]
+ )
+ if "vlan" in d:
+ commands.append("lldp tlv-set vlan " + str(d["vlan"]))
+
+ return commands
+
+ def del_commands(self, obj):
+ commands = []
+ if not obj or len(obj.keys()) == 1:
+ return commands
+ commands.append("interface " + obj["name"])
+ if "transmit" in obj:
+ commands.append("lldp transmit")
+ if "receive" in obj:
+ commands.append("lldp receive")
+ if "management_address" in obj:
+ commands.append(
+ "no lldp tlv-set management-address "
+ + obj["management_address"]
+ )
+ if "vlan" in obj:
+ commands.append("no lldp tlv-set vlan " + str(obj["vlan"]))
+
+ return commands
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/ospf_interfaces.py
new file mode 100644
index 00000000..10fd6a54
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospf_interfaces/ospf_interfaces.py
@@ -0,0 +1,206 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+"""
+The nxos_ospf_interfaces config file.
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to its desired end-state is
+created.
+"""
+
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_merge,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import (
+ ResourceModule,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.ospf_interfaces import (
+ Ospf_interfacesTemplate,
+)
+
+
+class Ospf_interfaces(ResourceModule):
+ """
+ The nxos_ospf_interfaces config class
+ """
+
+ def __init__(self, module):
+ super(Ospf_interfaces, self).__init__(
+ empty_fact_val=[],
+ facts_module=Facts(module),
+ module=module,
+ resource="ospf_interfaces",
+ tmplt=Ospf_interfacesTemplate(),
+ )
+ self.parsers = [
+ "authentication",
+ "authentication_key",
+ "message_digest_key",
+ "cost",
+ "dead_interval",
+ "hello_interval",
+ "instance",
+ "mtu_ignore",
+ "network",
+ "passive_interface",
+ "priority",
+ "retransmit_interval",
+ "shutdown",
+ "transmit_delay",
+ ]
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ if self.state not in ["parsed", "gathered"]:
+ self.generate_commands()
+ self.run_commands()
+ return self.result
+
+ def generate_commands(self):
+ """ Generate configuration commands to send based on
+ want, have and desired state.
+ """
+ wantd = {entry["name"]: entry for entry in self.want}
+ haved = {entry["name"]: entry for entry in self.have}
+
+ # turn all lists of dicts into dicts prior to merge
+ for entry in wantd, haved:
+ self._list_to_dict(entry)
+
+ # if state is merged, merge want onto have and then compare
+ if self.state == "merged":
+ wantd = dict_merge(haved, wantd)
+
+ # if state is deleted, empty out wantd and set haved to wantd
+ if self.state == "deleted":
+ haved = {
+ k: v for k, v in iteritems(haved) if k in wantd or not wantd
+ }
+ wantd = {}
+
+ # remove superfluous config for overridden and deleted
+ if self.state in ["overridden", "deleted"]:
+ for k, have in iteritems(haved):
+ if k not in wantd:
+ self._compare(want={}, have=have)
+
+ for k, want in iteritems(wantd):
+ self._compare(want=want, have=haved.pop(k, {}))
+
+ def _compare(self, want, have):
+ """Leverages the base class `compare()` method and
+ populates the list of commands to be run by comparing
+ the `want` and `have` data with the `parsers` defined
+ for the Ospf_interfaces network resource.
+ """
+ begin = len(self.commands)
+ self._compare_ospf_interfaces(want, have)
+ if len(self.commands) != begin:
+ self.commands.insert(
+ begin, self._tmplt.render(want or have, "interface", False)
+ )
+
+ def _compare_ospf_interfaces(self, want, have):
+ waf = want.get("address_family", {})
+ haf = have.get("address_family", {})
+
+ for afi in ("ipv4", "ipv6"):
+ witem = waf.pop(afi, {})
+ hitem = haf.pop(afi, {})
+
+ # this key needs to be compared separately and
+ # popped from `authentication` dict to
+ # preserve idempotence for other keys in this dict
+ self.compare(["authentication.key_chain"], want=witem, have=hitem)
+ witem.get("authentication", {}).pop("key_chain", None)
+ hitem.get("authentication", {}).pop("key_chain", None)
+
+ self.compare(parsers=self.parsers, want=witem, have=hitem)
+
+ # compare top-level `multi_areas` config
+ for area in witem.get("multi_areas", []):
+ if area not in hitem.get("multi_areas", []):
+ self.addcmd(
+ {"afi": afi, "area": area}, "multi_areas", negate=False
+ )
+ # remove superfluous top-level `multi_areas` config
+ for area in hitem.get("multi_areas", []):
+ if area not in witem.get("multi_areas", []):
+ self.addcmd(
+ {"afi": afi, "area": area}, "multi_areas", negate=True
+ )
+
+ # compare config->address_family->processes
+ self._compare_processes(
+ afi, witem.get("processes", {}), hitem.get("processes", {})
+ )
+
+ def _compare_processes(self, afi, want, have):
+ # add and update config->address_family->processes
+
+ for w_id, wproc in want.items():
+ hproc = have.pop(w_id, {})
+ hproc["afi"] = wproc["afi"] = afi
+
+ # compare config->address_family->processes->area
+ self.compare(["area"], wproc, hproc)
+
+ # compare config->address_family->processes->multi_areas
+ marea_dict = {"afi": afi, "process_id": wproc["process_id"]}
+ for area in wproc.get("multi_areas", []):
+ if area not in hproc.get("multi_areas", []):
+ marea_dict["area"] = area
+ self.addcmd(
+ marea_dict, "processes_multi_areas", negate=False
+ )
+ # remove superfluous processes->multi_areas config
+ for area in hproc.get("multi_areas", []):
+ if area not in wproc.get("multi_areas", []):
+ marea_dict["area"] = area
+ self.addcmd(
+ marea_dict, "processes_multi_areas", negate=True
+ )
+
+ # remove superflous config->address_family->processes
+ for hproc in have.values():
+ hproc["afi"] = afi
+
+ # remove config->address_family->processes->area
+ self.addcmd(hproc, "area", negate=True)
+
+ # remove superfluous processes->multi_areas config
+ marea_dict = {"afi": afi, "process_id": hproc["process_id"]}
+ for area in hproc.get("multi_areas", []):
+ marea_dict["area"] = area
+ self.addcmd(marea_dict, "processes_multi_areas", negate=True)
+
+ def _list_to_dict(self, entry):
+ for item in entry.values():
+ for ag in item.get("address_family", []):
+ ag["processes"] = {
+ subentry["process_id"]: subentry
+ for subentry in ag.get("processes", [])
+ }
+ item["address_family"] = {
+ subentry["afi"]: subentry
+ for subentry in item.get("address_family", [])
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/ospfv2.py
new file mode 100644
index 00000000..6338317e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv2/ospfv2.py
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# 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
+
+"""
+The nxos_ospfv2 class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from copy import deepcopy
+from ansible.module_utils.six import iteritems
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.ospfv2 import (
+ Ospfv2Template,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_merge,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import (
+ ResourceModule,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ get_from_dict,
+)
+
+
+class Ospfv2(ResourceModule):
+ """
+ The nxos_ospfv2 class
+ """
+
+ def __init__(self, module):
+ super(Ospfv2, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="ospfv2",
+ tmplt=Ospfv2Template(),
+ )
+ self.parsers = [
+ "router_id",
+ "auto_cost",
+ "graceful_restart.set",
+ "graceful_restart.helper_disable",
+ "isolate",
+ "log_adjacency_changes",
+ "max_lsa",
+ "mpls.traffic_eng.router_id",
+ "mpls.traffic_eng.multicast_intact",
+ "name_lookup",
+ "passive_interface.default",
+ "rfc1583compatibility",
+ "shutdown",
+ "default_information.originate",
+ "default_metric",
+ "distance",
+ "table_map",
+ "timers.lsa_arrival",
+ "timers.lsa_group_pacing",
+ "timers.throttle.lsa",
+ "timers.throttle.spf",
+ "maximum_paths",
+ "max_metric",
+ "down_bit_ignore",
+ "capability.vrf_lite",
+ "bfd",
+ ]
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ if self.state not in ["parsed", "gathered"]:
+ self.gen_config()
+ self.run_commands()
+ return self.result
+
+ def gen_config(self):
+ """ Select the appropriate function based on the state provided
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ wantd = {
+ (entry["process_id"]): entry
+ for entry in self.want.get("processes", [])
+ }
+ haved = {
+ (entry["process_id"]): entry
+ for entry in self.have.get("processes", [])
+ }
+
+ # turn all lists of dicts into dicts prior to merge
+ for entry in wantd, haved:
+ self._ospf_list_to_dict(entry)
+
+ # if state is merged, merge want onto have and then compare
+ if self.state == "merged":
+ wantd = dict_merge(haved, wantd)
+
+ # if state is deleted, empty out wantd and set haved to wantd
+ if self.state == "deleted":
+ haved = {
+ k: v for k, v in iteritems(haved) if k in wantd or not wantd
+ }
+ wantd = {}
+
+ # if state is overridden, first remove processes that are in have but not in want
+ if self.state in ["overridden", "deleted"]:
+ for k, have in iteritems(haved):
+ if k not in wantd:
+ self.addcmd(have, "process_id", True)
+
+ for k, want in iteritems(wantd):
+ self._compare(want=want, have=haved.pop(k, {}))
+
+ def _compare(self, want, have):
+ begin = len(self.commands)
+ self.compare(self.parsers, want=want, have=have)
+ self._compare_lists(want=want, have=have)
+ self._areas_compare(want=want, have=have)
+ self._vrfs_compare(want=want, have=have)
+
+ if len(self.commands) != begin or (not have and want):
+ self.commands.insert(
+ begin,
+ self._tmplt.render(
+ want or have,
+ "vrf"
+ if "vrf" in (want.keys() or have.keys())
+ else "process_id",
+ False,
+ ),
+ )
+
+ def _areas_compare(self, want, have):
+ wareas = want.get("areas", {})
+ hareas = have.get("areas", {})
+ for name, entry in iteritems(wareas):
+ self._area_compare(want=entry, have=hareas.pop(name, {}))
+ for name, entry in iteritems(hareas):
+ self._area_compare(want={}, have=entry)
+
+ def _area_compare(self, want, have):
+ parsers = [
+ "area.default_cost",
+ "area.authentication",
+ "area.nssa",
+ "area.nssa.translate",
+ "area.stub",
+ ]
+ self.compare(parsers=parsers, want=want, have=have)
+ self._area_compare_lists(want=want, have=have)
+
+ def _area_compare_lists(self, want, have):
+ for attrib in ["filter_list", "ranges"]:
+ wdict = want.get(attrib, {})
+ hdict = have.get(attrib, {})
+ for key, entry in iteritems(wdict):
+ if entry != hdict.pop(key, {}):
+ entry["area_id"] = want["area_id"]
+ self.addcmd(entry, "area.{0}".format(attrib), False)
+ # remove remaining items in have for replaced
+ for entry in hdict.values():
+ entry["area_id"] = have["area_id"]
+ self.addcmd(entry, "area.{0}".format(attrib), True)
+
+ def _compare_lists(self, want, have):
+ for attrib in [
+ "summary_address",
+ "redistribute",
+ "mpls.traffic_eng.areas",
+ ]:
+ wdict = get_from_dict(want, attrib) or {}
+ hdict = get_from_dict(have, attrib) or {}
+
+ for key, entry in iteritems(wdict):
+ if entry != hdict.pop(key, {}):
+ self.addcmd(entry, attrib, False)
+ # remove remaining items in have for replaced
+ for entry in hdict.values():
+ self.addcmd(entry, attrib, True)
+
+ def _vrfs_compare(self, want, have):
+ wvrfs = want.get("vrfs", {})
+ hvrfs = have.get("vrfs", {})
+ for name, entry in iteritems(wvrfs):
+ self._compare(want=entry, have=hvrfs.pop(name, {}))
+ # remove remaining items in have for replaced
+ for name, entry in iteritems(hvrfs):
+ self.addcmd(entry, "vrf", True)
+
+ def _ospf_list_to_dict(self, entry):
+ for _pid, proc in iteritems(entry):
+ for area in proc.get("areas", []):
+ area["ranges"] = {
+ entry["prefix"]: entry for entry in area.get("ranges", [])
+ }
+ area["filter_list"] = {
+ entry["direction"]: entry
+ for entry in area.get("filter_list", [])
+ }
+ mpls_areas = {
+ entry["area_id"]: entry
+ for entry in proc.get("mpls", {})
+ .get("traffic_eng", {})
+ .get("areas", [])
+ }
+ if mpls_areas:
+ proc["mpls"]["traffic_eng"]["areas"] = mpls_areas
+ proc["areas"] = {
+ entry["area_id"]: entry for entry in proc.get("areas", [])
+ }
+ proc["summary_address"] = {
+ entry["prefix"]: entry
+ for entry in proc.get("summary_address", [])
+ }
+ proc["redistribute"] = {
+ (entry.get("id"), entry["protocol"]): entry
+ for entry in proc.get("redistribute", [])
+ }
+ if "vrfs" in proc:
+ proc["vrfs"] = {
+ entry["vrf"]: entry for entry in proc.get("vrfs", [])
+ }
+ self._ospf_list_to_dict(proc["vrfs"])
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/ospfv3.py
new file mode 100644
index 00000000..648332b9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/ospfv3/ospfv3.py
@@ -0,0 +1,253 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+"""
+The nxos_ospfv3 config file.
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to its desired end-state is
+created.
+"""
+
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_merge,
+ get_from_dict,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import (
+ ResourceModule,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.ospfv3 import (
+ Ospfv3Template,
+)
+
+
+class Ospfv3(ResourceModule):
+ """
+ The nxos_ospfv3 config class
+ """
+
+ def __init__(self, module):
+ super(Ospfv3, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="ospfv3",
+ tmplt=Ospfv3Template(),
+ )
+ self.parsers = [
+ "auto_cost",
+ "flush_routes",
+ "graceful_restart.set",
+ "graceful_restart.helper_disable",
+ "graceful_restart.grace_period",
+ "graceful_restart.planned_only",
+ "isolate",
+ "log_adjacency_changes",
+ "max_lsa",
+ "max_metric",
+ "name_lookup",
+ "passive_interface.default",
+ "router_id",
+ "shutdown",
+ "timers.lsa_arrival",
+ "timers.lsa_group_pacing",
+ "timers.throttle.lsa",
+ ]
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ if self.state not in ["parsed", "gathered"]:
+ self.generate_commands()
+ self.run_commands()
+ return self.result
+
+ def generate_commands(self):
+ """ Generate configuration commands to send based on
+ want, have and desired state.
+ """
+ wantd = {
+ (entry["process_id"]): entry
+ for entry in self.want.get("processes", [])
+ }
+ haved = {
+ (entry["process_id"]): entry
+ for entry in self.have.get("processes", [])
+ }
+
+ # turn all lists of dicts into dicts prior to merge
+ for entry in wantd, haved:
+ self._ospfv3_list_to_dict(entry)
+
+ # if state is merged, merge want onto have and then compare
+ if self.state == "merged":
+ wantd = dict_merge(haved, wantd)
+
+ # if state is deleted, empty out wantd and set haved to wantd
+ if self.state == "deleted":
+ haved = {
+ k: v for k, v in iteritems(haved) if k in wantd or not wantd
+ }
+ wantd = {}
+
+ # if state is overridden, first remove processes that are in have but not in want
+ if self.state in ["overridden", "deleted"]:
+ for k, have in iteritems(haved):
+ if k not in wantd:
+ self.addcmd(have, "process_id", True)
+
+ for k, want in iteritems(wantd):
+ self._compare(want=want, have=haved.pop(k, {}))
+
+ def _compare(self, want, have):
+ """Leverages the base class `compare()` method and
+ populates the list of commands to be run by comparing
+ the `want` and `have` data with the `parsers` defined
+ for the Ospfv3 network resource.
+ """
+ begin = len(self.commands)
+ self.compare(parsers=self.parsers, want=want, have=have)
+ self._areas_compare(want=want, have=have)
+ self._vrfs_compare(want=want, have=have)
+ self._af_compare(want=want, have=have)
+
+ if len(self.commands) != begin or (not have and want):
+ self.commands.insert(
+ begin,
+ self._tmplt.render(
+ want or have,
+ "vrf"
+ if "vrf" in (want.keys() or have.keys())
+ else "process_id",
+ False,
+ ),
+ )
+
+ def _areas_compare(self, want, have):
+ wareas = want.get("areas", {})
+ hareas = have.get("areas", {})
+ for name, entry in iteritems(wareas):
+ self._area_compare(want=entry, have=hareas.pop(name, {}))
+ for name, entry in iteritems(hareas):
+ self._area_compare(want={}, have=entry)
+
+ def _area_compare(self, want, have):
+ parsers = ["area.nssa", "area.nssa.translate", "area.stub"]
+ self.compare(parsers=parsers, want=want, have=have)
+
+ def _vrfs_compare(self, want, have):
+ wvrfs = want.get("vrfs", {})
+ hvrfs = have.get("vrfs", {})
+ for name, entry in iteritems(wvrfs):
+ self._compare(want=entry, have=hvrfs.pop(name, {}))
+ # remove remaining items in have for replaced
+ for name, entry in iteritems(hvrfs):
+ self.addcmd(entry, "vrf", True)
+
+ def _af_compare(self, want, have):
+ parsers = [
+ "default_information.originate",
+ "distance",
+ "maximum_paths",
+ "table_map",
+ "timers.throttle.spf",
+ ]
+ waf = want.get("address_family", {})
+ haf = have.get("address_family", {})
+
+ cmd_ptr = len(self.commands)
+
+ self._af_areas_compare(want=waf, have=haf)
+ self._af_compare_lists(want=waf, have=haf)
+ self.compare(parsers=parsers, want=waf, have=haf)
+
+ cmd_ptr_nxt = len(self.commands)
+ if cmd_ptr < cmd_ptr_nxt:
+ self.commands.insert(cmd_ptr, "address-family ipv6 unicast")
+
+ def _af_areas_compare(self, want, have):
+ wareas = want.get("areas", {})
+ hareas = have.get("areas", {})
+ for name, entry in iteritems(wareas):
+ self._af_area_compare(want=entry, have=hareas.pop(name, {}))
+ for name, entry in iteritems(hareas):
+ self._af_area_compare(want={}, have=entry)
+
+ def _af_area_compare(self, want, have):
+ self.compare(parsers=["area.default_cost"], want=want, have=have)
+ self._af_area_compare_lists(want=want, have=have)
+
+ def _af_area_compare_lists(self, want, have):
+ for attrib in ["filter_list", "ranges"]:
+ wdict = want.get(attrib, {})
+ hdict = have.get(attrib, {})
+ for key, entry in iteritems(wdict):
+ if entry != hdict.pop(key, {}):
+ entry["area_id"] = want["area_id"]
+ self.addcmd(entry, "area.{0}".format(attrib), False)
+ # remove remaining items in have for replaced
+ for entry in hdict.values():
+ entry["area_id"] = have["area_id"]
+ self.addcmd(entry, "area.{0}".format(attrib), True)
+
+ def _af_compare_lists(self, want, have):
+ for attrib in ["summary_address", "redistribute"]:
+ wdict = get_from_dict(want, attrib) or {}
+ hdict = get_from_dict(have, attrib) or {}
+
+ for key, entry in iteritems(wdict):
+ if entry != hdict.pop(key, {}):
+ self.addcmd(entry, attrib, False)
+ # remove remaining items in have for replaced
+ for entry in hdict.values():
+ self.addcmd(entry, attrib, True)
+
+ def _ospfv3_list_to_dict(self, entry):
+ for _pid, proc in iteritems(entry):
+ proc["areas"] = {
+ entry["area_id"]: entry for entry in proc.get("areas", [])
+ }
+ af = proc.get("address_family")
+ if af:
+ for area in af.get("areas", []):
+ area["ranges"] = {
+ entry["prefix"]: entry
+ for entry in area.get("ranges", [])
+ }
+ area["filter_list"] = {
+ entry["direction"]: entry
+ for entry in area.get("filter_list", [])
+ }
+ af["areas"] = {
+ entry["area_id"]: entry for entry in af.get("areas", [])
+ }
+ af["summary_address"] = {
+ entry["prefix"]: entry
+ for entry in af.get("summary_address", [])
+ }
+ af["redistribute"] = {
+ (entry.get("id"), entry["protocol"]): entry
+ for entry in af.get("redistribute", [])
+ }
+ if "vrfs" in proc:
+ proc["vrfs"] = {
+ entry["vrf"]: entry for entry in proc.get("vrfs", [])
+ }
+ self._ospfv3_list_to_dict(proc["vrfs"])
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/static_routes.py
new file mode 100644
index 00000000..a28b5699
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/static_routes/static_routes.py
@@ -0,0 +1,616 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_static_routes class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ remove_empties,
+ dict_diff,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from copy import deepcopy
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ flatten_dict,
+ search_obj_in_list,
+ get_interface_type,
+ normalize_interface,
+)
+
+
+class Static_routes(ConfigBase):
+ """
+ The nxos_xstatic_routes class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["static_routes"]
+
+ def __init__(self, module):
+ super(Static_routes, self).__init__(module)
+
+ def get_static_routes_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ static_routes_facts = facts["ansible_network_resources"].get(
+ "static_routes"
+ )
+ if not static_routes_facts:
+ return []
+
+ return static_routes_facts
+
+ def edit_config(self, commands):
+ """Wrapper method for `_connection.edit_config()`
+ This exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ warnings = list()
+ commands = list()
+ state = self._module.params["state"]
+ action_states = ["merged", "replaced", "deleted", "overridden"]
+
+ if state == "gathered":
+ result["gathered"] = self.get_static_routes_facts()
+ elif state == "rendered":
+ result["rendered"] = self.set_config({})
+ # no need to fetch facts for rendered
+ elif state == "parsed":
+ result["parsed"] = self.set_config({})
+ # no need to fetch facts for parsed
+ else:
+ existing_static_routes_facts = self.get_static_routes_facts()
+ commands.extend(self.set_config(existing_static_routes_facts))
+ if commands and state in action_states:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result["changed"] = True
+ result["before"] = existing_static_routes_facts
+ result["commands"] = commands
+
+ changed_static_routes_facts = self.get_static_routes_facts()
+ if result["changed"]:
+ result["after"] = changed_static_routes_facts
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_static_routes_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params["config"]
+ want = []
+ if config:
+ for w in config:
+ want.append(remove_empties(w))
+ have = existing_static_routes_facts
+ want = self.add_default_vrf(deepcopy(want))
+ have = self.add_default_vrf(deepcopy(have))
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ commands = []
+ if state == "overridden":
+ commands = self._state_overridden(want, have)
+ elif state == "deleted":
+ commands = self._state_deleted(want, have)
+ elif state == "rendered":
+ commands = self._state_rendered(want, have=[])
+ elif state == "parsed":
+ want = self._module.params["running_config"]
+ commands = self._state_parsed(want)
+ else:
+ for w in want:
+ if state == "merged":
+ commands.extend(self._state_merged(w, have))
+ elif state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_parsed(self, want):
+ return self.get_static_routes_facts(want)
+
+ def _state_rendered(self, want, have):
+ commands = []
+ for w in want:
+ commands.extend(self.set_commands(w, {}))
+ return commands
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ delete_commands = []
+ state = self._module.params["state"]
+ merged_commands = []
+ obj_in_have = search_obj_in_list(want["vrf"], have, "vrf")
+ # in replaced, we check if whatever in have is in want, unlike merged. This is because we need to apply deleted on have config
+ if obj_in_have and obj_in_have != {"vrf": "default"}:
+ want_afi_list = []
+ if "address_families" in want.keys():
+ want_afi_list = [w["afi"] for w in want["address_families"]]
+ if len(want_afi_list) > 0:
+ for h in obj_in_have["address_families"]:
+ if h["afi"] in want_afi_list:
+ want_afi = search_obj_in_list(
+ h["afi"], want["address_families"], "afi"
+ )
+ want_dest_list = []
+ if "routes" in want_afi.keys():
+ want_dest_list = [
+ w["dest"] for w in want_afi["routes"]
+ ]
+ if len(want_dest_list) > 0:
+ for ro in h["routes"]:
+ if ro["dest"] in want_dest_list:
+ want_dest = search_obj_in_list(
+ ro["dest"], want_afi["routes"], "dest"
+ )
+ want_next_hops = []
+ if "next_hops" in want_dest.keys():
+ want_next_hops = [
+ nh for nh in want_dest["next_hops"]
+ ]
+ if len(want_next_hops) > 0:
+ for next_hop in ro["next_hops"]:
+ if next_hop not in want_next_hops:
+ # have's next hop not in want, so delete it
+ delete_dict = {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [
+ {
+ "afi": h["afi"],
+ "routes": [
+ {
+ "dest": ro[
+ "dest"
+ ],
+ "next_hops": [
+ next_hop
+ ],
+ }
+ ],
+ }
+ ],
+ }
+ delete_commands.extend(
+ self.del_commands(
+ [delete_dict]
+ )
+ )
+ else:
+ # want has no next_hops, so delete all next_hops under that dest
+ if state == "overridden":
+ delete_dict = {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [
+ {
+ "afi": h["afi"],
+ "routes": [
+ {
+ "dest": ro[
+ "dest"
+ ],
+ "next_hops": ro[
+ "next_hops"
+ ],
+ }
+ ],
+ }
+ ],
+ }
+ delete_commands.extend(
+ self.del_commands(
+ [delete_dict]
+ )
+ )
+ else:
+ if state == "overridden":
+ delete_dict = {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [
+ {
+ "afi": h["afi"],
+ "routes": [
+ {
+ "dest": ro["dest"],
+ "next_hops": ro[
+ "next_hops"
+ ],
+ }
+ ],
+ }
+ ],
+ }
+ delete_commands.extend(
+ self.del_commands([delete_dict])
+ )
+
+ else:
+ if (
+ state == "overridden"
+ ): # want has no 'routes' key, so delete all routes under that afi
+ if "routes" in h.keys():
+ delete_dict = {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [
+ {
+ "afi": h["afi"],
+ "routes": h["routes"],
+ }
+ ],
+ }
+ delete_commands.extend(
+ self.del_commands([delete_dict])
+ )
+ else:
+ if (
+ state == "overridden"
+ ): # want has 'vrf' key only. So delete all address families in it
+ delete_commands.extend(
+ self.del_commands(
+ [
+ {
+ "address_families": [
+ h
+ for h in obj_in_have[
+ "address_families"
+ ]
+ ],
+ "vrf": obj_in_have["vrf"],
+ }
+ ]
+ )
+ )
+ final_delete_commands = []
+ for d in delete_commands:
+ if d not in final_delete_commands:
+ final_delete_commands.append(d)
+ # if there are two afis, 'vrf context..' is added twice fom del_commands. The above code removes the redundant 'vrf context ..'
+ merged_commands = self.set_commands(want, have)
+ if merged_commands:
+ cmds = set(final_delete_commands).intersection(
+ set(merged_commands)
+ )
+ for c in cmds:
+ merged_commands.remove(c)
+
+ # set_commands adds a 'vrf context..' line. The above code removes the redundant 'vrf context ..'
+ commands.extend(final_delete_commands)
+ commands.extend(merged_commands)
+ return commands
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ want_vrfs = [w["vrf"] for w in want]
+ for h in have:
+ if h["vrf"] not in want_vrfs and h["vrf"] != "management":
+ commands.extend(self._state_deleted([h], have))
+ for w in want:
+ commands.extend(self._state_replaced(w, have))
+ return commands
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.set_commands(want, have)
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ delete_dict = {}
+ obj_in_have = search_obj_in_list(w["vrf"], have, "vrf")
+ if obj_in_have:
+ if "address_families" in w.keys():
+ o1 = obj_in_have["address_families"]
+ afi_list = [o["afi"] for o in o1] # have's afi list
+ for w1 in w["address_families"]:
+ if w1["afi"] in afi_list:
+ o2 = search_obj_in_list(w1["afi"], o1, "afi")
+ state = self._module.params["state"]
+ if state != "deleted":
+ # Deleted scope is till afi only. Part below is for use by overridden state.
+ if "routes" in w1.keys():
+ for w2 in w1["routes"]:
+ o3 = search_obj_in_list(
+ w2["dest"],
+ o2["routes"],
+ "dest",
+ )
+ hops = []
+ if "next_hops" in w2.keys():
+ for nh in w2["next_hops"]:
+ if nh in o3["next_hops"]:
+ hops.append(nh)
+ else:
+ # if next hops not given
+ hops = o3["next_hops"]
+
+ delete_dict = {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [
+ {
+ "afi": w1["afi"],
+ "routes": [
+ {
+ "dest": w2[
+ "dest"
+ ],
+ "next_hops": hops,
+ }
+ ],
+ }
+ ],
+ }
+ commands.extend(
+ self.del_commands(
+ [delete_dict]
+ )
+ )
+ else:
+ # case when only afi given for delete
+ delete_dict = {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [
+ {
+ "afi": o2["afi"],
+ "routes": o2["routes"],
+ }
+ ],
+ }
+ commands.extend(
+ self.del_commands([delete_dict])
+ )
+ else:
+ commands.extend(
+ self.del_commands(
+ [
+ {
+ "vrf": obj_in_have["vrf"],
+ "address_families": [o2],
+ }
+ ]
+ )
+ )
+ else:
+ # only vrf given to delete
+ commands.extend(self.del_commands([obj_in_have]))
+ else:
+ if have:
+ # delete everything
+ del_have = []
+ for h in have:
+ if h["vrf"] != "management": # protect management vrf
+ del_have.append(h)
+ commands = self.del_commands(del_have)
+
+ final_delete_commands = []
+ # del_commands might add 'vrf context..' twice for two routes in the same vrf. This removes it
+ for c in commands:
+ if c not in final_delete_commands:
+ final_delete_commands.append(c)
+ return final_delete_commands
+
+ def del_commands(self, have):
+ commands = []
+ for h in have:
+ if h != {"vrf": "default"}:
+ vrf = h["vrf"]
+ commands.append("vrf context " + vrf)
+ for af in h["address_families"]:
+ for route in af["routes"]:
+ for next_hop in route["next_hops"]:
+ command = self.del_next_hop(af, route, next_hop)
+ commands.append(command.strip())
+ return commands
+
+ def del_next_hop(self, af, route, next_hop):
+ command = ""
+ if af["afi"] == "ipv4":
+ command = (
+ "no ip route "
+ + route["dest"]
+ + " "
+ + self.add_commands(next_hop)
+ )
+ else:
+ command = (
+ "no ipv6 route "
+ + route["dest"]
+ + " "
+ + self.add_commands(next_hop)
+ )
+ return command
+
+ def add_commands(self, want):
+ command = ""
+ params = want.keys()
+ pref = vrf = ip = intf = name = tag = track = ""
+ if "admin_distance" in params:
+ pref = str(want["admin_distance"]) + " "
+ if "track" in params:
+ track = "track " + str(want["track"]) + " "
+ if "dest_vrf" in params:
+ vrf = "vrf " + str(want["dest_vrf"]) + " "
+ if "forward_router_address" in params:
+ ip = want["forward_router_address"] + " "
+ if "interface" in params:
+ intf = normalize_interface(want["interface"]) + " "
+ if "null0" in intf:
+ ip = ""
+ intf = "null0 "
+ if "route_name" in params:
+ name = "name " + str(want["route_name"]) + " "
+ if "tag" in params:
+ tag = "tag " + str(want["tag"]) + " "
+ command = intf + ip + vrf + name + tag + track + pref
+ if intf != "Null0 " and ip == "":
+ self._module.fail_json(msg="forward_router_address error")
+ return command.strip()
+
+ def set_commands(self, want, have):
+ commands = []
+ h1 = h2 = h3 = {}
+ want = remove_empties(want)
+ vrf_list = []
+ if have:
+ vrf_list = [h["vrf"] for h in have]
+ if want["vrf"] in vrf_list and have != [{"vrf": "default"}]:
+ for x in have:
+ if x["vrf"] == want["vrf"]:
+ h1 = x # this has the 'have' dict with same vrf as want
+ if "address_families" in h1.keys():
+ afi_list = [h["afi"] for h in h1["address_families"]]
+ if "address_families" in want.keys():
+ for af in want["address_families"]:
+ if af["afi"] in afi_list:
+ for x in h1["address_families"]:
+ if x["afi"] == af["afi"]:
+ h2 = (
+ x
+ ) # this has the have dict with same vrf and afi as want
+ dest_list = [h["dest"] for h in h2["routes"]]
+ if "routes" in af.keys():
+ for ro in af["routes"]:
+ if ro["dest"] in dest_list:
+ for x in h2["routes"]:
+ if x["dest"] == ro["dest"]:
+ h3 = (
+ x
+ ) # this has the have dict with same vrf, afi and dest as want
+ next_hop_list = [
+ h for h in h3["next_hops"]
+ ]
+ if "next_hops" in ro.keys():
+ for nh in ro["next_hops"]:
+ if "interface" in nh.keys():
+ nh[
+ "interface"
+ ] = normalize_interface(
+ nh["interface"]
+ )
+ if nh not in next_hop_list:
+ # no match for next hop in have
+ commands = self.set_next_hop(
+ want,
+ h2,
+ nh,
+ ro,
+ commands,
+ )
+ vrf_list.append(
+ want["vrf"]
+ )
+ else:
+ # no match for dest
+ if "next_hops" in ro.keys():
+ for nh in ro["next_hops"]:
+ commands = self.set_next_hop(
+ want, h2, nh, ro, commands
+ )
+ else:
+ # no match for afi
+ if "routes" in af.keys():
+ for ro in af["routes"]:
+ for nh in ro["next_hops"]:
+ commands = self.set_next_hop(
+ want, af, nh, ro, commands
+ )
+ else:
+ # no match for vrf
+ vrf_list.append(want["vrf"])
+ for af in want["address_families"]:
+ for ro in af["routes"]:
+ for nh in ro["next_hops"]:
+ commands = self.set_next_hop(
+ want, af, nh, ro, commands
+ )
+ return commands
+
+ def set_next_hop(self, want, h2, nh, ro, commands):
+ vrf = want["vrf"]
+ if h2["afi"] == "ipv4":
+ com = "ip route " + ro["dest"] + " " + self.add_commands(nh)
+ else:
+ com = "ipv6 route " + ro["dest"] + " " + self.add_commands(nh)
+ commands.append(com.strip())
+ string = "vrf context " + str(vrf)
+ if string not in commands:
+ commands.insert(0, string)
+ return commands
+
+ def add_default_vrf(self, dictionary):
+ """
+ This method is used to add 'default' vrf to the facts collected as global/default vrf
+ is not shown in facts. vrf key exists for all vrfs except global.
+ """
+ for d in dictionary:
+ if "vrf" not in d.keys():
+ d.update({"vrf": "default"})
+ return dictionary
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/telemetry.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/telemetry.py
new file mode 100644
index 00000000..b1a5a0b2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/telemetry/telemetry.py
@@ -0,0 +1,665 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_telemetry class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.cmdref.telemetry.telemetry import (
+ TMS_GLOBAL,
+ TMS_DESTGROUP,
+ TMS_SENSORGROUP,
+ TMS_SUBSCRIPTION,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.telemetry.telemetry import (
+ normalize_data,
+ remove_duplicate_context,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.telemetry.telemetry import (
+ valiate_input,
+ get_setval_path,
+ massage_data,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.telemetry.telemetry import (
+ get_module_params_subsection,
+ remove_duplicate_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ normalize_interface,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ NxosCmdRef,
+)
+
+
+class Telemetry(ConfigBase):
+ """
+ The nxos_telemetry class
+ """
+
+ gather_subset = ["!all", "!min"]
+
+ gather_network_resources = ["telemetry"]
+
+ def __init__(self, module):
+ super(Telemetry, self).__init__(module)
+
+ def get_telemetry_facts(self):
+ """ Get the 'facts' (the current configuration)
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources
+ )
+ telemetry_facts = facts["ansible_network_resources"].get("telemetry")
+ if not telemetry_facts:
+ return {}
+ return telemetry_facts
+
+ def edit_config(self, commands):
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = list()
+ warnings = list()
+
+ state = self._module.params["state"]
+ if "overridden" in state:
+ self._module.fail_json(
+ msg="State <overridden> is invalid for this module."
+ )
+ # When state is 'deleted', the module_params should not contain data
+ # under the 'config' key
+ if "deleted" in state and self._module.params.get("config"):
+ self._module.fail_json(
+ msg="Remove config key from playbook when state is <deleted>"
+ )
+
+ if self._module.params["config"] is None:
+ self._module.params["config"] = {}
+ # Normalize interface name.
+ int = self._module.params["config"].get("source_interface")
+ if int:
+ self._module.params["config"][
+ "source_interface"
+ ] = normalize_interface(int)
+
+ existing_telemetry_facts = self.get_telemetry_facts()
+ commands.extend(self.set_config(existing_telemetry_facts))
+ if commands:
+ if not self._module.check_mode:
+ self.edit_config(commands)
+ # TODO: edit_config is only available for network_cli. Once we
+ # add support for httpapi, we will need to switch to load_config
+ # or add support to httpapi for edit_config
+ #
+ # self._connection.load_config(commands)
+ result["changed"] = True
+ result["commands"] = commands
+
+ changed_telemetry_facts = self.get_telemetry_facts()
+
+ result["before"] = existing_telemetry_facts
+ if result["changed"]:
+ result["after"] = changed_telemetry_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_tms_global_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ config = self._module.params["config"]
+ want = dict((k, v) for k, v in config.items() if v is not None)
+ have = existing_tms_global_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+
+ # The deleted case is very simple since we purge all telemetry config
+ # and does not require any processing using NxosCmdRef objects.
+ if state == "deleted":
+ return self._state_deleted(want, have)
+ elif state == "replaced":
+ if want == have:
+ return []
+ return self._state_replaced(want, have)
+
+ # Save off module params
+ ALL_MP = self._module.params["config"]
+
+ cmd_ref = {}
+ cmd_ref["TMS_GLOBAL"] = {}
+ cmd_ref["TMS_DESTGROUP"] = {}
+ cmd_ref["TMS_SENSORGROUP"] = {}
+ cmd_ref["TMS_SUBSCRIPTION"] = {}
+
+ # Build Telemetry Global NxosCmdRef Object
+ cmd_ref["TMS_GLOBAL"]["ref"] = []
+ self._module.params["config"] = get_module_params_subsection(
+ ALL_MP, "TMS_GLOBAL"
+ )
+ cmd_ref["TMS_GLOBAL"]["ref"].append(
+ NxosCmdRef(self._module, TMS_GLOBAL)
+ )
+ ref = cmd_ref["TMS_GLOBAL"]["ref"][0]
+ ref.set_context()
+ ref.get_existing()
+ ref.get_playvals()
+ device_cache = ref.cache_existing
+
+ def build_cmdref_objects(td):
+ cmd_ref[td["type"]]["ref"] = []
+ saved_ids = []
+ if want.get(td["name"]):
+ for playvals in want[td["name"]]:
+ valiate_input(playvals, td["name"], self._module)
+ if playvals["id"] in saved_ids:
+ continue
+ saved_ids.append(playvals["id"])
+ resource_key = td["cmd"].format(playvals["id"])
+ # Only build the NxosCmdRef object for the td['name'] module parameters.
+ self._module.params[
+ "config"
+ ] = get_module_params_subsection(
+ ALL_MP, td["type"], playvals["id"]
+ )
+ cmd_ref[td["type"]]["ref"].append(
+ NxosCmdRef(self._module, td["obj"])
+ )
+ ref = cmd_ref[td["type"]]["ref"][-1]
+ ref.set_context([resource_key])
+ if td["type"] == "TMS_SENSORGROUP" and get_setval_path(
+ self._module
+ ):
+ # Sensor group path setting can contain optional values.
+ # Call get_setval_path helper function to process any
+ # optional setval keys.
+ ref._ref["path"]["setval"] = get_setval_path(
+ self._module
+ )
+ ref.get_existing(device_cache)
+ ref.get_playvals()
+ if td["type"] == "TMS_DESTGROUP":
+ normalize_data(ref)
+
+ # Build Telemetry Destination Group NxosCmdRef Objects
+ td = {
+ "name": "destination_groups",
+ "type": "TMS_DESTGROUP",
+ "obj": TMS_DESTGROUP,
+ "cmd": "destination-group {0}",
+ }
+ build_cmdref_objects(td)
+
+ # Build Telemetry Sensor Group NxosCmdRef Objects
+ td = {
+ "name": "sensor_groups",
+ "type": "TMS_SENSORGROUP",
+ "obj": TMS_SENSORGROUP,
+ "cmd": "sensor-group {0}",
+ }
+ build_cmdref_objects(td)
+
+ # Build Telemetry Subscription NxosCmdRef Objects
+ td = {
+ "name": "subscriptions",
+ "type": "TMS_SUBSCRIPTION",
+ "obj": TMS_SUBSCRIPTION,
+ "cmd": "subscription {0}",
+ }
+ build_cmdref_objects(td)
+
+ if state == "merged":
+ if want == have:
+ return []
+ commands = self._state_merged(cmd_ref)
+ return commands
+
+ @staticmethod
+ def _state_replaced(want, have):
+ """ The command generator when state is replaced
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ massaged_have = massage_data(have)
+ massaged_want = massage_data(want)
+
+ ref = {}
+ ref["tms_global"] = NxosCmdRef([], TMS_GLOBAL, ref_only=True)
+ ref["tms_destgroup"] = NxosCmdRef([], TMS_DESTGROUP, ref_only=True)
+ ref["tms_sensorgroup"] = NxosCmdRef([], TMS_SENSORGROUP, ref_only=True)
+ ref["tms_subscription"] = NxosCmdRef(
+ [], TMS_SUBSCRIPTION, ref_only=True
+ )
+
+ # Order matters for state replaced.
+ # First remove all subscriptions, followed by sensor-groups and destination-groups.
+ # Second add all destination-groups, followed by sensor-groups and subscriptions
+ add = {
+ "TMS_GLOBAL": [],
+ "TMS_DESTGROUP": [],
+ "TMS_SENSORGROUP": [],
+ "TMS_SUBSCRIPTION": [],
+ }
+ delete = {
+ "TMS_DESTGROUP": [],
+ "TMS_SENSORGROUP": [],
+ "TMS_SUBSCRIPTION": [],
+ }
+
+ # Process Telemetry Global Want and Have Values
+ # Possible states:
+ # - want and have are (set) (equal: no action, not equal: replace with want)
+ # - want (set) have (not set) (add want)
+ # - want (not set) have (set) (delete have)
+ # - want (not set) have (not set) (no action)
+ # global_ctx = ref['tms_global']._ref['_template']['context']
+ # property_ctx = ref['tms_global']._ref['certificate'].get('context')
+ # setval = ref['tms_global']._ref['certificate']['setval']
+ #
+ all_global_properties = [
+ "certificate",
+ "compression",
+ "source_interface",
+ "vrf",
+ ]
+ dest_profile_properties = ["compression", "source_interface", "vrf"]
+ dest_profile_remote_commands = []
+ for property in all_global_properties:
+ cmd = None
+ global_ctx = ref["tms_global"]._ref["_template"]["context"]
+ property_ctx = ref["tms_global"]._ref[property].get("context")
+ setval = ref["tms_global"]._ref[property]["setval"]
+ kind = ref["tms_global"]._ref[property]["kind"]
+ if want.get(property) is not None:
+ if have.get(property) is not None:
+ if want.get(property) != have.get(property):
+ if kind == "dict":
+ cmd = [setval.format(**want.get(property))]
+ else:
+ cmd = [setval.format(want.get(property))]
+ elif have.get(property) is None:
+ if kind == "dict":
+ cmd = [setval.format(**want.get(property))]
+ else:
+ cmd = [setval.format(want.get(property))]
+ elif want.get(property) is None:
+ if have.get(property) is not None:
+ if kind == "dict":
+ cmd = ["no " + setval.format(**have.get(property))]
+ else:
+ cmd = ["no " + setval.format(have.get(property))]
+ if property in dest_profile_properties:
+ dest_profile_remote_commands.extend(cmd)
+
+ if cmd is not None:
+ ctx = global_ctx
+ if property_ctx is not None:
+ ctx.extend(property_ctx)
+ add["TMS_GLOBAL"].extend(ctx)
+ add["TMS_GLOBAL"].extend(cmd)
+
+ add["TMS_GLOBAL"] = remove_duplicate_commands(add["TMS_GLOBAL"])
+ # If all destination profile commands are being removed then just
+ # remove the config context instead.
+ if len(dest_profile_remote_commands) == 3:
+ for item in dest_profile_remote_commands:
+ add["TMS_GLOBAL"].remove(item)
+ add["TMS_GLOBAL"].remove("destination-profile")
+ add["TMS_GLOBAL"].extend(["no destination-profile"])
+
+ # Process Telemetry destination_group, sensor_group and subscription Want and Have Values
+ # Possible states:
+ # - want (not set) have (set) (delete have)
+ # - want and have are (set) (equal: no action, not equal: replace with want)
+ # - want (set) have (not set) (add want)
+ # - want (not set) have (not set) (no action)
+ tms_resources = [
+ "TMS_DESTGROUP",
+ "TMS_SENSORGROUP",
+ "TMS_SUBSCRIPTION",
+ ]
+ for resource in tms_resources:
+ if resource == "TMS_DESTGROUP":
+ name = "destination-group"
+ cmd_property = "destination"
+ global_ctx = ref["tms_destgroup"]._ref["_template"]["context"]
+ setval = ref["tms_destgroup"]._ref["destination"]["setval"]
+ want_resources = massaged_want.get("destination_groups")
+ have_resources = massaged_have.get("destination_groups")
+ if resource == "TMS_SENSORGROUP":
+ name = "sensor-group"
+ global_ctx = ref["tms_sensorgroup"]._ref["_template"][
+ "context"
+ ]
+ setval = {}
+ setval["data_source"] = ref["tms_sensorgroup"]._ref[
+ "data_source"
+ ]["setval"]
+ setval["path"] = ref["tms_sensorgroup"]._ref["path"]["setval"]
+ want_resources = massaged_want.get("sensor_groups")
+ have_resources = massaged_have.get("sensor_groups")
+ if resource == "TMS_SUBSCRIPTION":
+ name = "subscription"
+ global_ctx = ref["tms_subscription"]._ref["_template"][
+ "context"
+ ]
+ setval = {}
+ setval["destination_group"] = ref["tms_subscription"]._ref[
+ "destination_group"
+ ]["setval"]
+ setval["sensor_group"] = ref["tms_subscription"]._ref[
+ "sensor_group"
+ ]["setval"]
+ want_resources = massaged_want.get("subscriptions")
+ have_resources = massaged_have.get("subscriptions")
+
+ if not want_resources and have_resources:
+ # want not and have not set so delete have
+ for key in have_resources.keys():
+ remove_context = ["{0} {1} {2}".format("no", name, key)]
+ delete[resource].extend(global_ctx)
+ if remove_context[0] not in delete[resource]:
+ delete[resource].extend(remove_context)
+ else:
+ # want and have are set.
+ # process wants:
+ for want_key in want_resources.keys():
+ if want_key not in have_resources.keys():
+ # Want resource key not in have resource key so add it
+ property_ctx = ["{0} {1}".format(name, want_key)]
+ for item in want_resources[want_key]:
+ if resource == "TMS_DESTGROUP":
+ cmd = [setval.format(**item[cmd_property])]
+ add[resource].extend(global_ctx)
+ if property_ctx[0] not in add[resource]:
+ add[resource].extend(property_ctx)
+ add[resource].extend(cmd)
+ if resource == "TMS_SENSORGROUP":
+ cmd = {}
+ if item.get("data_source"):
+ cmd["data_source"] = [
+ setval["data_source"].format(
+ item["data_source"]
+ )
+ ]
+ if item.get("path"):
+ setval["path"] = get_setval_path(
+ item.get("path")
+ )
+ cmd["path"] = [
+ setval["path"].format(**item["path"])
+ ]
+ add[resource].extend(global_ctx)
+ if property_ctx[0] not in add[resource]:
+ add[resource].extend(property_ctx)
+ if cmd.get("data_source"):
+ add[resource].extend(cmd["data_source"])
+ if cmd.get("path"):
+ add[resource].extend(cmd["path"])
+ if resource == "TMS_SUBSCRIPTION":
+ cmd = {}
+ if item.get("destination_group"):
+ cmd["destination_group"] = [
+ setval["destination_group"].format(
+ item["destination_group"]
+ )
+ ]
+ if item.get("sensor_group"):
+ cmd["sensor_group"] = [
+ setval["sensor_group"].format(
+ **item["sensor_group"]
+ )
+ ]
+ add[resource].extend(global_ctx)
+ if property_ctx[0] not in add[resource]:
+ add[resource].extend(property_ctx)
+ if cmd.get("destination_group"):
+ add[resource].extend(
+ cmd["destination_group"]
+ )
+ if cmd.get("sensor_group"):
+ add[resource].extend(cmd["sensor_group"])
+
+ elif want_key in have_resources.keys():
+ # Want resource key exists in have resource keys but we need to
+ # inspect the individual items under the resource key
+ # for differences
+ for item in want_resources[want_key]:
+ if item not in have_resources[want_key]:
+ if item is None:
+ continue
+ # item wanted but does not exist so add it
+ property_ctx = [
+ "{0} {1}".format(name, want_key)
+ ]
+ if resource == "TMS_DESTGROUP":
+ cmd = [setval.format(**item[cmd_property])]
+ add[resource].extend(global_ctx)
+ if property_ctx[0] not in add[resource]:
+ add[resource].extend(property_ctx)
+ add[resource].extend(cmd)
+ if resource == "TMS_SENSORGROUP":
+ cmd = {}
+ if item.get("data_source"):
+ cmd["data_source"] = [
+ setval["data_source"].format(
+ item["data_source"]
+ )
+ ]
+ if item.get("path"):
+ setval["path"] = get_setval_path(
+ item.get("path")
+ )
+ cmd["path"] = [
+ setval["path"].format(
+ **item["path"]
+ )
+ ]
+ add[resource].extend(global_ctx)
+ if property_ctx[0] not in add[resource]:
+ add[resource].extend(property_ctx)
+ if cmd.get("data_source"):
+ add[resource].extend(
+ cmd["data_source"]
+ )
+ if cmd.get("path"):
+ add[resource].extend(cmd["path"])
+ if resource == "TMS_SUBSCRIPTION":
+ cmd = {}
+ if item.get("destination_group"):
+ cmd["destination_group"] = [
+ setval["destination_group"].format(
+ item["destination_group"]
+ )
+ ]
+ if item.get("sensor_group"):
+ cmd["sensor_group"] = [
+ setval["sensor_group"].format(
+ **item["sensor_group"]
+ )
+ ]
+ add[resource].extend(global_ctx)
+ if property_ctx[0] not in add[resource]:
+ add[resource].extend(property_ctx)
+ if cmd.get("destination_group"):
+ add[resource].extend(
+ cmd["destination_group"]
+ )
+ if cmd.get("sensor_group"):
+ add[resource].extend(
+ cmd["sensor_group"]
+ )
+
+ # process haves:
+ for have_key in have_resources.keys():
+ if have_key not in want_resources.keys():
+ # Want resource key is not in have resource keys so remove it
+ cmd = ["no " + "{0} {1}".format(name, have_key)]
+ delete[resource].extend(global_ctx)
+ delete[resource].extend(cmd)
+ elif have_key in want_resources.keys():
+ # Have resource key exists in want resource keys but we need to
+ # inspect the individual items under the resource key
+ # for differences
+ for item in have_resources[have_key]:
+ if item not in want_resources[have_key]:
+ if item is None:
+ continue
+ # have item not wanted so remove it
+ property_ctx = [
+ "{0} {1}".format(name, have_key)
+ ]
+ if resource == "TMS_DESTGROUP":
+ cmd = [
+ "no "
+ + setval.format(**item[cmd_property])
+ ]
+ delete[resource].extend(global_ctx)
+ if property_ctx[0] not in delete[resource]:
+ delete[resource].extend(property_ctx)
+ delete[resource].extend(cmd)
+ if resource == "TMS_SENSORGROUP":
+ cmd = {}
+ if item.get("data_source"):
+ cmd["data_source"] = [
+ "no "
+ + setval["data_source"].format(
+ item["data_source"]
+ )
+ ]
+ if item.get("path"):
+ setval["path"] = get_setval_path(
+ item.get("path")
+ )
+ cmd["path"] = [
+ "no "
+ + setval["path"].format(
+ **item["path"]
+ )
+ ]
+ delete[resource].extend(global_ctx)
+ if property_ctx[0] not in delete[resource]:
+ delete[resource].extend(property_ctx)
+ if cmd.get("data_source"):
+ delete[resource].extend(
+ cmd["data_source"]
+ )
+ if cmd.get("path"):
+ delete[resource].extend(cmd["path"])
+ if resource == "TMS_SUBSCRIPTION":
+ cmd = {}
+ if item.get("destination_group"):
+ cmd["destination_group"] = [
+ "no "
+ + setval[
+ "destination_group"
+ ].format(item["destination_group"])
+ ]
+ if item.get("sensor_group"):
+ cmd["sensor_group"] = [
+ "no "
+ + setval["sensor_group"].format(
+ **item["sensor_group"]
+ )
+ ]
+ delete[resource].extend(global_ctx)
+ if property_ctx[0] not in delete[resource]:
+ delete[resource].extend(property_ctx)
+ if cmd.get("destination_group"):
+ delete[resource].extend(
+ cmd["destination_group"]
+ )
+ if cmd.get("sensor_group"):
+ delete[resource].extend(
+ cmd["sensor_group"]
+ )
+
+ add[resource] = remove_duplicate_context(add[resource])
+ delete[resource] = remove_duplicate_context(delete[resource])
+
+ commands.extend(delete["TMS_SUBSCRIPTION"])
+ commands.extend(delete["TMS_SENSORGROUP"])
+ commands.extend(delete["TMS_DESTGROUP"])
+ commands.extend(add["TMS_DESTGROUP"])
+ commands.extend(add["TMS_SENSORGROUP"])
+ commands.extend(add["TMS_SUBSCRIPTION"])
+ commands.extend(add["TMS_GLOBAL"])
+ commands = remove_duplicate_context(commands)
+
+ return commands
+
+ @staticmethod
+ def _state_merged(cmd_ref):
+ """ The command generator when state is merged
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = cmd_ref["TMS_GLOBAL"]["ref"][0].get_proposed()
+
+ if cmd_ref["TMS_DESTGROUP"].get("ref"):
+ for cr in cmd_ref["TMS_DESTGROUP"]["ref"]:
+ commands.extend(cr.get_proposed())
+
+ if cmd_ref["TMS_SENSORGROUP"].get("ref"):
+ for cr in cmd_ref["TMS_SENSORGROUP"]["ref"]:
+ commands.extend(cr.get_proposed())
+
+ if cmd_ref["TMS_SUBSCRIPTION"].get("ref"):
+ for cr in cmd_ref["TMS_SUBSCRIPTION"]["ref"]:
+ commands.extend(cr.get_proposed())
+
+ return remove_duplicate_context(commands)
+
+ @staticmethod
+ def _state_deleted(want, have):
+ """ The command generator when state is deleted
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want != have:
+ commands = ["no telemetry"]
+ return commands
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/vlans.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/vlans.py
new file mode 100644
index 00000000..09badec7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/config/vlans/vlans.py
@@ -0,0 +1,338 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos_vlans class
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to it's desired end-state is
+created
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ dict_diff,
+ to_list,
+ remove_empties,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ search_obj_in_list,
+)
+
+
+class Vlans(ConfigBase):
+ """
+ The nxos_vlans class
+ """
+
+ gather_subset = ["min"]
+
+ gather_network_resources = ["vlans"]
+
+ def __init__(self, module):
+ super(Vlans, self).__init__(module)
+
+ def get_platform(self):
+ """Wrapper method for getting platform info
+ This method exists solely to allow the unit test framework to mock calls.
+ """
+ return self.facts.get("ansible_net_platform", "")
+
+ def get_vlans_facts(self, data=None):
+ """ Get the 'facts' (the current configuration)
+
+ :rtype: A dictionary
+ :returns: The current configuration as a dictionary
+ """
+ self.facts, _warnings = Facts(self._module).get_facts(
+ self.gather_subset, self.gather_network_resources, data=data
+ )
+ vlans_facts = self.facts["ansible_network_resources"].get("vlans")
+ if not vlans_facts:
+ return []
+
+ return vlans_facts
+
+ def edit_config(self, commands):
+ """Wrapper method for `_connection.edit_config()`
+ This exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return self._connection.edit_config(commands)
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {"changed": False}
+ commands = []
+ warnings = []
+
+ if self.state in self.ACTION_STATES:
+ existing_vlans_facts = self.get_vlans_facts()
+ self._platform = self.get_platform()
+ else:
+ existing_vlans_facts = []
+ self._platform = ""
+
+ if self.state in self.ACTION_STATES or self.state == "rendered":
+ commands.extend(self.set_config(existing_vlans_facts))
+
+ if commands and self.state in self.ACTION_STATES:
+ if not self._module.check_mode:
+ self.edit_config(commands)
+ result["changed"] = True
+
+ if self.state in self.ACTION_STATES:
+ result["commands"] = commands
+
+ if self.state in self.ACTION_STATES or self.state == "gathered":
+ changed_vlans_facts = self.get_vlans_facts()
+
+ elif self.state == "rendered":
+ result["rendered"] = commands
+
+ elif self.state == "parsed":
+ running_config = self._module.params["running_config"]
+ if not running_config:
+ self._module.fail_json(
+ msg="value of running_config parameter must not be empty for state parsed"
+ )
+ result["parsed"] = self.get_vlans_facts(data=running_config)
+
+ if self.state in self.ACTION_STATES:
+ result["before"] = existing_vlans_facts
+ if result["changed"]:
+ result["after"] = changed_vlans_facts
+
+ elif self.state == "gathered":
+ result["gathered"] = changed_vlans_facts
+
+ result["warnings"] = warnings
+ return result
+
+ def set_config(self, existing_vlans_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ want = self._module.params.get("config") or []
+ have = existing_vlans_facts
+ resp = self.set_state(self._sanitize(want), self._sanitize(have))
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ state = self._module.params["state"]
+ if (
+ state in ("overridden", "merged", "replaced", "rendered")
+ and not want
+ ):
+ self._module.fail_json(
+ msg="value of config parameter must not be empty for state {0}".format(
+ state
+ )
+ )
+
+ commands = list()
+ if state == "overridden":
+ commands.extend(self._state_overridden(want, have))
+ elif state == "deleted":
+ commands.extend(self._state_deleted(want, have))
+ else:
+ for w in want:
+ if state in ["merged", "rendered"]:
+ commands.extend(self._state_merged(w, have))
+ elif state == "replaced":
+ commands.extend(self._state_replaced(w, have))
+
+ return commands
+
+ def remove_default_states(self, obj):
+ """Removes non-empty but default states from the obj.
+ """
+ default_states = {"enabled": True, "state": "active", "mode": "ce"}
+ for k in default_states.keys():
+ if obj.get(k) == default_states[k]:
+ obj.pop(k, None)
+ return obj
+
+ def _state_replaced(self, want, have):
+ """ The command generator when state is replaced.
+ Scope is limited to vlan objects defined in the playbook.
+ :rtype: A list
+ :returns: The minimum command set required to migrate the current
+ configuration to the desired configuration.
+ """
+ obj_in_have = search_obj_in_list(want["vlan_id"], have, "vlan_id")
+ if obj_in_have:
+ # Diff the want and have
+ diff = dict_diff(want, obj_in_have)
+ # Remove merge items from diff; what's left will be used to
+ # remove states not specified in the playbook
+ for k in dict(set(want.items()) - set(obj_in_have.items())).keys():
+ diff.pop(k, None)
+ else:
+ diff = want
+
+ # Remove default states from resulting diff
+ diff = self.remove_default_states(diff)
+
+ # merged_cmds: 'want' cmds to update 'have' states that don't match
+ # replaced_cmds: remaining 'have' cmds that need to be reset to default
+ merged_cmds = self.set_commands(want, have)
+ replaced_cmds = []
+ if obj_in_have:
+ # Remaining diff items are used to reset states to default
+ replaced_cmds = self.del_attribs(diff)
+ cmds = []
+ if replaced_cmds or merged_cmds:
+ cmds += ["vlan %s" % str(want["vlan_id"])]
+ cmds += merged_cmds + replaced_cmds
+ return cmds
+
+ def _state_overridden(self, want, have):
+ """ The command generator when state is overridden.
+ Scope includes all vlan objects on the device.
+ :rtype: A list
+ :returns: the minimum command set required to migrate the current
+ configuration to the desired configuration.
+ """
+ # overridden behavior is the same as replaced except for scope.
+ cmds = []
+ existing_vlans = []
+ for h in have:
+ existing_vlans.append(h["vlan_id"])
+ obj_in_want = search_obj_in_list(h["vlan_id"], want, "vlan_id")
+ if obj_in_want:
+ if h != obj_in_want:
+ replaced_cmds = self._state_replaced(obj_in_want, [h])
+ if replaced_cmds:
+ cmds.extend(replaced_cmds)
+ else:
+ cmds.append("no vlan %s" % h["vlan_id"])
+
+ # Add wanted vlans that don't exist on the device yet
+ for w in want:
+ if w["vlan_id"] not in existing_vlans:
+ new_vlan = ["vlan %s" % w["vlan_id"]]
+ cmds.extend(new_vlan + self.add_commands(w))
+ return cmds
+
+ def _state_merged(self, w, have):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ cmds = self.set_commands(w, have)
+ if cmds:
+ cmds.insert(0, "vlan %s" % str(w["vlan_id"]))
+ return cmds
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+ if want:
+ for w in want:
+ obj_in_have = search_obj_in_list(w["vlan_id"], have, "vlan_id")
+ if obj_in_have:
+ commands.append("no vlan " + str(obj_in_have["vlan_id"]))
+ else:
+ if not have:
+ return commands
+ for h in have:
+ commands.append("no vlan " + str(h["vlan_id"]))
+ return commands
+
+ def del_attribs(self, obj):
+ """Returns a list of commands to reset states to default
+ """
+ commands = []
+ if not obj:
+ return commands
+ default_cmds = {
+ "name": "no name",
+ "state": "no state",
+ "enabled": "no shutdown",
+ "mode": "mode ce",
+ "mapped_vni": "no vn-segment",
+ }
+ for k in obj:
+ commands.append(default_cmds[k])
+ return commands
+
+ def diff_of_dicts(self, w, obj):
+ diff = set(w.items()) - set(obj.items())
+ diff = dict(diff)
+ if diff and w["vlan_id"] == obj["vlan_id"]:
+ diff.update({"vlan_id": w["vlan_id"]})
+ return diff
+
+ def add_commands(self, d):
+ commands = []
+ if not d:
+ return commands
+ if "name" in d:
+ commands.append("name " + d["name"])
+ if "state" in d:
+ commands.append("state " + d["state"])
+ if "enabled" in d:
+ if d["enabled"] is True:
+ commands.append("no shutdown")
+ else:
+ commands.append("shutdown")
+ if "mode" in d:
+ commands.append("mode " + d["mode"])
+ if "mapped_vni" in d:
+ commands.append("vn-segment %s" % d["mapped_vni"])
+
+ return commands
+
+ def set_commands(self, w, have):
+ commands = []
+ obj_in_have = search_obj_in_list(w["vlan_id"], have, "vlan_id")
+ if not obj_in_have:
+ commands = self.add_commands(w)
+ else:
+ diff = self.diff_of_dicts(w, obj_in_have)
+ commands = self.add_commands(diff)
+ return commands
+
+ def _sanitize(self, vlans):
+ sanitized_vlans = []
+ for vlan in vlans:
+ if not re.search("N[567][7K]", self._platform):
+ if "mode" in vlan:
+ del vlan["mode"]
+ sanitized_vlans.append(remove_empties(vlan))
+ return sanitized_vlans
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py
new file mode 100644
index 00000000..94686bd9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acl_interfaces/acl_interfaces.py
@@ -0,0 +1,135 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The nxos acl_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acl_interfaces.acl_interfaces import (
+ Acl_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ normalize_interface,
+)
+
+
+class Acl_interfacesFacts(object):
+ """ The nxos acl_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Acl_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_device_data(self, connection):
+ return connection.get("show running-config | section ^interface")
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for acl_interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = self.get_device_data(connection)
+ data = data.split("interface")
+
+ resources = []
+ for i in range(len(data)):
+ intf = data[i].split("\n")
+ for l in range(1, len(intf)):
+ if not re.search(
+ "ip(v6)?( port)? (access-group|traffic-filter)", intf[l]
+ ):
+ intf[l] = ""
+ intf = list(filter(None, intf))
+ resources.append(intf)
+
+ objs = []
+ for resource in resources:
+ if resource:
+ obj = self.render_config(self.generated_spec, resource)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("acl_interfaces", None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ params = utils.remove_empties(params)
+ facts["acl_interfaces"] = params["config"]
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+ config["name"] = conf[0].strip()
+ config["access_groups"] = []
+ v4 = {"afi": "ipv4", "acls": []}
+ v6 = {"afi": "ipv6", "acls": []}
+ for c in conf[1:]:
+ if c:
+ acl4 = re.search(
+ r"ip(?P<port>\sport)?\saccess-group\s(?P<name>\S+)\s(?P<dir>in|out)",
+ c,
+ )
+ acl6 = re.search(
+ r"ipv6(?P<port>\sport)?\straffic-filter\s(?P<name>\S+)\s(?P<dir>in|out)",
+ c,
+ )
+ if acl4:
+ v4["acls"].append(self._parse(acl4))
+ elif acl6:
+ v6["acls"].append(self._parse(acl6))
+
+ if len(v4["acls"]) > 0:
+ config["access_groups"].append(v4)
+ if len(v6["acls"]) > 0:
+ config["access_groups"].append(v6)
+
+ return utils.remove_empties(config)
+
+ def _parse(self, data):
+ return {
+ "name": data.group("name").strip(),
+ "direction": data.group("dir").strip(),
+ "port": True if data.group("port") else None,
+ }
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/acls.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/acls.py
new file mode 100644
index 00000000..abdf2968
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/acls/acls.py
@@ -0,0 +1,283 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos acls fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acls.acls import (
+ AclsArgs,
+)
+
+
+class AclsFacts(object):
+ """ The nxos acls fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = AclsArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_device_data(self, connection):
+ data = connection.get(
+ "show running-config | section 'ip(v6)* access-list'"
+ )
+ if data == "{}":
+ return ""
+ return data
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for acls
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = self.get_device_data(connection)
+ data = re.split("\nip", data)
+ v6 = []
+ v4 = []
+
+ for i in range(len(data)):
+ if str(data[i]):
+ if "v6" in str(data[i]).split()[0]:
+ v6.append(data[i])
+ else:
+ v4.append(data[i])
+
+ resources = []
+ resources.append(v6)
+ resources.append(v4)
+ objs = []
+ for resource in resources:
+ if resource:
+ obj = self.render_config(self.generated_spec, resource)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("acls", None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ params = utils.remove_empties(params)
+ facts["acls"] = params["config"]
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def get_endpoint(self, ace, pro):
+ ret_dict = {}
+ option = ace.split()[0]
+ if option == "any":
+ ret_dict.update({"any": True})
+ else:
+ # it could be a.b.c.d or a.b.c.d/x or a.b.c.d/32
+ if "/" in option: # or 'host' in option:
+ ip = re.search(r"(.*)/(\d+)", option)
+ if int(ip.group(2)) < 32 or 32 < int(ip.group(2)) < 128:
+ ret_dict.update({"prefix": option})
+ else:
+ ret_dict.update({"host": ip.group(1)})
+ else:
+ ret_dict.update({"address": option})
+ wb = ace.split()[1]
+ ret_dict.update({"wildcard_bits": wb})
+ ace = re.sub("{0}".format(wb), "", ace, 1)
+ ace = re.sub(option, "", ace, 1)
+ if pro in ["tcp", "udp"]:
+ keywords = ["eq", "lt", "gt", "neq", "range"]
+ if len(ace.split()) and ace.split()[0] in keywords:
+ port_protocol = {}
+ port_pro = re.search(r"(eq|lt|gt|neq) (\w*)", ace)
+ if port_pro:
+ port_protocol.update(
+ {port_pro.group(1): port_pro.group(2)}
+ )
+ ace = re.sub(port_pro.group(1), "", ace, 1)
+ ace = re.sub(port_pro.group(2), "", ace, 1)
+ else:
+ limit = re.search(r"(range) (\w*) (\w*)", ace)
+ if limit:
+ port_protocol.update(
+ {
+ "range": {
+ "start": limit.group(2),
+ "end": limit.group(3),
+ }
+ }
+ )
+ ace = re.sub(limit.group(2), "", ace, 1)
+ ace = re.sub(limit.group(3), "", ace, 1)
+ if port_protocol:
+ ret_dict.update({"port_protocol": port_protocol})
+ return ace, ret_dict
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+ protocol_options = {
+ "tcp": ["fin", "established", "psh", "rst", "syn", "urg", "ack"],
+ "icmp": [
+ "administratively_prohibited",
+ "alternate_address",
+ "conversion_error",
+ "dod_host_prohibited",
+ "dod_net_prohibited",
+ "echo_request",
+ "echo",
+ "echo_reply",
+ "general_parameter_problem",
+ "host_isolated",
+ "host_precedence_unreachable",
+ "host_redirect",
+ "host_tos_redirect",
+ "host_tos_unreachable",
+ "host_unknown",
+ "host_unreachable",
+ "information_reply",
+ "information_request",
+ "mask_reply",
+ "mask_request",
+ "mobile_redirect",
+ "net_redirect",
+ "net_tos_redirect",
+ "net_tos_unreachable",
+ "net_unreachable",
+ "network_unknown",
+ "no_room_for_option",
+ "option_missing",
+ "packet_too_big",
+ "parameter_problem",
+ "port_unreachable",
+ "precedence_unreachable",
+ "protocol_unreachable",
+ "reassembly_timeout",
+ "redirect",
+ "router_advertisement",
+ "router_solicitation",
+ "source_quench",
+ "source_route_failed",
+ "time_exceeded",
+ "timestamp_reply",
+ "timestamp_request",
+ "traceroute",
+ "ttl_exceeded",
+ "unreachable",
+ ],
+ "igmp": ["dvmrp", "host_query", "host_report"],
+ }
+ if conf:
+ if "v6" in conf[0].split()[0]:
+ config["afi"] = "ipv6"
+ else:
+ config["afi"] = "ipv4"
+ config["acls"] = []
+ for acl in conf:
+ acls = {}
+ if "match-local-traffic" in acl:
+ config["match_local_traffic"] = True
+ continue
+ acl = acl.split("\n")
+ acl = [a.strip() for a in acl]
+ acl = list(filter(None, acl))
+ acls["name"] = re.match(
+ r"(ip)?(v6)?\s?access-list (.*)", acl[0]
+ ).group(3)
+ acls["aces"] = []
+ for ace in list(filter(None, acl[1:])):
+ if re.search(r"ip(.*)access-list.*", ace):
+ break
+ entry = {}
+ ace = ace.strip()
+ seq = re.match(r"(\d*)", ace).group(0)
+ entry.update({"sequence": seq})
+ ace = re.sub(seq, "", ace, 1)
+ grant = ace.split()[0]
+ rem = ""
+ if grant != "remark":
+ entry.update({"grant": grant})
+ else:
+ rem = re.match(".*remark (.*)", ace).group(1)
+ entry.update({"remark": rem})
+
+ if not rem:
+ ace = re.sub(grant, "", ace, 1)
+ pro = ace.split()[0]
+ entry.update({"protocol": pro})
+ ace = re.sub(pro, "", ace, 1)
+ ace, source = self.get_endpoint(ace, pro)
+ entry.update({"source": source})
+ ace, dest = self.get_endpoint(ace, pro)
+ entry.update({"destination": dest})
+
+ dscp = re.search(r"dscp (\w*)", ace)
+ if dscp:
+ entry.update({"dscp": dscp.group(1)})
+
+ frag = re.search(r"fragments", ace)
+ if frag:
+ entry.update({"fragments": True})
+
+ prec = re.search(r"precedence (\w*)", ace)
+ if prec:
+ entry.update({"precedence": prec.group(1)})
+
+ log = re.search("log", ace)
+ if log:
+ entry.update({"log": True})
+
+ if pro == "tcp" or pro == "icmp" or pro == "igmp":
+ pro_options = {}
+ options = {}
+ for option in protocol_options[pro]:
+ option = re.sub("_", "-", option)
+ if option in ace:
+ if (
+ option == "echo"
+ and "echo_request" in options
+ ):
+ continue
+ option = re.sub("-", "_", option)
+ options.update({option: True})
+ if options:
+ pro_options.update({pro: options})
+ if pro_options:
+ entry.update({"protocol_options": pro_options})
+ acls["aces"].append(entry)
+ config["acls"].append(acls)
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py
new file mode 100644
index 00000000..dee9d60c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bfd_interfaces/bfd_interfaces.py
@@ -0,0 +1,114 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# 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
+
+"""
+The nxos bfd_interfaces fact class
+Populate the facts tree based on the current device configuration.
+"""
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.bfd_interfaces.bfd_interfaces import (
+ Bfd_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+
+
+class Bfd_interfacesFacts(object):
+ """ The nxos_bfd_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Bfd_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for bfd_interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+
+ if not data:
+ data = connection.get(
+ "show running-config | section '^interface|^feature bfd'"
+ )
+
+ # Some of the bfd attributes
+ if "feature bfd" in data.split("\n"):
+ resources = data.split("interface ")
+ resources.pop(0)
+ else:
+ resources = []
+ for resource in resources:
+ if resource:
+ obj = self.render_config(self.generated_spec, resource)
+ if obj and len(obj.keys()) > 1:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("bfd_interfaces", None)
+ facts = {}
+ if objs:
+ facts["bfd_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["bfd_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ match = re.search(r"^(\S+)", conf)
+ intf = match.group(1)
+ if get_interface_type(intf) == "unknown":
+ return {}
+ config["name"] = intf
+ # 'bfd'/'bfd echo' do not nvgen when enabled thus set to 'enable' when None.
+ # 'bfd' is not supported on some platforms
+ config["bfd"] = (
+ utils.parse_conf_cmd_arg(conf, "bfd", "enable", "disable")
+ or "enable"
+ )
+ config["echo"] = (
+ utils.parse_conf_cmd_arg(conf, "bfd echo", "enable", "disable")
+ or "enable"
+ )
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/bgp_global.py
new file mode 100644
index 00000000..764acaa1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/bgp_global/bgp_global.py
@@ -0,0 +1,135 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The nxos bgp_global fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.bgp_global import (
+ Bgp_globalTemplate,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.bgp_global.bgp_global import (
+ Bgp_globalArgs,
+)
+
+
+class Bgp_globalFacts(object):
+ """ The nxos bgp_global facts class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Bgp_globalArgs.argument_spec
+
+ def get_config(self, connection):
+ """Wrapper method for `connection.get()`
+ This method exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return connection.get("show running-config | section '^router bgp'")
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for Bgp_global network resource
+
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+
+ :rtype: dictionary
+ :returns: facts
+ """
+ facts = {}
+
+ if not data:
+ data = self.get_config(connection)
+
+ data = self._flatten_config(data)
+
+ # parse native config using the Bgp_global template
+ bgp_global_parser = Bgp_globalTemplate(lines=data.splitlines())
+ obj = bgp_global_parser.parse()
+
+ vrfs = obj.get("vrfs", {})
+
+ # move global vals to their correct position in facts tree
+ # this is only needed for keys that are valid for both global
+ # and VRF contexts
+ global_vals = vrfs.pop("vrf_", {})
+ for key, value in iteritems(global_vals):
+ obj[key] = value
+
+ # transform vrfs into a list
+ if vrfs:
+ obj["vrfs"] = sorted(
+ list(obj["vrfs"].values()), key=lambda k, sk="vrf": k[sk]
+ )
+ for vrf in obj["vrfs"]:
+ self._post_parse(vrf)
+
+ self._post_parse(obj)
+
+ obj = utils.remove_empties(obj)
+
+ ansible_facts["ansible_network_resources"].pop("bgp_global", None)
+ params = utils.remove_empties(
+ utils.validate_config(self.argument_spec, {"config": obj})
+ )
+
+ facts["bgp_global"] = params.get("config", {})
+ ansible_facts["ansible_network_resources"].update(facts)
+
+ return ansible_facts
+
+ def _flatten_config(self, data):
+ """ Flatten neighbor contexts in
+ the running-config for easier parsing.
+ :param obj: dict
+ :returns: flattened running config
+ """
+ data = data.split("\n")
+ in_nbr_cxt = False
+ cur_nbr = {}
+
+ for x in data:
+ cur_indent = len(x) - len(x.lstrip())
+ if x.strip().startswith("neighbor"):
+ in_nbr_cxt = True
+ cur_nbr["nbr"] = x
+ cur_nbr["indent"] = cur_indent
+ elif cur_nbr and (cur_indent <= cur_nbr["indent"]):
+ in_nbr_cxt = False
+ elif in_nbr_cxt:
+ data[data.index(x)] = cur_nbr["nbr"] + " " + x.strip()
+
+ return "\n".join(data)
+
+ def _post_parse(self, obj):
+ """ Converts the intermediate data structure
+ to valid format as per argspec.
+ :param obj: dict
+ """
+ conf_peers = obj.get("confederation", {}).get("peers")
+ if conf_peers:
+ obj["confederation"]["peers"] = conf_peers.split()
+ obj["confederation"]["peers"].sort()
+
+ neighbors = obj.get("neighbors", {})
+ if neighbors:
+ obj["neighbors"] = sorted(
+ list(neighbors.values()),
+ key=lambda k, sk="neighbor_address": k[sk],
+ )
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/facts.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/facts.py
new file mode 100644
index 00000000..9620c415
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/facts.py
@@ -0,0 +1,145 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# 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
+"""
+The facts class for nxos
+this file validates each subset of facts and selectively
+calls the appropriate facts gathering function
+"""
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import (
+ FactsBase,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.legacy.base import (
+ Default,
+ Legacy,
+ Hardware,
+ Config,
+ Interfaces,
+ Features,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.bfd_interfaces.bfd_interfaces import (
+ Bfd_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.hsrp_interfaces.hsrp_interfaces import (
+ Hsrp_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.interfaces.interfaces import (
+ InterfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.l2_interfaces.l2_interfaces import (
+ L2_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.lacp.lacp import (
+ LacpFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.l3_interfaces.l3_interfaces import (
+ L3_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.lag_interfaces.lag_interfaces import (
+ Lag_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.telemetry.telemetry import (
+ TelemetryFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.vlans.vlans import (
+ VlansFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.lacp_interfaces.lacp_interfaces import (
+ Lacp_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.lldp_global.lldp_global import (
+ Lldp_globalFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.lldp_interfaces.lldp_interfaces import (
+ Lldp_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.acl_interfaces.acl_interfaces import (
+ Acl_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.acls.acls import (
+ AclsFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.static_routes.static_routes import (
+ Static_routesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.ospfv2.ospfv2 import (
+ Ospfv2Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.ospfv3.ospfv3 import (
+ Ospfv3Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.ospf_interfaces.ospf_interfaces import (
+ Ospf_interfacesFacts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.bgp_global.bgp_global import (
+ Bgp_globalFacts,
+)
+
+
+FACT_LEGACY_SUBSETS = dict(
+ default=Default,
+ legacy=Legacy,
+ hardware=Hardware,
+ interfaces=Interfaces,
+ config=Config,
+ features=Features,
+)
+FACT_RESOURCE_SUBSETS = dict(
+ bfd_interfaces=Bfd_interfacesFacts,
+ hsrp_interfaces=Hsrp_interfacesFacts,
+ lag_interfaces=Lag_interfacesFacts,
+ lldp_global=Lldp_globalFacts,
+ telemetry=TelemetryFacts,
+ vlans=VlansFacts,
+ lacp=LacpFacts,
+ lacp_interfaces=Lacp_interfacesFacts,
+ interfaces=InterfacesFacts,
+ l3_interfaces=L3_interfacesFacts,
+ l2_interfaces=L2_interfacesFacts,
+ lldp_interfaces=Lldp_interfacesFacts,
+ acl_interfaces=Acl_interfacesFacts,
+ acls=AclsFacts,
+ static_routes=Static_routesFacts,
+ ospfv2=Ospfv2Facts,
+ ospfv3=Ospfv3Facts,
+ ospf_interfaces=Ospf_interfacesFacts,
+ bgp_global=Bgp_globalFacts,
+)
+
+
+class Facts(FactsBase):
+ """ The fact class for nxos
+ """
+
+ VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys())
+ VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys())
+
+ def __init__(self, module):
+ super(Facts, self).__init__(module)
+
+ def get_facts(
+ self, legacy_facts_type=None, resource_facts_type=None, data=None
+ ):
+ """ Collect the facts for nxos
+ :param legacy_facts_type: List of legacy facts types
+ :param resource_facts_type: List of resource fact types
+ :param data: previously collected conf
+ :rtype: dict
+ :return: the facts gathered
+ """
+ if self.VALID_RESOURCE_SUBSETS:
+ self.get_network_resources_facts(
+ FACT_RESOURCE_SUBSETS, resource_facts_type, data
+ )
+
+ if self.VALID_LEGACY_GATHER_SUBSETS:
+ self.get_network_legacy_facts(
+ FACT_LEGACY_SUBSETS, legacy_facts_type
+ )
+
+ return self.ansible_facts, self._warnings
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py
new file mode 100644
index 00000000..7275e4e3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/hsrp_interfaces/hsrp_interfaces.py
@@ -0,0 +1,100 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# 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
+
+"""
+The nxos hsrp_interfaces fact class
+Populate the facts tree based on the current device configuration.
+"""
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.hsrp_interfaces.hsrp_interfaces import (
+ Hsrp_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+
+
+class Hsrp_interfacesFacts(object):
+ """ The nxos hsrp_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Hsrp_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for hsrp_interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+
+ if not data:
+ data = connection.get("show running-config | section ^interface")
+
+ resources = data.split("interface ")
+ for resource in resources:
+ if resource:
+ obj = self.render_config(self.generated_spec, resource)
+ if obj and len(obj.keys()) > 1:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("hsrp_interfaces", None)
+ facts = {}
+ if objs:
+ facts["hsrp_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["hsrp_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ match = re.search(r"^(\S+)", conf)
+ intf = match.group(1)
+ if get_interface_type(intf) == "unknown":
+ return {}
+ config["name"] = intf
+ config["bfd"] = utils.parse_conf_cmd_arg(
+ conf, "hsrp bfd", "enable", "disable"
+ )
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/interfaces.py
new file mode 100644
index 00000000..0c5f0964
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/interfaces/interfaces.py
@@ -0,0 +1,119 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python
+"""
+The nxos interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.interfaces.interfaces import (
+ InterfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ default_intf_enabled,
+)
+
+
+class InterfacesFacts(object):
+ """ The nxos interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = InterfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for interfaces
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ if not data:
+ data = connection.get("show running-config | section ^interface")
+
+ config = ("\n" + data).split("\ninterface ")
+ for conf in config:
+ conf = conf.strip()
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("interfaces", None)
+ facts = {}
+ facts["interfaces"] = []
+ if objs:
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ match = re.search(r"^(\S+)", conf)
+ intf = match.group(1)
+ if get_interface_type(intf) == "unknown":
+ return {}
+ config["name"] = intf
+ config["description"] = utils.parse_conf_arg(conf, "description")
+ config["speed"] = utils.parse_conf_arg(conf, "speed")
+ config["mtu"] = utils.parse_conf_arg(conf, "mtu")
+ config["duplex"] = utils.parse_conf_arg(conf, "duplex")
+ config["mode"] = utils.parse_conf_cmd_arg(
+ conf, "switchport", "layer2", "layer3"
+ )
+
+ config["enabled"] = utils.parse_conf_cmd_arg(
+ conf, "shutdown", False, True
+ )
+
+ config["fabric_forwarding_anycast_gateway"] = utils.parse_conf_cmd_arg(
+ conf, "fabric forwarding mode anycast-gateway", True
+ )
+ config["ip_forward"] = utils.parse_conf_cmd_arg(
+ conf, "ip forward", True
+ )
+
+ interfaces_cfg = utils.remove_empties(config)
+ return interfaces_cfg
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py
new file mode 100644
index 00000000..481b6db2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,109 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python
+"""
+The nxos l2_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.l2_interfaces.l2_interfaces import (
+ L2_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+
+
+class L2_interfacesFacts(object):
+ """The nxos l2_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = L2_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for l2_interfaces
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ if not data:
+ data = connection.get("show running-config | section ^interface")
+
+ config = ("\n" + data).split("\ninterface ")
+ for conf in config:
+ conf = conf.strip()
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("l2_interfaces", None)
+ facts = {}
+ if objs:
+ facts["l2_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["l2_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ match = re.search(r"^(\S+)", conf)
+ intf = match.group(1)
+ if get_interface_type(intf) == "unknown":
+ return {}
+
+ config["name"] = intf
+ config["mode"] = utils.parse_conf_arg(conf, "switchport mode")
+ config["ip_forward"] = utils.parse_conf_arg(conf, "ip forward")
+ config["access"]["vlan"] = utils.parse_conf_arg(
+ conf, "switchport access vlan"
+ )
+ config["trunk"]["allowed_vlans"] = utils.parse_conf_arg(
+ conf, "switchport trunk allowed vlan"
+ )
+ config["trunk"]["native_vlan"] = utils.parse_conf_arg(
+ conf, "switchport trunk native vlan"
+ )
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py
new file mode 100644
index 00000000..18dc6595
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/l3_interfaces/l3_interfaces.py
@@ -0,0 +1,144 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python
+"""
+The nxos l3_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.l3_interfaces.l3_interfaces import (
+ L3_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+
+
+class L3_interfacesFacts(object):
+ """ The nxos l3_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = L3_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for l3_interfaces
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ if not data:
+ data = connection.get("show running-config | section ^interface")
+
+ config = ("\n" + data).split("\ninterface ")
+ for conf in config:
+ conf = conf.strip()
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("l3_interfaces", None)
+ facts = {}
+ if objs:
+ facts["l3_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["l3_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+ match = re.search(r"^(\S+)", conf)
+ intf = match.group(1)
+ if get_interface_type(intf) == "unknown":
+ return {}
+ config["name"] = intf
+ config["dot1q"] = utils.parse_conf_arg(conf, "encapsulation dot1[qQ]")
+ config["redirects"] = utils.parse_conf_cmd_arg(
+ conf, "no ip redirects", False, True
+ )
+ config["unreachables"] = utils.parse_conf_cmd_arg(
+ conf, "ip unreachables", True, False
+ )
+ config["evpn_multisite_tracking"] = utils.parse_conf_arg(
+ conf, "evpn multisite"
+ )
+ ipv4_match = re.compile(r"\n ip address (.*)")
+ matches = ipv4_match.findall(conf)
+ if matches:
+ if matches[0]:
+ config["ipv4"] = []
+ for m in matches:
+ ipv4_conf = m.split()
+ addr = ipv4_conf[0]
+ if addr:
+ config_dict = {"address": addr}
+ if len(ipv4_conf) > 1:
+ d = ipv4_conf[1]
+ if d == "secondary":
+ config_dict.update({"secondary": True})
+ if len(ipv4_conf) == 4:
+ if ipv4_conf[2] == "tag":
+ config_dict.update(
+ {"tag": int(ipv4_conf[-1])}
+ )
+ elif d == "tag":
+ config_dict.update({"tag": int(ipv4_conf[-1])})
+ config["ipv4"].append(config_dict)
+
+ ipv6_match = re.compile(r"\n ipv6 address (.*)")
+ matches = ipv6_match.findall(conf)
+ if matches:
+ if matches[0]:
+ config["ipv6"] = []
+ for m in matches:
+ ipv6_conf = m.split()
+ addr = ipv6_conf[0]
+ if addr:
+ config_dict = {"address": addr}
+ if len(ipv6_conf) > 1:
+ d = ipv6_conf[1]
+ if d == "tag":
+ config_dict.update({"tag": int(ipv6_conf[-1])})
+ config["ipv6"].append(config_dict)
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/lacp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/lacp.py
new file mode 100644
index 00000000..3813bcd6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp/lacp.py
@@ -0,0 +1,93 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos lacp fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lacp.lacp import (
+ LacpArgs,
+)
+
+
+class LacpFacts(object):
+ """ The nxos lacp fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = LacpArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for lacp
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = connection.get("show running-config | include lacp")
+ resources = data.strip()
+ objs = self.render_config(self.generated_spec, resources)
+ ansible_facts["ansible_network_resources"].pop("lacp", None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ facts["lacp"] = utils.remove_empties(params["config"])
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ p_match = re.search(r"lacp system-priority (\d+)", conf, re.M)
+ if p_match:
+ config["system"]["priority"] = p_match.group(1)
+
+ a_match = re.search(r"lacp system-mac (\S+)", conf, re.M)
+ if a_match:
+ address = a_match.group(1)
+ config["system"]["mac"]["address"] = address
+ r_match = re.search(
+ r"lacp system-mac {0} role (\S+)".format(address), conf, re.M
+ )
+ if r_match:
+ config["system"]["mac"]["role"] = r_match.group(1)
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py
new file mode 100644
index 00000000..7e11c93a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py
@@ -0,0 +1,119 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos lacp_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lacp_interfaces.lacp_interfaces import (
+ Lacp_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+
+
+class Lacp_interfacesFacts(object):
+ """ The nxos lacp_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Lacp_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for lacp_interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+
+ if not data:
+ data = connection.get("show running-config | section ^interface")
+
+ resources = ("\n" + data).split("\ninterface ")
+ for resource in resources:
+ if resource and re.search(r"lacp", resource):
+ obj = self.render_config(self.generated_spec, resource)
+ if obj and len(obj.keys()) > 1:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("lacp_interfaces", None)
+ facts = {}
+ if objs:
+ facts["lacp_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["lacp_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ match = re.search(r"^(\S+)", conf)
+ intf = match.group(1)
+ if get_interface_type(intf) == "unknown":
+ return {}
+ config["name"] = intf
+ config["port_priority"] = utils.parse_conf_arg(
+ conf, "lacp port-priority"
+ )
+ config["rate"] = utils.parse_conf_arg(conf, "lacp rate")
+ config["mode"] = utils.parse_conf_arg(conf, "lacp mode")
+ suspend_individual = re.search(r"no lacp suspend-individual", conf)
+ if suspend_individual:
+ config["suspend_individual"] = False
+ max_links = utils.parse_conf_arg(conf, "lacp max-bundle")
+ if max_links:
+ config["links"]["max"] = max_links
+ min_links = utils.parse_conf_arg(conf, "lacp min-links")
+ if min_links:
+ config["links"]["min"] = min_links
+ graceful = re.search(r"no lacp graceful-convergence", conf)
+ if graceful:
+ config["convergence"]["graceful"] = False
+ vpc = re.search(r"lacp vpc-convergence", conf)
+ if vpc:
+ config["convergence"]["vpc"] = True
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py
new file mode 100644
index 00000000..ff287ecc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lag_interfaces/lag_interfaces.py
@@ -0,0 +1,112 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python
+"""
+The nxos lag_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lag_interfaces.lag_interfaces import (
+ Lag_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+ normalize_interface,
+)
+
+
+class Lag_interfacesFacts(object):
+ """ The nxos lag_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Lag_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for lag_interfaces
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ if not data:
+ data = connection.get("show running-config | section ^interface")
+
+ objs = self.render_config(self.generated_spec, data, connection)
+
+ ansible_facts["ansible_network_resources"].pop("lag_interfaces", None)
+ facts = {}
+ if objs:
+ facts["lag_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["lag_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf, connection):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ result = []
+ match = re.findall(r"interface (port-channel\d+)", conf)
+
+ for item in match:
+ result.append({"name": item, "members": []})
+
+ for intf in conf.split("interface "):
+ member = {}
+ match_intf = re.search(r"(port-channel|Ethernet)(\S+)", intf)
+ if match_intf:
+ member["member"] = match_intf.group(0)
+
+ match_line = re.search(
+ r"channel-group\s(?P<port_channel>\d+)(\smode\s(?P<mode>on|active|passive))?",
+ intf,
+ )
+ if match_line:
+ member.update(match_line.groupdict())
+
+ if member and member.get("port_channel", None):
+ port_channel = "port-channel{0}".format(
+ member.pop("port_channel")
+ )
+ for x in result:
+ if x["name"] == port_channel:
+ x["members"].append(utils.remove_empties(member))
+
+ return result
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/base.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/base.py
new file mode 100644
index 00000000..bfe14e52
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/legacy/base.py
@@ -0,0 +1,822 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# 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
+
+import platform
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ run_commands,
+ get_config,
+ get_capabilities,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+ normalize_interface,
+)
+from ansible.module_utils.six import iteritems
+
+
+g_config = None
+
+
+class FactsBase(object):
+ def __init__(self, module):
+ self.module = module
+ self.warnings = list()
+ self.facts = dict()
+ self.capabilities = get_capabilities(self.module)
+
+ def populate(self):
+ pass
+
+ def run(self, command, output="text"):
+ command_string = command
+ command = {"command": command, "output": output}
+ resp = run_commands(self.module, [command], check_rc="retry_json")
+ try:
+ return resp[0]
+ except IndexError:
+ self.warnings.append(
+ "command %s failed, facts for this command will not be populated"
+ % command_string
+ )
+ return None
+
+ def get_config(self):
+ global g_config
+ if not g_config:
+ g_config = get_config(self.module)
+ return g_config
+
+ def transform_dict(self, data, keymap):
+ transform = dict()
+ for key, fact in keymap:
+ if key in data:
+ transform[fact] = data[key]
+ return transform
+
+ def transform_iterable(self, iterable, keymap):
+ for item in iterable:
+ yield self.transform_dict(item, keymap)
+
+
+class Default(FactsBase):
+ def populate(self):
+ data = None
+ data = self.run("show version")
+
+ if data:
+ self.facts["serialnum"] = self.parse_serialnum(data)
+
+ data = self.run("show license host-id")
+ if data:
+ self.facts["license_hostid"] = self.parse_license_hostid(data)
+
+ self.facts.update(self.platform_facts())
+
+ def parse_serialnum(self, data):
+ match = re.search(r"Processor Board ID\s*(\S+)", data, re.M)
+ if match:
+ return match.group(1)
+
+ def platform_facts(self):
+ platform_facts = {}
+
+ resp = self.capabilities
+ device_info = resp["device_info"]
+
+ platform_facts["system"] = device_info["network_os"]
+
+ for item in ("model", "image", "version", "platform", "hostname"):
+ val = device_info.get("network_os_%s" % item)
+ if val:
+ platform_facts[item] = val
+
+ platform_facts["api"] = resp["network_api"]
+ platform_facts["python_version"] = platform.python_version()
+
+ return platform_facts
+
+ def parse_license_hostid(self, data):
+ match = re.search(r"License hostid: VDH=(.+)$", data, re.M)
+ if match:
+ return match.group(1)
+
+
+class Config(FactsBase):
+ def populate(self):
+ super(Config, self).populate()
+ self.facts["config"] = self.get_config()
+
+
+class Features(FactsBase):
+ def populate(self):
+ super(Features, self).populate()
+ data = self.get_config()
+
+ if data:
+ features = []
+ for line in data.splitlines():
+ if line.startswith("feature"):
+ features.append(line.replace("feature", "").strip())
+
+ self.facts["features_enabled"] = features
+
+
+class Hardware(FactsBase):
+ def populate(self):
+ data = self.run("dir")
+ if data:
+ self.facts["filesystems"] = self.parse_filesystems(data)
+
+ data = None
+ data = self.run("show system resources", output="json")
+
+ if data:
+ if isinstance(data, dict):
+ self.facts["memtotal_mb"] = (
+ int(data["memory_usage_total"]) / 1024
+ )
+ self.facts["memfree_mb"] = (
+ int(data["memory_usage_free"]) / 1024
+ )
+ else:
+ self.facts["memtotal_mb"] = self.parse_memtotal_mb(data)
+ self.facts["memfree_mb"] = self.parse_memfree_mb(data)
+
+ def parse_filesystems(self, data):
+ return re.findall(r"^Usage for (\S+)//", data, re.M)
+
+ def parse_memtotal_mb(self, data):
+ match = re.search(r"(\S+)K(\s+|)total", data, re.M)
+ if match:
+ memtotal = match.group(1)
+ return int(memtotal) / 1024
+
+ def parse_memfree_mb(self, data):
+ match = re.search(r"(\S+)K(\s+|)free", data, re.M)
+ if match:
+ memfree = match.group(1)
+ return int(memfree) / 1024
+
+
+class Interfaces(FactsBase):
+
+ INTERFACE_MAP = frozenset(
+ [
+ ("state", "state"),
+ ("desc", "description"),
+ ("eth_bw", "bandwidth"),
+ ("eth_duplex", "duplex"),
+ ("eth_speed", "speed"),
+ ("eth_mode", "mode"),
+ ("eth_hw_addr", "macaddress"),
+ ("eth_mtu", "mtu"),
+ ("eth_hw_desc", "type"),
+ ]
+ )
+
+ INTERFACE_SVI_MAP = frozenset(
+ [
+ ("svi_line_proto", "state"),
+ ("svi_bw", "bandwidth"),
+ ("svi_mac", "macaddress"),
+ ("svi_mtu", "mtu"),
+ ("type", "type"),
+ ]
+ )
+
+ INTERFACE_IPV4_MAP = frozenset(
+ [("eth_ip_addr", "address"), ("eth_ip_mask", "masklen")]
+ )
+
+ INTERFACE_SVI_IPV4_MAP = frozenset(
+ [("svi_ip_addr", "address"), ("svi_ip_mask", "masklen")]
+ )
+
+ INTERFACE_IPV6_MAP = frozenset([("addr", "address"), ("prefix", "subnet")])
+
+ def ipv6_structure_op_supported(self):
+ data = self.capabilities
+ if data:
+ nxos_os_version = data["device_info"]["network_os_version"]
+ unsupported_versions = ["I2", "F1", "A8"]
+ for ver in unsupported_versions:
+ if ver in nxos_os_version:
+ return False
+ return True
+
+ def populate(self):
+ self.facts["all_ipv4_addresses"] = list()
+ self.facts["all_ipv6_addresses"] = list()
+ self.facts["neighbors"] = {}
+ data = None
+
+ data = self.run("show interface", output="json")
+
+ if data:
+ if isinstance(data, dict):
+ self.facts["interfaces"] = self.populate_structured_interfaces(
+ data
+ )
+ else:
+ interfaces = self.parse_interfaces(data)
+ self.facts["interfaces"] = self.populate_interfaces(interfaces)
+
+ if self.ipv6_structure_op_supported():
+ data = self.run("show ipv6 interface", output="json")
+ else:
+ data = None
+ if data:
+ if isinstance(data, dict):
+ self.populate_structured_ipv6_interfaces(data)
+ else:
+ interfaces = self.parse_interfaces(data)
+ self.populate_ipv6_interfaces(interfaces)
+
+ data = self.run("show lldp neighbors", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts["neighbors"].update(
+ self.populate_structured_neighbors_lldp(data)
+ )
+ else:
+ self.facts["neighbors"].update(self.populate_neighbors(data))
+
+ data = self.run("show cdp neighbors detail", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts["neighbors"].update(
+ self.populate_structured_neighbors_cdp(data)
+ )
+ else:
+ self.facts["neighbors"].update(
+ self.populate_neighbors_cdp(data)
+ )
+
+ self.facts["neighbors"].pop(None, None) # Remove null key
+
+ def populate_structured_interfaces(self, data):
+ interfaces = dict()
+ data = data["TABLE_interface"]["ROW_interface"]
+
+ if isinstance(data, dict):
+ data = [data]
+
+ for item in data:
+ name = item["interface"]
+
+ intf = dict()
+ if "type" in item:
+ intf.update(self.transform_dict(item, self.INTERFACE_SVI_MAP))
+ else:
+ intf.update(self.transform_dict(item, self.INTERFACE_MAP))
+
+ if "eth_ip_addr" in item:
+ intf["ipv4"] = self.transform_dict(
+ item, self.INTERFACE_IPV4_MAP
+ )
+ self.facts["all_ipv4_addresses"].append(item["eth_ip_addr"])
+
+ if "svi_ip_addr" in item:
+ intf["ipv4"] = self.transform_dict(
+ item, self.INTERFACE_SVI_IPV4_MAP
+ )
+ self.facts["all_ipv4_addresses"].append(item["svi_ip_addr"])
+
+ interfaces[name] = intf
+
+ return interfaces
+
+ def populate_structured_ipv6_interfaces(self, data):
+ try:
+ data = data["TABLE_intf"]
+ if data:
+ if isinstance(data, dict):
+ data = [data]
+ for item in data:
+ name = item["ROW_intf"]["intf-name"]
+ intf = self.facts["interfaces"][name]
+ intf["ipv6"] = self.transform_dict(
+ item, self.INTERFACE_IPV6_MAP
+ )
+ try:
+ addr = item["ROW_intf"]["addr"]
+ except KeyError:
+ addr = item["ROW_intf"]["TABLE_addr"]["ROW_addr"][
+ "addr"
+ ]
+ self.facts["all_ipv6_addresses"].append(addr)
+ else:
+ return ""
+ except TypeError:
+ return ""
+
+ def populate_structured_neighbors_lldp(self, data):
+ objects = dict()
+ data = data["TABLE_nbor"]["ROW_nbor"]
+
+ if isinstance(data, dict):
+ data = [data]
+
+ for item in data:
+ local_intf = normalize_interface(item["l_port_id"])
+ objects[local_intf] = list()
+ nbor = dict()
+ nbor["port"] = item["port_id"]
+ nbor["host"] = nbor["sysname"] = item["chassis_id"]
+ objects[local_intf].append(nbor)
+
+ return objects
+
+ def populate_structured_neighbors_cdp(self, data):
+ objects = dict()
+ data = data["TABLE_cdp_neighbor_detail_info"][
+ "ROW_cdp_neighbor_detail_info"
+ ]
+
+ if isinstance(data, dict):
+ data = [data]
+
+ for item in data:
+ local_intf = item["intf_id"]
+ objects[local_intf] = list()
+ nbor = dict()
+ nbor["port"] = item["port_id"]
+ nbor["host"] = nbor["sysname"] = item["device_id"]
+ objects[local_intf].append(nbor)
+
+ return objects
+
+ def parse_interfaces(self, data):
+ parsed = dict()
+ key = ""
+ for line in data.split("\n"):
+ if len(line) == 0:
+ continue
+ elif line.startswith("admin") or line[0] == " ":
+ parsed[key] += "\n%s" % line
+ else:
+ match = re.match(r"^(\S+)", line)
+ if match:
+ key = match.group(1)
+ if not key.startswith("admin") or not key.startswith(
+ "IPv6 Interface"
+ ):
+ parsed[key] = line
+ return parsed
+
+ def populate_interfaces(self, interfaces):
+ facts = dict()
+ for key, value in iteritems(interfaces):
+ intf = dict()
+ if get_interface_type(key) == "svi":
+ intf["state"] = self.parse_state(key, value, intf_type="svi")
+ intf["macaddress"] = self.parse_macaddress(
+ value, intf_type="svi"
+ )
+ intf["mtu"] = self.parse_mtu(value, intf_type="svi")
+ intf["bandwidth"] = self.parse_bandwidth(
+ value, intf_type="svi"
+ )
+ intf["type"] = self.parse_type(value, intf_type="svi")
+ if "Internet Address" in value:
+ intf["ipv4"] = self.parse_ipv4_address(
+ value, intf_type="svi"
+ )
+ facts[key] = intf
+ else:
+ intf["state"] = self.parse_state(key, value)
+ intf["description"] = self.parse_description(value)
+ intf["macaddress"] = self.parse_macaddress(value)
+ intf["mode"] = self.parse_mode(value)
+ intf["mtu"] = self.parse_mtu(value)
+ intf["bandwidth"] = self.parse_bandwidth(value)
+ intf["duplex"] = self.parse_duplex(value)
+ intf["speed"] = self.parse_speed(value)
+ intf["type"] = self.parse_type(value)
+ if "Internet Address" in value:
+ intf["ipv4"] = self.parse_ipv4_address(value)
+ facts[key] = intf
+
+ return facts
+
+ def parse_state(self, key, value, intf_type="ethernet"):
+ match = None
+ if intf_type == "svi":
+ match = re.search(r"line protocol is\s*(\S+)", value, re.M)
+ else:
+ match = re.search(r"%s is\s*(\S+)" % key, value, re.M)
+
+ if match:
+ return match.group(1)
+
+ def parse_macaddress(self, value, intf_type="ethernet"):
+ match = None
+ if intf_type == "svi":
+ match = re.search(r"address is\s*(\S+)", value, re.M)
+ else:
+ match = re.search(r"address:\s*(\S+)", value, re.M)
+
+ if match:
+ return match.group(1)
+
+ def parse_mtu(self, value, intf_type="ethernet"):
+ match = re.search(r"MTU\s*(\S+)", value, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_bandwidth(self, value, intf_type="ethernet"):
+ match = re.search(r"BW\s*(\S+)", value, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_type(self, value, intf_type="ethernet"):
+ match = None
+ if intf_type == "svi":
+ match = re.search(r"Hardware is\s*(\S+)", value, re.M)
+ else:
+ match = re.search(r"Hardware:\s*(.+),", value, re.M)
+
+ if match:
+ return match.group(1)
+
+ def parse_description(self, value, intf_type="ethernet"):
+ match = re.search(r"Description: (.+)$", value, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_mode(self, value, intf_type="ethernet"):
+ match = re.search(r"Port mode is (\S+)", value, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_duplex(self, value, intf_type="ethernet"):
+ match = re.search(r"(\S+)-duplex", value, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_speed(self, value, intf_type="ethernet"):
+ match = re.search(r"duplex, (.+)$", value, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_ipv4_address(self, value, intf_type="ethernet"):
+ ipv4 = {}
+ match = re.search(r"Internet Address is (.+)$", value, re.M)
+ if match:
+ address = match.group(1)
+ addr = address.split("/")[0]
+ ipv4["address"] = address.split("/")[0]
+ ipv4["masklen"] = address.split("/")[1]
+ self.facts["all_ipv4_addresses"].append(addr)
+ return ipv4
+
+ def populate_neighbors(self, data):
+ objects = dict()
+ # if there are no neighbors the show command returns
+ # ERROR: No neighbour information
+ if data.startswith("ERROR"):
+ return dict()
+
+ regex = re.compile(r"(\S+)\s+(\S+)\s+\d+\s+\w+\s+(\S+)")
+
+ for item in data.split("\n")[4:-1]:
+ match = regex.match(item)
+ if match:
+ nbor = dict()
+ nbor["host"] = nbor["sysname"] = match.group(1)
+ nbor["port"] = match.group(3)
+ local_intf = normalize_interface(match.group(2))
+ if local_intf not in objects:
+ objects[local_intf] = []
+ objects[local_intf].append(nbor)
+
+ return objects
+
+ def populate_neighbors_cdp(self, data):
+ facts = dict()
+
+ for item in data.split("----------------------------------------"):
+ if item == "":
+ continue
+ local_intf = self.parse_lldp_intf(item)
+ if local_intf not in facts:
+ facts[local_intf] = list()
+
+ fact = dict()
+ fact["port"] = self.parse_lldp_port(item)
+ fact["sysname"] = self.parse_lldp_sysname(item)
+ facts[local_intf].append(fact)
+
+ return facts
+
+ def parse_lldp_intf(self, data):
+ match = re.search(r"Interface:\s*(\S+)", data, re.M)
+ if match:
+ return match.group(1).strip(",")
+
+ def parse_lldp_port(self, data):
+ match = re.search(r"Port ID \(outgoing port\):\s*(\S+)", data, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_lldp_sysname(self, data):
+ match = re.search(r"Device ID:(.+)$", data, re.M)
+ if match:
+ return match.group(1)
+
+ def populate_ipv6_interfaces(self, interfaces):
+ facts = dict()
+ for key, value in iteritems(interfaces):
+ intf = dict()
+ intf["ipv6"] = self.parse_ipv6_address(value)
+ facts[key] = intf
+
+ def parse_ipv6_address(self, value):
+ ipv6 = {}
+ match_addr = re.search(r"IPv6 address:\s*(\S+)", value, re.M)
+ if match_addr:
+ addr = match_addr.group(1)
+ ipv6["address"] = addr
+ self.facts["all_ipv6_addresses"].append(addr)
+ match_subnet = re.search(r"IPv6 subnet:\s*(\S+)", value, re.M)
+ if match_subnet:
+ ipv6["subnet"] = match_subnet.group(1)
+
+ return ipv6
+
+
+class Legacy(FactsBase):
+ # facts from nxos_facts 2.1
+
+ VERSION_MAP = frozenset(
+ [
+ ("host_name", "_hostname"),
+ ("kickstart_ver_str", "_os"),
+ ("chassis_id", "_platform"),
+ ]
+ )
+
+ MODULE_MAP = frozenset(
+ [
+ ("model", "model"),
+ ("modtype", "type"),
+ ("ports", "ports"),
+ ("status", "status"),
+ ]
+ )
+
+ FAN_MAP = frozenset(
+ [
+ ("fanname", "name"),
+ ("fanmodel", "model"),
+ ("fanhwver", "hw_ver"),
+ ("fandir", "direction"),
+ ("fanstatus", "status"),
+ ]
+ )
+
+ POWERSUP_MAP = frozenset(
+ [
+ ("psmodel", "model"),
+ ("psnum", "number"),
+ ("ps_status", "status"),
+ ("ps_status_3k", "status"),
+ ("actual_out", "actual_output"),
+ ("actual_in", "actual_in"),
+ ("total_capa", "total_capacity"),
+ ("input_type", "input_type"),
+ ("watts", "watts"),
+ ("amps", "amps"),
+ ]
+ )
+
+ def populate(self):
+ data = None
+
+ data = self.run("show version", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts.update(self.transform_dict(data, self.VERSION_MAP))
+ else:
+ self.facts["hostname"] = self.parse_hostname(data)
+ self.facts["os"] = self.parse_os(data)
+ self.facts["platform"] = self.parse_platform(data)
+
+ data = self.run("show interface", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts[
+ "interfaces_list"
+ ] = self.parse_structured_interfaces(data)
+ else:
+ self.facts["interfaces_list"] = self.parse_interfaces(data)
+
+ data = self.run("show vlan brief", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts["vlan_list"] = self.parse_structured_vlans(data)
+ else:
+ self.facts["vlan_list"] = self.parse_vlans(data)
+
+ data = self.run("show module", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts["module"] = self.parse_structured_module(data)
+ else:
+ self.facts["module"] = self.parse_module(data)
+
+ data = self.run("show environment fan", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts["fan_info"] = self.parse_structured_fan_info(data)
+ else:
+ self.facts["fan_info"] = self.parse_fan_info(data)
+
+ data = self.run("show environment power", output="json")
+ if data:
+ if isinstance(data, dict):
+ self.facts[
+ "power_supply_info"
+ ] = self.parse_structured_power_supply_info(data)
+ else:
+ self.facts["power_supply_info"] = self.parse_power_supply_info(
+ data
+ )
+
+ def parse_structured_interfaces(self, data):
+ objects = list()
+ data = data["TABLE_interface"]["ROW_interface"]
+ if isinstance(data, dict):
+ objects.append(data["interface"])
+ elif isinstance(data, list):
+ for item in data:
+ objects.append(item["interface"])
+ return objects
+
+ def parse_structured_vlans(self, data):
+ objects = list()
+ data = data["TABLE_vlanbriefxbrief"]["ROW_vlanbriefxbrief"]
+ if isinstance(data, dict):
+ objects.append(data["vlanshowbr-vlanid-utf"])
+ elif isinstance(data, list):
+ for item in data:
+ objects.append(item["vlanshowbr-vlanid-utf"])
+ return objects
+
+ def parse_structured_module(self, data):
+ data = data["TABLE_modinfo"]["ROW_modinfo"]
+ if isinstance(data, dict):
+ data = [data]
+ objects = list(self.transform_iterable(data, self.MODULE_MAP))
+ return objects
+
+ def parse_structured_fan_info(self, data):
+ objects = list()
+
+ for key in ("fandetails", "fandetails_3k"):
+ if data.get(key):
+ try:
+ data = data[key]["TABLE_faninfo"]["ROW_faninfo"]
+ except KeyError:
+ # Some virtual images don't actually report faninfo. In this case, move on and
+ # just return an empty list.
+ pass
+ else:
+ objects = list(self.transform_iterable(data, self.FAN_MAP))
+ break
+
+ return objects
+
+ def parse_structured_power_supply_info(self, data):
+ ps_data = data.get("powersup", {})
+ if ps_data.get("TABLE_psinfo_n3k"):
+ fact = ps_data["TABLE_psinfo_n3k"]["ROW_psinfo_n3k"]
+ else:
+ # {TABLE,ROW}_psinfo keys have been renamed to
+ # {TABLE,ROW}_ps_info in later NX-OS releases
+ tab_key, row_key = "TABLE_psinfo", "ROW_psinfo"
+ if tab_key not in ps_data:
+ tab_key, row_key = "TABLE_ps_info", "ROW_ps_info"
+
+ ps_tab_data = ps_data[tab_key]
+
+ if isinstance(ps_tab_data, list):
+ fact = []
+ for i in ps_tab_data:
+ fact.append(i[row_key])
+ else:
+ fact = ps_tab_data[row_key]
+
+ objects = list(self.transform_iterable(fact, self.POWERSUP_MAP))
+ return objects
+
+ def parse_hostname(self, data):
+ match = re.search(r"\s+Device name:\s+(\S+)", data, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_os(self, data):
+ match = re.search(r"\s+system:\s+version\s*(\S+)", data, re.M)
+ if match:
+ return match.group(1)
+ else:
+ match = re.search(r"\s+kickstart:\s+version\s*(\S+)", data, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_platform(self, data):
+ match = re.search(r"Hardware\n\s+cisco\s+(\S+\s+\S+)", data, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_interfaces(self, data):
+ objects = list()
+ for line in data.split("\n"):
+ if len(line) == 0:
+ continue
+ elif line.startswith("admin") or line[0] == " ":
+ continue
+ else:
+ match = re.match(r"^(\S+)", line)
+ if match:
+ intf = match.group(1)
+ if get_interface_type(intf) != "unknown":
+ objects.append(intf)
+ return objects
+
+ def parse_vlans(self, data):
+ objects = list()
+ for line in data.splitlines():
+ if line == "":
+ continue
+ if line[0].isdigit():
+ vlan = line.split()[0]
+ objects.append(vlan)
+ return objects
+
+ def parse_module(self, data):
+ objects = list()
+ for line in data.splitlines():
+ if line == "":
+ break
+ if line[0].isdigit():
+ obj = {}
+ match_port = re.search(r"\d\s*(\d*)", line, re.M)
+ if match_port:
+ obj["ports"] = match_port.group(1)
+
+ match = re.search(r"\d\s*\d*\s*(.+)$", line, re.M)
+ if match:
+ l = match.group(1).split(" ")
+ items = list()
+ for item in l:
+ if item == "":
+ continue
+ items.append(item.strip())
+
+ if items:
+ obj["type"] = items[0]
+ obj["model"] = items[1]
+ obj["status"] = items[2]
+
+ objects.append(obj)
+ return objects
+
+ def parse_fan_info(self, data):
+ objects = list()
+
+ for l in data.splitlines():
+ if "-----------------" in l or "Status" in l:
+ continue
+ line = l.split()
+ if len(line) > 1:
+ obj = {}
+ obj["name"] = line[0]
+ obj["model"] = line[1]
+ obj["hw_ver"] = line[-2]
+ obj["status"] = line[-1]
+ objects.append(obj)
+ return objects
+
+ def parse_power_supply_info(self, data):
+ objects = list()
+
+ for l in data.splitlines():
+ if l == "":
+ break
+ if l[0].isdigit():
+ obj = {}
+ line = l.split()
+ obj["model"] = line[1]
+ obj["number"] = line[0]
+ obj["status"] = line[-1]
+
+ objects.append(obj)
+ return objects
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/lldp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/lldp_global.py
new file mode 100644
index 00000000..d9898840
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_global/lldp_global.py
@@ -0,0 +1,113 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos lldp_global fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lldp_global.lldp_global import (
+ Lldp_globalArgs,
+)
+
+
+class Lldp_globalFacts(object):
+ """ The nxos lldp_global fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Lldp_globalArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for lldp_global
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+
+ if not data:
+ data = connection.get("show running-config | include lldp")
+
+ objs = {}
+ objs = self.render_config(self.generated_spec, data)
+ ansible_facts["ansible_network_resources"].pop("lldp_global", None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ facts["lldp_global"] = params["config"]
+ facts = utils.remove_empties(facts)
+ ansible_facts["ansible_network_resources"].update((facts))
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+ conf = re.split("\n", conf)
+ for command in conf:
+ param = re.search(
+ r"(.*)lldp (\w+(-?)\w+)", command
+ ) # get the word after 'lldp'
+ if param:
+ # get the nested-dict/value for that param
+ key2 = re.search(r"%s(.*)" % param.group(2), command)
+ key2 = key2.group(1).strip()
+ key1 = param.group(2).replace("-", "_")
+
+ if key1 == "portid_subtype":
+ key1 = "port_id"
+ config[key1] = key2
+ elif key1 == "tlv_select":
+ key2 = key2.split()
+ key2[0] = key2[0].replace("-", "_")
+ if len(key2) == 1:
+ if (
+ "port" in key2[0] or "system" in key2[0]
+ ): # nested dicts
+ key2 = key2[0].split("_")
+ # config[tlv_select][system][name]=False
+ config[key1][key2[0]][key2[1]] = False
+ else:
+ # config[tlv_select][dcbxp]=False
+ config[key1][key2[0]] = False
+ else:
+ # config[tlv_select][management_address][v6]=False
+ config[key1][key2[0]][key2[1]] = False
+ else:
+ config[key1] = key2 # config[reinit]=4
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py
new file mode 100644
index 00000000..ebf13bc1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/lldp_interfaces/lldp_interfaces.py
@@ -0,0 +1,129 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos lldp_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lldp_interfaces.lldp_interfaces import (
+ Lldp_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.utils import (
+ get_interface_type,
+)
+
+
+class Lldp_interfacesFacts(object):
+ """ The nxos lldp_interfaces fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Lldp_interfacesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_device_data(self, connection):
+ return connection.get("show running-config | section ^interface")
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for lldp_interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = self.get_device_data(connection)
+
+ objs = []
+
+ data = data.split("interface")
+ resources = []
+
+ for i in range(len(data)):
+ intf = data[i].split("\n")
+ for l in range(1, len(intf)):
+ if not re.search("lldp", intf[l]):
+ intf[l] = ""
+ intf = list(filter(None, intf))
+ intf = "".join(i for i in intf)
+ resources.append(intf)
+
+ for resource in resources:
+ if resource: # and re.search(r'lldp', resource):
+ obj = self.render_config(self.generated_spec, resource)
+ if obj and len(obj.keys()) >= 1:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("lldp_interfaces", None)
+ facts = {}
+ if objs:
+ facts["lldp_interfaces"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["lldp_interfaces"].append(utils.remove_empties(cfg))
+
+ ansible_facts["ansible_network_resources"].update(facts)
+
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+ match = re.search(r"^ (\S+)", conf)
+ if match is None:
+ return {}
+ intf = match.group(1)
+ if get_interface_type(intf) not in ["management", "ethernet"]:
+ return {}
+ config["name"] = intf
+ if "lldp receive" in conf: # for parsed state only
+ config["receive"] = True
+ if "no lldp receive" in conf:
+ config["receive"] = False
+
+ if "lldp transmit" in conf: # for parsed state only
+ config["transmit"] = True
+ if "no lldp transmit" in conf:
+ config["transmit"] = False
+ if "management-address" in conf:
+ config["tlv_set"]["management_address"] = re.search(
+ r"management-address (\S*)", conf
+ ).group(1)
+ if "vlan" in conf:
+ config["tlv_set"]["vlan"] = re.search(r"vlan (\S*)", conf).group(1)
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/ospf_interfaces.py
new file mode 100644
index 00000000..44726688
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospf_interfaces/ospf_interfaces.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+"""
+The nxos ospf_interfaces fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.ospf_interfaces import (
+ Ospf_interfacesTemplate,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.ospf_interfaces.ospf_interfaces import (
+ Ospf_interfacesArgs,
+)
+
+
+class Ospf_interfacesFacts(object):
+ """ The nxos ospf_interfaces facts class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Ospf_interfacesArgs.argument_spec
+
+ def get_config(self, connection):
+ """Wrapper method for `connection.get()`
+ This method exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return connection.get("show running-config | section '^interface'")
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for Ospf_interfaces network resource
+
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+
+ :rtype: dictionary
+ :returns: facts
+ """
+ facts = {}
+ objs = []
+
+ if not data:
+ data = self.get_config(connection)
+
+ # parse native config using the Ospf_interfaces template
+ ospf_interfaces_parser = Ospf_interfacesTemplate(
+ lines=data.splitlines()
+ )
+ objs = list(ospf_interfaces_parser.parse().values())
+ if objs:
+ for item in objs:
+ item["address_family"] = list(item["address_family"].values())
+ for af in item["address_family"]:
+ if af.get("processes"):
+ af["processes"] = list(af["processes"].values())
+
+ ansible_facts["ansible_network_resources"].pop("ospf_interfaces", None)
+
+ params = utils.remove_empties(
+ utils.validate_config(self.argument_spec, {"config": objs})
+ )
+
+ facts["ospf_interfaces"] = params.get("config", [])
+ ansible_facts["ansible_network_resources"].update(facts)
+
+ return ansible_facts
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/ospfv2.py
new file mode 100644
index 00000000..c0c58a3f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv2/ospfv2.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# 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
+
+"""
+The nxos snmp fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.ospfv2 import (
+ Ospfv2Template,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.ospfv2.ospfv2 import (
+ Ospfv2Args,
+)
+
+
+class Ospfv2Facts(object):
+ """ The nxos snmp fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Ospfv2Args.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_config(self, connection):
+ """Wrapper method for `connection.get()`
+ This method exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return connection.get(
+ "show running-config | section '^router ospf .*'"
+ )
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = self.get_config(connection)
+
+ ipv4 = {"processes": []}
+ for section in data.split("router "):
+ rmmod = Ospfv2Template(lines=section.splitlines())
+ entry = rmmod.parse()
+
+ if entry:
+ global_vals = entry.get("vrfs", {}).pop("vrf_", {})
+ for key, value in iteritems(global_vals):
+ entry[key] = value
+
+ if "vrfs" in entry:
+ entry["vrfs"] = list(entry["vrfs"].values())
+
+ for vrf in entry["vrfs"]:
+ if "areas" in vrf:
+ vrf["areas"] = list(vrf["areas"].values())
+
+ if "areas" in entry:
+ entry["areas"] = list(entry["areas"].values())
+
+ ipv4["processes"].append(entry)
+
+ ansible_facts["ansible_network_resources"].pop("ospfv2", None)
+ facts = {}
+ params = utils.validate_config(self.argument_spec, {"config": ipv4})
+ params = utils.remove_empties(params)
+
+ facts["ospfv2"] = params.get("config", [])
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/ospfv3.py
new file mode 100644
index 00000000..9704b864
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/ospfv3/ospfv3.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+"""
+The nxos ospfv3 fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from copy import deepcopy
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.rm_templates.ospfv3 import (
+ Ospfv3Template,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.ospfv3.ospfv3 import (
+ Ospfv3Args,
+)
+
+
+class Ospfv3Facts(object):
+ """ The nxos ospfv3 facts class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Ospfv3Args.argument_spec
+
+ def get_config(self, connection):
+ """Wrapper method for `connection.get()`
+ This method exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return connection.get("show running-config | section '^router ospfv3'")
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for Ospfv3 network resource
+
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = self.get_config(connection)
+
+ ipv6 = {"processes": []}
+ for section in data.split("router "):
+ rmmod = Ospfv3Template(lines=section.splitlines())
+ entry = rmmod.parse()
+
+ if entry:
+ global_vals = entry.get("vrfs", {}).pop("vrf_", {})
+ for key, value in iteritems(global_vals):
+ entry[key] = value
+
+ if "vrfs" in entry:
+ entry["vrfs"] = list(entry["vrfs"].values())
+
+ for vrf in entry["vrfs"]:
+ if "areas" in vrf:
+ vrf["areas"] = list(vrf["areas"].values())
+
+ if "areas" in entry:
+ entry["areas"] = list(entry["areas"].values())
+
+ if "address_family" in entry:
+ if "areas" in entry["address_family"]:
+ entry["address_family"]["areas"] = list(
+ entry["address_family"]["areas"].values()
+ )
+
+ ipv6["processes"].append(entry)
+
+ ansible_facts["ansible_network_resources"].pop("ospfv3", None)
+ facts = {}
+ params = utils.validate_config(self.argument_spec, {"config": ipv6})
+ params = utils.remove_empties(params)
+
+ facts["ospfv3"] = params.get("config", [])
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/static_routes.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/static_routes.py
new file mode 100644
index 00000000..e887fd5f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/static_routes/static_routes.py
@@ -0,0 +1,245 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The nxos static_routes fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.static_routes.static_routes import (
+ Static_routesArgs,
+)
+
+
+class Static_routesFacts(object):
+ """ The nxos static_routes fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = Static_routesArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_device_data(self, connection, data):
+ vrf_data = []
+ non_vrf_data = []
+ if not data:
+ non_vrf_data = connection.get(
+ "show running-config | include '^ip(v6)* route'"
+ )
+ vrf_data = connection.get(
+ "show running-config | section '^vrf context'"
+ )
+ if non_vrf_data:
+ non_vrf_data = non_vrf_data.split("\n")
+ else:
+ non_vrf_data = []
+ vrf_data = vrf_data.split("\nvrf context")
+ # as we split based on 'vrf context', it is stripped from the data except the first element
+ else:
+ # used for parsed state where data is from the 'running-config' key
+ data = data.split("\n")
+ i = 0
+ while i <= (len(data) - 1):
+ if "vrf context " in data[i]:
+ vrf_conf = data[i]
+ j = i + 1
+ while j < len(data) and "vrf context " not in data[j]:
+ vrf_conf += "\n" + data[j]
+ j += 1
+ i = j
+ vrf_data.append(vrf_conf)
+ else:
+ non_vrf_data.append(data[i])
+ i += 1
+
+ new_vrf_data = []
+ for v in vrf_data:
+ if re.search(r"\n\s*ip(v6)? route", v):
+ new_vrf_data.append(v)
+ # dont consider vrf if it does not have routes
+ for i in range(len(new_vrf_data)):
+ if not re.search("^vrf context", new_vrf_data[i]):
+ new_vrf_data[i] = "vrf context" + new_vrf_data[i]
+
+ resources = non_vrf_data + new_vrf_data
+ return resources
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for static_routes
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ resources = self.get_device_data(connection, data)
+ objs = self.render_config(self.generated_spec, resources)
+ ansible_facts["ansible_network_resources"].pop("static_routes", None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ params = utils.remove_empties(params)
+ for c in params["config"]:
+ if c == {"vrf": "default"}:
+ params["config"].remove(c)
+ facts["static_routes"] = params["config"]
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def get_inner_dict(self, conf, inner_dict):
+ """
+ This method parses the command to create the innermost dictionary of the config
+ """
+ conf = re.sub(r"\s*ip(v6)? route", "", conf)
+ # strip 'ip route'
+ inner_dict["dest"] = re.match(r"^\s*(\S+\/\d+) .*", conf).group(1)
+
+ # ethernet1/2/23
+ iface = re.match(
+ r".* (Ethernet|loopback|mgmt|port\-channel)(\S*) .*", conf
+ )
+ i = ["Ethernet", "loopback", "mgmt", "port-channel"]
+ if iface and iface.group(1) in i:
+ inner_dict["interface"] = (iface.group(1)) + (iface.group(2))
+ conf = re.sub(inner_dict["interface"], "", conf)
+
+ if "." in inner_dict["dest"]:
+ conf = re.sub(inner_dict["dest"], "", conf)
+ inner_dict["afi"] = "ipv4"
+ ipv4 = re.match(
+ r".* (\d+\.\d+\.\d+\.\d+\/?\d*).*", conf
+ ) # gets next hop ip
+ if ipv4:
+ inner_dict["forward_router_address"] = ipv4.group(1)
+ conf = re.sub(inner_dict["forward_router_address"], "", conf)
+ else:
+ inner_dict["afi"] = "ipv6"
+ conf = re.sub(inner_dict["dest"], "", conf)
+ ipv6 = re.match(r".* (\S*:\S*:\S*\/?\d*).*", conf)
+ if ipv6:
+ inner_dict["forward_router_address"] = ipv6.group(1)
+ conf = re.sub(inner_dict["forward_router_address"], "", conf)
+
+ nullif = re.search(r"null0", conf, re.IGNORECASE)
+ if nullif:
+ inner_dict["interface"] = "Null0"
+ inner_dict["forward_router_address"] = None
+ return inner_dict # dest IP not needed for null if
+
+ keywords = ["vrf", "name", "tag", "track"]
+ for key in keywords:
+ pattern = re.match(r".* (?:%s) (\S+).*" % key, conf)
+ if pattern:
+ if key == "vrf":
+ key = "dest_vrf"
+ elif key == "name":
+ key = "route_name"
+ inner_dict[key] = pattern.group(1).strip()
+ conf = re.sub(key + " " + inner_dict[key], "", conf)
+
+ pref = re.match(r"(?:.*) (\d+)$", conf)
+ if pref:
+ # if something is left at the end without any key, it is the pref
+ inner_dict["admin_distance"] = pref.group(1)
+ return inner_dict
+
+ def get_command(self, conf, afi_list, dest_list, af):
+ inner_dict = {}
+ inner_dict = self.get_inner_dict(conf, inner_dict)
+ if inner_dict["afi"] not in afi_list:
+ af.append({"afi": inner_dict["afi"], "routes": []})
+ afi_list.append(inner_dict["afi"])
+
+ next_hop = {}
+ params = [
+ "forward_router_address",
+ "interface",
+ "admin_distance",
+ "route_name",
+ "tag",
+ "track",
+ "dest_vrf",
+ ]
+ for p in params:
+ if p in inner_dict.keys():
+ next_hop.update({p: inner_dict[p]})
+
+ if inner_dict["dest"] not in dest_list:
+ dest_list.append(inner_dict["dest"])
+ af[-1]["routes"].append(
+ {"dest": inner_dict["dest"], "next_hops": []}
+ )
+ # if 'dest' is new, create new list under 'routes'
+ af[-1]["routes"][-1]["next_hops"].append(next_hop)
+ else:
+ af[-1]["routes"][-1]["next_hops"].append(next_hop)
+ # just append if dest already exists
+ return af
+
+ def render_config(self, spec, con):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ # config=deepcopy(spec)
+ config = []
+ global_afi_list = []
+ global_af = []
+ global_dest_list = []
+ if con:
+ for conf in con:
+ if conf.startswith("vrf context"):
+ svrf = re.match(r"vrf context (\S+)\n", conf).group(1)
+ afi_list = []
+ af = []
+ dest_list = []
+ config_dict = {"vrf": svrf, "address_families": []}
+ conf = conf.split("\n")
+ # considering from the second line as first line is 'vrf context..'
+ conf = conf[1:]
+ for c in conf:
+ if "ip route" in c or "ipv6 route" in c:
+ self.get_command(c, afi_list, dest_list, af)
+ config_dict["address_families"] = af
+ config.append(config_dict)
+ else:
+ if "ip route" in conf or "ipv6 route" in conf:
+ self.get_command(
+ conf, global_afi_list, global_dest_list, global_af
+ )
+ if global_af:
+ config.append(
+ utils.remove_empties({"address_families": global_af})
+ )
+ return config
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/telemetry.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/telemetry.py
new file mode 100644
index 00000000..47fdf118
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/telemetry/telemetry.py
@@ -0,0 +1,210 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos telemetry fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.telemetry.telemetry import (
+ TelemetryArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.cmdref.telemetry.telemetry import (
+ TMS_GLOBAL,
+ TMS_DESTGROUP,
+ TMS_SENSORGROUP,
+ TMS_SUBSCRIPTION,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.telemetry.telemetry import (
+ get_instance_data,
+ cr_key_lookup,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.utils.telemetry.telemetry import (
+ normalize_data,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ NxosCmdRef,
+)
+
+
+class TelemetryFacts(object):
+ """ The nxos telemetry fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = TelemetryArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for telemetry
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if connection: # just for linting purposes, remove
+ pass
+
+ cmd_ref = {}
+ cmd_ref["TMS_GLOBAL"] = {}
+ cmd_ref["TMS_DESTGROUP"] = {}
+ cmd_ref["TMS_SENSORGROUP"] = {}
+ cmd_ref["TMS_SUBSCRIPTION"] = {}
+
+ # For fact gathering, module state should be 'present' when using
+ # NxosCmdRef to query state
+ if self._module.params.get("state"):
+ saved_module_state = self._module.params["state"]
+ self._module.params["state"] = "present"
+
+ # Get Telemetry Global Data
+ cmd_ref["TMS_GLOBAL"]["ref"] = []
+ cmd_ref["TMS_GLOBAL"]["ref"].append(
+ NxosCmdRef(self._module, TMS_GLOBAL)
+ )
+ ref = cmd_ref["TMS_GLOBAL"]["ref"][0]
+ ref.set_context()
+ ref.get_existing()
+ device_cache = ref.cache_existing
+
+ if device_cache is None:
+ device_cache_lines = []
+ else:
+ device_cache_lines = device_cache.split("\n")
+
+ # Get Telemetry Destination Group Data
+ cmd_ref["TMS_DESTGROUP"]["ref"] = []
+ for line in device_cache_lines:
+ if re.search(r"destination-group", line):
+ resource_key = line.strip()
+ cmd_ref["TMS_DESTGROUP"]["ref"].append(
+ NxosCmdRef(self._module, TMS_DESTGROUP)
+ )
+ ref = cmd_ref["TMS_DESTGROUP"]["ref"][-1]
+ ref.set_context([resource_key])
+ ref.get_existing(device_cache)
+ normalize_data(ref)
+
+ # Get Telemetry Sensorgroup Group Data
+ cmd_ref["TMS_SENSORGROUP"]["ref"] = []
+ for line in device_cache_lines:
+ if re.search(r"sensor-group", line):
+ resource_key = line.strip()
+ cmd_ref["TMS_SENSORGROUP"]["ref"].append(
+ NxosCmdRef(self._module, TMS_SENSORGROUP)
+ )
+ ref = cmd_ref["TMS_SENSORGROUP"]["ref"][-1]
+ ref.set_context([resource_key])
+ ref.get_existing(device_cache)
+
+ # Get Telemetry Subscription Data
+ cmd_ref["TMS_SUBSCRIPTION"]["ref"] = []
+ for line in device_cache_lines:
+ if re.search(r"subscription", line):
+ resource_key = line.strip()
+ cmd_ref["TMS_SUBSCRIPTION"]["ref"].append(
+ NxosCmdRef(self._module, TMS_SUBSCRIPTION)
+ )
+ ref = cmd_ref["TMS_SUBSCRIPTION"]["ref"][-1]
+ ref.set_context([resource_key])
+ ref.get_existing(device_cache)
+
+ objs = []
+ objs = self.render_config(self.generated_spec, cmd_ref)
+ facts = {"telemetry": {}}
+ if objs:
+ # params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts["telemetry"] = objs
+
+ ansible_facts["ansible_network_resources"].update(facts)
+ if self._module.params.get("state"):
+ self._module.params["state"] = saved_module_state
+ return ansible_facts
+
+ def render_config(self, spec, cmd_ref):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+ config["destination_groups"] = []
+ config["sensor_groups"] = []
+ config["subscriptions"] = []
+ managed_objects = [
+ "TMS_GLOBAL",
+ "TMS_DESTGROUP",
+ "TMS_SENSORGROUP",
+ "TMS_SUBSCRIPTION",
+ ]
+
+ # Walk the argspec and cmd_ref objects and build out config dict.
+ for key in config.keys():
+ for mo in managed_objects:
+ for cr in cmd_ref[mo]["ref"]:
+ cr_keys = cr_key_lookup(key, mo)
+ for cr_key in cr_keys:
+ if cr._ref.get(cr_key) and cr._ref[cr_key].get(
+ "existing"
+ ):
+ if isinstance(config[key], dict):
+ for k in config[key].keys():
+ for existing_key in cr._ref[cr_key][
+ "existing"
+ ].keys():
+ config[key][k] = cr._ref[cr_key][
+ "existing"
+ ][existing_key][k]
+ continue
+ if isinstance(config[key], list):
+ for existing_key in cr._ref[cr_key][
+ "existing"
+ ].keys():
+ data = get_instance_data(
+ key, cr_key, cr, existing_key
+ )
+ config[key].append(data)
+ continue
+ for existing_key in cr._ref[cr_key][
+ "existing"
+ ].keys():
+ config[key] = cr._ref[cr_key]["existing"][
+ existing_key
+ ]
+ elif cr._ref.get(cr_key):
+ data = get_instance_data(key, cr_key, cr, None)
+ if (
+ isinstance(config[key], list)
+ and data not in config[key]
+ ):
+ config[key].append(data)
+
+ return utils.remove_empties(config)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/vlans.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/vlans.py
new file mode 100644
index 00000000..78951140
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/facts/vlans/vlans.py
@@ -0,0 +1,192 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python
+
+"""
+The nxos vlans fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+import ast
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ parse_conf_arg,
+ parse_conf_cmd_arg,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.vlans.vlans import (
+ VlansArgs,
+)
+
+
+class VlansFacts(object):
+ """ The nxos vlans fact class
+ """
+
+ def __init__(self, module, subspec="config", options="options"):
+ self._module = module
+ self.argument_spec = VlansArgs.argument_spec
+ spec = deepcopy(self.argument_spec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = utils.generate_dict(facts_argument_spec)
+
+ def get_device_data(self, connection, show_cmd):
+ """Wrapper method for `connection.get()`
+ This exists solely to allow the unit test framework to mock device connection calls.
+ """
+ return connection.get(show_cmd)
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for vlans
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ # **TBD**
+ # N7K EOL/legacy image 6.2 does not support show vlan | json output.
+ # If support is still required for this image then:
+ # - Wrapp the json calls below in a try/except
+ # - When excepted, use a helper method to parse the run_cfg_output,
+ # using the run_cfg_output data to generate compatible json data that
+ # can be read by normalize_table_data.
+ if not data:
+ # Use structured for most of the vlan parameter states.
+ # This data is consistent across the supported nxos platforms.
+ structured = self.get_device_data(connection, "show vlan | json")
+
+ # Raw cli config is needed for mapped_vni, which is not included in structured.
+ run_cfg_output = self.get_device_data(
+ connection, "show running-config | section ^vlan"
+ )
+ else:
+ running_config = data.split("\n\n")
+ structured, run_cfg_output = running_config[0], running_config[1]
+
+ # Create a single dictionary from all data sources
+ data = self.normalize_table_data(structured, run_cfg_output)
+
+ for vlan in data:
+ obj = self.render_config(self.generated_spec, vlan)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts["ansible_network_resources"].pop("vlans", None)
+ facts = {}
+ if objs:
+ facts["vlans"] = []
+ params = utils.validate_config(
+ self.argument_spec, {"config": objs}
+ )
+ for cfg in params["config"]:
+ facts["vlans"].append(utils.remove_empties(cfg))
+ ansible_facts["ansible_network_resources"].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, vlan):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+ :param spec: The facts tree, generated from the argspec
+ :param vlan: structured data vlan settings (dict) and raw cfg from device
+ :rtype: dictionary
+ :returns: The generated config
+ Sample inputs: test/units/modules/network/nxos/fixtures/nxos_vlans/show_vlan
+ """
+ obj = deepcopy(spec)
+
+ obj["vlan_id"] = vlan["vlan_id"]
+
+ # name: 'VLAN000x' (default name) or custom name
+ name = vlan["vlanshowbr-vlanname"]
+ if name and re.match("VLAN%04d" % int(vlan["vlan_id"]), name):
+ name = None
+ obj["name"] = name
+
+ # mode: 'ce-vlan' or 'fabricpath-vlan'
+ obj["mode"] = vlan["vlanshowinfo-vlanmode"].replace("-vlan", "")
+
+ # enabled: shutdown, noshutdown
+ obj["enabled"] = (
+ True if "noshutdown" in vlan["vlanshowbr-shutstate"] else False
+ )
+
+ # state: active, suspend
+ obj["state"] = vlan["vlanshowbr-vlanstate"]
+
+ # non-structured data
+ obj["mapped_vni"] = parse_conf_arg(vlan["run_cfg"], "vn-segment")
+
+ return utils.remove_empties(obj)
+
+ def normalize_table_data(self, structured, run_cfg_output):
+ """Normalize structured output and raw running-config output into
+ a single dict to simplify render_config usage.
+ This is needed because:
+ - The NXOS devices report most of the vlan settings within two
+ structured data keys: 'vlanbrief' and 'mtuinfo', but the output is
+ incomplete and therefore raw running-config data is also needed.
+ - running-config by itself is insufficient because of major differences
+ in the cli config syntax across platforms.
+ - Thus a helper method combines settings from the separate top-level keys,
+ and adds a 'run_cfg' key containing raw cli from the device.
+ """
+ # device output may be string, convert to list
+ structured = ast.literal_eval(str(structured))
+
+ vlanbrief = []
+ mtuinfo = []
+ if "TABLE_vlanbrief" in structured:
+ # SAMPLE: {"TABLE_vlanbriefid": {"ROW_vlanbriefid": {
+ # "vlanshowbr-vlanid": "4", "vlanshowbr-vlanid-utf": "4",
+ # "vlanshowbr-vlanname": "VLAN0004", "vlanshowbr-vlanstate": "active",
+ # "vlanshowbr-shutstate": "noshutdown"}},
+ vlanbrief = structured["TABLE_vlanbrief"]["ROW_vlanbrief"]
+
+ # SAMPLE: "TABLE_mtuinfoid": {"ROW_mtuinfoid": {
+ # "vlanshowinfo-vlanid": "4", "vlanshowinfo-media-type": "enet",
+ # "vlanshowinfo-vlanmode": "ce-vlan"}}
+ mtuinfo = structured["TABLE_mtuinfo"]["ROW_mtuinfo"]
+
+ if type(vlanbrief) is not list:
+ # vlanbrief is not a list when only one vlan is found.
+ vlanbrief = [vlanbrief]
+ mtuinfo = [mtuinfo]
+
+ # split out any per-vlan cli config
+ run_cfg_list = re.split(r"[\n^]vlan ", run_cfg_output)
+
+ # Create a list of vlan dicts where each dict contains vlanbrief,
+ # mtuinfo, and non-structured running-config data for one vlan.
+ vlans = []
+ for index, v in enumerate(vlanbrief):
+ v["vlan_id"] = v.get("vlanshowbr-vlanid-utf")
+ vlan = {}
+ vlan.update(v)
+ vlan.update(mtuinfo[index])
+
+ run_cfg = [
+ i for i in run_cfg_list if "%s\n" % v["vlan_id"] in i
+ ] or [""]
+ vlan["run_cfg"] = run_cfg.pop()
+ vlans.append(vlan)
+ return vlans
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/nxos.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/nxos.py
new file mode 100644
index 00000000..32ecc0d8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/nxos.py
@@ -0,0 +1,1501 @@
+#
+# 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, Red Hat Inc.
+#
+# 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 collections
+import json
+import re
+import sys
+from copy import deepcopy
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import env_fallback
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+ ComplexList,
+)
+from ansible.module_utils.connection import Connection, ConnectionError
+from ansible.module_utils.common._collections_compat import Mapping
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ NetworkConfig,
+ dumps,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+from ansible.module_utils.six import iteritems, PY2, PY3
+from ansible.module_utils.urls import fetch_url
+
+try:
+ import yaml
+
+ HAS_YAML = True
+except ImportError:
+ HAS_YAML = False
+
+try:
+ if sys.version_info[:2] < (2, 7):
+ from ordereddict import OrderedDict
+ else:
+ from collections import OrderedDict
+ HAS_ORDEREDDICT = True
+except ImportError:
+ HAS_ORDEREDDICT = False
+
+_DEVICE_CONNECTION = None
+
+nxos_provider_spec = {
+ "host": dict(type="str"),
+ "port": dict(type="int"),
+ "username": dict(
+ type="str", fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])
+ ),
+ "password": dict(
+ type="str",
+ no_log=True,
+ fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]),
+ ),
+ "ssh_keyfile": dict(
+ type="str", fallback=(env_fallback, ["ANSIBLE_NET_SSH_KEYFILE"])
+ ),
+ "authorize": dict(
+ type="bool",
+ default=False,
+ fallback=(env_fallback, ["ANSIBLE_NET_AUTHORIZE"]),
+ ),
+ "auth_pass": dict(
+ type="str",
+ no_log=True,
+ fallback=(env_fallback, ["ANSIBLE_NET_AUTH_PASS"]),
+ ),
+ "use_ssl": dict(type="bool", default=False),
+ "use_proxy": dict(type="bool", default=True),
+ "validate_certs": dict(type="bool", default=False),
+ "timeout": dict(type="int"),
+ "transport": dict(type="str", default="cli", choices=["cli", "nxapi"]),
+}
+nxos_argument_spec = {
+ "provider": dict(
+ type="dict",
+ options=nxos_provider_spec,
+ removed_at_date="2022-06-01",
+ removed_from_collection="cisco.nxos",
+ )
+}
+
+
+def get_provider_argspec():
+ return nxos_provider_spec
+
+
+def get_connection(module):
+ global _DEVICE_CONNECTION
+ if not _DEVICE_CONNECTION:
+ if is_local_nxapi(module):
+ conn = LocalNxapi(module)
+ else:
+ connection_proxy = Connection(module._socket_path)
+ cap = json.loads(connection_proxy.get_capabilities())
+ if cap["network_api"] == "cliconf":
+ conn = Cli(module)
+ elif cap["network_api"] == "nxapi":
+ conn = HttpApi(module)
+ _DEVICE_CONNECTION = conn
+ return _DEVICE_CONNECTION
+
+
+class Cli:
+ def __init__(self, module):
+ self._module = module
+ self._device_configs = {}
+ self._connection = None
+
+ def _get_connection(self):
+ if self._connection:
+ return self._connection
+ self._connection = Connection(self._module._socket_path)
+
+ return self._connection
+
+ def get_config(self, flags=None):
+ """Retrieves the current config from the device or cache
+ """
+ flags = [] if flags is None else flags
+
+ cmd = "show running-config "
+ cmd += " ".join(flags)
+ cmd = cmd.strip()
+
+ try:
+ return self._device_configs[cmd]
+ except KeyError:
+ connection = self._get_connection()
+ try:
+ out = connection.get_config(flags=flags)
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ cfg = to_text(out, errors="surrogate_then_replace").strip() + "\n"
+ self._device_configs[cmd] = cfg
+ return cfg
+
+ def run_commands(self, commands, check_rc=True):
+ """Run list of commands on remote device and return results
+ """
+ connection = self._get_connection()
+
+ try:
+ out = connection.run_commands(commands, check_rc)
+ if check_rc == "retry_json":
+ capabilities = self.get_capabilities()
+ network_api = capabilities.get("network_api")
+
+ if network_api == "cliconf" and out:
+ for index, resp in enumerate(out):
+ if (
+ "Invalid command at" in resp
+ or "Ambiguous command at" in resp
+ ) and "json" in resp:
+ if commands[index]["output"] == "json":
+ commands[index]["output"] = "text"
+ out = connection.run_commands(
+ commands, check_rc
+ )
+ return out
+ except ConnectionError as exc:
+ self._module.fail_json(msg=to_text(exc))
+
+ def load_config(self, config, return_error=False, opts=None, replace=None):
+ """Sends configuration commands to the remote device
+ """
+ if opts is None:
+ opts = {}
+
+ connection = self._get_connection()
+ responses = []
+ try:
+ resp = connection.edit_config(config, replace=replace)
+ if isinstance(resp, Mapping):
+ resp = resp["response"]
+ except ConnectionError as e:
+ code = getattr(e, "code", 1)
+ message = getattr(e, "err", e)
+ err = to_text(message, errors="surrogate_then_replace")
+ if opts.get("ignore_timeout") and code:
+ responses.append(err)
+ return responses
+ elif code and "no graceful-restart" in err:
+ if (
+ "ISSU/HA will be affected if Graceful Restart is disabled"
+ in err
+ ):
+ msg = [""]
+ responses.extend(msg)
+ return responses
+ else:
+ self._module.fail_json(msg=err)
+ elif code:
+ self._module.fail_json(msg=err)
+
+ responses.extend(resp)
+ return responses
+
+ def get_diff(
+ self,
+ candidate=None,
+ running=None,
+ diff_match="line",
+ diff_ignore_lines=None,
+ path=None,
+ diff_replace="line",
+ ):
+ conn = self._get_connection()
+ try:
+ response = conn.get_diff(
+ candidate=candidate,
+ running=running,
+ diff_match=diff_match,
+ diff_ignore_lines=diff_ignore_lines,
+ path=path,
+ diff_replace=diff_replace,
+ )
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+ return response
+
+ def get_capabilities(self):
+ """Returns platform info of the remove device
+ """
+ if hasattr(self._module, "_capabilities"):
+ return self._module._capabilities
+
+ connection = self._get_connection()
+ try:
+ capabilities = connection.get_capabilities()
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+ self._module._capabilities = json.loads(capabilities)
+ return self._module._capabilities
+
+ def read_module_context(self, module_key):
+ connection = self._get_connection()
+ try:
+ module_context = connection.read_module_context(module_key)
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ return module_context
+
+ def save_module_context(self, module_key, module_context):
+ connection = self._get_connection()
+ try:
+ connection.save_module_context(module_key, module_context)
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ return None
+
+
+class LocalNxapi:
+
+ OUTPUT_TO_COMMAND_TYPE = {
+ "text": "cli_show_ascii",
+ "json": "cli_show",
+ "bash": "bash",
+ "config": "cli_conf",
+ }
+
+ def __init__(self, module):
+ self._module = module
+ self._nxapi_auth = None
+ self._device_configs = {}
+ self._module_context = {}
+
+ provider = self._module.params.get("provider") or {}
+ self._module.params["url_username"] = provider.get("username")
+ self._module.params["url_password"] = provider.get("password")
+
+ host = provider.get("host")
+ port = provider.get("port")
+
+ if provider.get("use_ssl"):
+ proto = "https"
+ port = port or 443
+ else:
+ proto = "http"
+ port = port or 80
+
+ self._url = "%s://%s:%s/ins" % (proto, host, port)
+
+ def _error(self, msg, **kwargs):
+ self._nxapi_auth = None
+ if "url" not in kwargs:
+ kwargs["url"] = self._url
+ self._module.fail_json(msg=msg, **kwargs)
+
+ def _request_builder(
+ self, commands, output, version="1.0", chunk="0", sid=None
+ ):
+ """Encodes a NXAPI JSON request message
+ """
+ try:
+ command_type = self.OUTPUT_TO_COMMAND_TYPE[output]
+ except KeyError:
+ msg = "invalid format, received %s, expected one of %s" % (
+ output,
+ ",".join(self.OUTPUT_TO_COMMAND_TYPE.keys()),
+ )
+ self._error(msg=msg)
+
+ if isinstance(commands, (list, set, tuple)):
+ commands = " ;".join(commands)
+
+ # Order should not matter but some versions of NX-OS software fail
+ # to process the payload properly if 'input' gets serialized before
+ # 'type' and the payload of 'input' contains the word 'type'.
+ msg = collections.OrderedDict()
+ msg["version"] = version
+ msg["type"] = command_type
+ msg["chunk"] = chunk
+ msg["sid"] = sid
+ msg["input"] = commands
+ msg["output_format"] = "json"
+
+ return dict(ins_api=msg)
+
+ def send_request(
+ self,
+ commands,
+ output="text",
+ check_status=True,
+ return_error=False,
+ opts=None,
+ ):
+ # only 10 show commands can be encoded in each request
+ # messages sent to the remote device
+ if opts is None:
+ opts = {}
+ if output != "config":
+ commands = collections.deque(to_list(commands))
+ stack = list()
+ requests = list()
+
+ while commands:
+ stack.append(commands.popleft())
+ if len(stack) == 10:
+ body = self._request_builder(stack, output)
+ data = self._module.jsonify(body)
+ requests.append(data)
+ stack = list()
+
+ if stack:
+ body = self._request_builder(stack, output)
+ data = self._module.jsonify(body)
+ requests.append(data)
+
+ else:
+ body = self._request_builder(commands, "config")
+ requests = [self._module.jsonify(body)]
+
+ headers = {"Content-Type": "application/json"}
+ result = list()
+ timeout = self._module.params["provider"]["timeout"]
+ use_proxy = self._module.params["provider"]["use_proxy"]
+
+ for req in requests:
+ if self._nxapi_auth:
+ headers["Cookie"] = self._nxapi_auth
+
+ response, headers = fetch_url(
+ self._module,
+ self._url,
+ data=req,
+ headers=headers,
+ timeout=timeout,
+ method="POST",
+ use_proxy=use_proxy,
+ )
+ self._nxapi_auth = headers.get("set-cookie")
+
+ if opts.get("ignore_timeout") and re.search(
+ r"(-1|5\d\d)", str(headers["status"])
+ ):
+ result.append(headers["status"])
+ return result
+ elif headers["status"] != 200:
+ self._error(**headers)
+
+ try:
+ response = self._module.from_json(response.read())
+ except ValueError:
+ self._module.fail_json(msg="unable to parse response")
+
+ if response["ins_api"].get("outputs"):
+ output = response["ins_api"]["outputs"]["output"]
+ for item in to_list(output):
+ if check_status is True and item["code"] != "200":
+ if return_error:
+ result.append(item)
+ else:
+ self._error(output=output, **item)
+ elif "body" in item:
+ result.append(item["body"])
+ # else:
+ # error in command but since check_status is disabled
+ # silently drop it.
+ # result.append(item['msg'])
+
+ return result
+
+ def get_config(self, flags=None):
+ """Retrieves the current config from the device or cache
+ """
+ flags = [] if flags is None else flags
+
+ cmd = "show running-config "
+ cmd += " ".join(flags)
+ cmd = cmd.strip()
+
+ try:
+ return self._device_configs[cmd]
+ except KeyError:
+ out = self.send_request(cmd)
+ cfg = str(out[0]).strip()
+ self._device_configs[cmd] = cfg
+ return cfg
+
+ def run_commands(self, commands, check_rc=True):
+ """Run list of commands on remote device and return results
+ """
+ output = None
+ queue = list()
+ responses = list()
+
+ def _send(commands, output):
+ return self.send_request(commands, output, check_status=check_rc)
+
+ for item in to_list(commands):
+ if is_json(item["command"]):
+ item["command"] = str(item["command"]).rsplit("|", 1)[0]
+ item["output"] = "json"
+
+ if all((output == "json", item["output"] == "text")) or all(
+ (output == "text", item["output"] == "json")
+ ):
+ responses.extend(_send(queue, output))
+ queue = list()
+
+ output = item["output"] or "json"
+ queue.append(item["command"])
+
+ if queue:
+ responses.extend(_send(queue, output))
+
+ return responses
+
+ def load_config(
+ self, commands, return_error=False, opts=None, replace=None
+ ):
+ """Sends the ordered set of commands to the device
+ """
+
+ if opts is None:
+ opts = {}
+
+ responses = []
+
+ if replace:
+ device_info = self.get_device_info()
+ if "9K" not in device_info.get("network_os_platform", ""):
+ self._module.fail_json(
+ msg="replace is supported only on Nexus 9K devices"
+ )
+ commands = "config replace {0}".format(replace)
+
+ commands = to_list(commands)
+ try:
+ resp = self.send_request(
+ commands,
+ output="config",
+ check_status=True,
+ return_error=return_error,
+ opts=opts,
+ )
+ except ValueError as exc:
+ code = getattr(exc, "code", 1)
+ message = getattr(exc, "err", exc)
+ err = to_text(message, errors="surrogate_then_replace")
+ if opts.get("ignore_timeout") and code:
+ responses.append(code)
+ return responses
+ elif code and "no graceful-restart" in err:
+ if (
+ "ISSU/HA will be affected if Graceful Restart is disabled"
+ in err
+ ):
+ msg = [""]
+ responses.extend(msg)
+ return responses
+ else:
+ self._module.fail_json(msg=err)
+ elif code:
+ self._module.fail_json(msg=err)
+
+ if return_error:
+ return resp
+ else:
+ return responses.extend(resp)
+
+ def get_diff(
+ self,
+ candidate=None,
+ running=None,
+ diff_match="line",
+ diff_ignore_lines=None,
+ path=None,
+ diff_replace="line",
+ ):
+ diff = {}
+
+ # prepare candidate configuration
+ candidate_obj = NetworkConfig(indent=2)
+ candidate_obj.load(candidate)
+
+ if running and diff_match != "none" and diff_replace != "config":
+ # running configuration
+ running_obj = NetworkConfig(
+ indent=2, contents=running, ignore_lines=diff_ignore_lines
+ )
+ configdiffobjs = candidate_obj.difference(
+ running_obj, path=path, match=diff_match, replace=diff_replace
+ )
+
+ else:
+ configdiffobjs = candidate_obj.items
+
+ diff["config_diff"] = (
+ dumps(configdiffobjs, "commands") if configdiffobjs else ""
+ )
+ return diff
+
+ def get_device_info(self):
+ device_info = {}
+
+ device_info["network_os"] = "nxos"
+ reply = self.run_commands(
+ {"command": "show version", "output": "json"}
+ )
+ data = reply[0]
+
+ platform_reply = self.run_commands(
+ {"command": "show inventory", "output": "json"}
+ )
+ platform_info = platform_reply[0]
+
+ device_info["network_os_version"] = data.get(
+ "sys_ver_str"
+ ) or data.get("kickstart_ver_str")
+ device_info["network_os_model"] = data["chassis_id"]
+ device_info["network_os_hostname"] = data["host_name"]
+ device_info["network_os_image"] = data.get(
+ "isan_file_name"
+ ) or data.get("kick_file_name")
+
+ if platform_info:
+ inventory_table = platform_info["TABLE_inv"]["ROW_inv"]
+ for info in inventory_table:
+ if "Chassis" in info["name"]:
+ device_info["network_os_platform"] = info["productid"]
+
+ return device_info
+
+ def get_capabilities(self):
+ result = {}
+ result["device_info"] = self.get_device_info()
+ result["network_api"] = "nxapi"
+ return result
+
+ def read_module_context(self, module_key):
+ if self._module_context.get(module_key):
+ return self._module_context[module_key]
+
+ return None
+
+ def save_module_context(self, module_key, module_context):
+ self._module_context[module_key] = module_context
+
+ return None
+
+
+class HttpApi:
+ def __init__(self, module):
+ self._module = module
+ self._device_configs = {}
+ self._module_context = {}
+ self._connection_obj = None
+
+ @property
+ def _connection(self):
+ if not self._connection_obj:
+ self._connection_obj = Connection(self._module._socket_path)
+
+ return self._connection_obj
+
+ def run_commands(self, commands, check_rc=True):
+ """Runs list of commands on remote device and returns results
+ """
+ try:
+ out = self._connection.send_request(commands)
+ except ConnectionError as exc:
+ if check_rc is True:
+ raise
+ out = to_text(exc)
+
+ out = to_list(out)
+ if not out[0]:
+ return out
+
+ for index, response in enumerate(out):
+ if response[0] == "{":
+ out[index] = json.loads(response)
+
+ return out
+
+ def get_config(self, flags=None):
+ """Retrieves the current config from the device or cache
+ """
+ flags = [] if flags is None else flags
+
+ cmd = "show running-config "
+ cmd += " ".join(flags)
+ cmd = cmd.strip()
+
+ try:
+ return self._device_configs[cmd]
+ except KeyError:
+ try:
+ out = self._connection.send_request(cmd)
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ cfg = to_text(out).strip()
+ self._device_configs[cmd] = cfg
+ return cfg
+
+ def get_diff(
+ self,
+ candidate=None,
+ running=None,
+ diff_match="line",
+ diff_ignore_lines=None,
+ path=None,
+ diff_replace="line",
+ ):
+ diff = {}
+
+ # prepare candidate configuration
+ candidate_obj = NetworkConfig(indent=2)
+ candidate_obj.load(candidate)
+
+ if running and diff_match != "none" and diff_replace != "config":
+ # running configuration
+ running_obj = NetworkConfig(
+ indent=2, contents=running, ignore_lines=diff_ignore_lines
+ )
+ configdiffobjs = candidate_obj.difference(
+ running_obj, path=path, match=diff_match, replace=diff_replace
+ )
+
+ else:
+ configdiffobjs = candidate_obj.items
+
+ diff["config_diff"] = (
+ dumps(configdiffobjs, "commands") if configdiffobjs else ""
+ )
+ return diff
+
+ def load_config(
+ self, commands, return_error=False, opts=None, replace=None
+ ):
+ """Sends the ordered set of commands to the device
+ """
+ if opts is None:
+ opts = {}
+
+ responses = []
+ try:
+ resp = self.edit_config(commands, replace=replace)
+ except ConnectionError as exc:
+ code = getattr(exc, "code", 1)
+ message = getattr(exc, "err", exc)
+ err = to_text(message, errors="surrogate_then_replace")
+ if opts.get("ignore_timeout") and code:
+ responses.append(code)
+ return responses
+ elif opts.get("catch_clierror") and "400" in code:
+ return [code, err]
+ elif code and "no graceful-restart" in err:
+ if (
+ "ISSU/HA will be affected if Graceful Restart is disabled"
+ in err
+ ):
+ msg = [""]
+ responses.extend(msg)
+ return responses
+ else:
+ self._module.fail_json(msg=err)
+ elif code:
+ self._module.fail_json(msg=err)
+
+ responses.extend(resp)
+ return responses
+
+ def edit_config(
+ self, candidate=None, commit=True, replace=None, comment=None
+ ):
+ resp = list()
+
+ self.check_edit_config_capability(candidate, commit, replace, comment)
+
+ if replace:
+ candidate = "config replace {0}".format(replace)
+
+ responses = self._connection.send_request(candidate, output="config")
+ for response in to_list(responses):
+ if response != "{}":
+ resp.append(response)
+ if not resp:
+ resp = [""]
+
+ return resp
+
+ def get_capabilities(self):
+ """Returns platform info of the remove device
+ """
+ try:
+ capabilities = self._connection.get_capabilities()
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ return json.loads(capabilities)
+
+ def check_edit_config_capability(
+ self, candidate=None, commit=True, replace=None, comment=None
+ ):
+ operations = self._connection.get_device_operations()
+
+ if not candidate and not replace:
+ raise ValueError(
+ "must provide a candidate or replace to load configuration"
+ )
+
+ if commit not in (True, False):
+ raise ValueError("'commit' must be a bool, got %s" % commit)
+
+ if replace and not operations.get("supports_replace"):
+ raise ValueError("configuration replace is not supported")
+
+ if comment and not operations.get("supports_commit_comment", False):
+ raise ValueError("commit comment is not supported")
+
+ def read_module_context(self, module_key):
+ try:
+ module_context = self._connection.read_module_context(module_key)
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ return module_context
+
+ def save_module_context(self, module_key, module_context):
+ try:
+ self._connection.save_module_context(module_key, module_context)
+ except ConnectionError as exc:
+ self._module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ return None
+
+
+class NxosCmdRef:
+ """NXOS Command Reference utilities.
+ The NxosCmdRef class takes a yaml-formatted string of nxos module commands
+ and converts it into dict-formatted database of getters/setters/defaults
+ and associated common and platform-specific values. The utility methods
+ add additional data such as existing states, playbook states, and proposed cli.
+ The utilities also abstract away platform differences such as different
+ defaults and different command syntax.
+
+ Callers must provide a yaml formatted string that defines each command and
+ its properties; e.g. BFD global:
+ ---
+ _template: # _template holds common settings for all commands
+ # Enable feature bfd if disabled
+ feature: bfd
+ # Common getter syntax for BFD commands
+ get_command: show run bfd all | incl '^(no )*bfd'
+
+ interval:
+ kind: dict
+ getval: bfd interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
+ setval: bfd interval {tx} min_rx {min_rx} multiplier {multiplier}
+ default:
+ tx: 50
+ min_rx: 50
+ multiplier: 3
+ N3K:
+ # Platform overrides
+ default:
+ tx: 250
+ min_rx: 250
+ multiplier: 3
+ """
+
+ def __init__(self, module, cmd_ref_str, ref_only=False):
+ """Initialize cmd_ref from yaml data."""
+
+ self._module = module
+ self._check_imports()
+ self._yaml_load(cmd_ref_str)
+ self.cache_existing = None
+ self.present_states = ["present", "merged", "replaced"]
+ self.absent_states = ["absent", "deleted"]
+ ref = self._ref
+
+ # Create a list of supported commands based on ref keys
+ ref["commands"] = sorted([k for k in ref if not k.startswith("_")])
+ ref["_proposed"] = []
+ ref["_context"] = []
+ ref["_resource_key"] = None
+
+ if not ref_only:
+ ref["_state"] = module.params.get("state", "present")
+ self.feature_enable()
+ self.get_platform_defaults()
+ self.normalize_defaults()
+
+ def __getitem__(self, key=None):
+ if key is None:
+ return self._ref
+ return self._ref[key]
+
+ def _check_imports(self):
+ module = self._module
+ msg = nxosCmdRef_import_check()
+ if msg:
+ module.fail_json(msg=msg)
+
+ def _yaml_load(self, cmd_ref_str):
+ if PY2:
+ self._ref = yaml.load(cmd_ref_str)
+ elif PY3:
+ self._ref = yaml.load(cmd_ref_str, Loader=yaml.FullLoader)
+
+ def feature_enable(self):
+ """Add 'feature <foo>' to _proposed if ref includes a 'feature' key. """
+ ref = self._ref
+ feature = ref["_template"].get("feature")
+ if feature:
+ show_cmd = "show run | incl 'feature {0}'".format(feature)
+ output = self.execute_show_command(show_cmd, "text")
+ if not output or "CLI command error" in output:
+ msg = "** 'feature {0}' is not enabled. Module will auto-enable feature {0} ** ".format(
+ feature
+ )
+ self._module.warn(msg)
+ ref["_proposed"].append("feature {0}".format(feature))
+ ref["_cli_is_feature_disabled"] = ref["_proposed"]
+
+ def get_platform_shortname(self):
+ """Query device for platform type, normalize to a shortname/nickname.
+ Returns platform shortname (e.g. 'N3K-3058P' returns 'N3K') or None.
+ """
+ # TBD: add this method logic to get_capabilities() after those methods
+ # are made consistent across transports
+ platform_info = self.execute_show_command("show inventory", "json")
+ if not platform_info or not isinstance(platform_info, dict):
+ return None
+ inventory_table = platform_info["TABLE_inv"]["ROW_inv"]
+ for info in inventory_table:
+ if "Chassis" in info["name"]:
+ network_os_platform = info["productid"]
+ break
+ else:
+ return None
+
+ # Supported Platforms: N3K,N5K,N6K,N7K,N9K,N3K-F,N9K-F
+ m = re.match(
+ "(?P<short>N[35679][K57])-(?P<N35>C35)*", network_os_platform
+ )
+ if not m:
+ return None
+ shortname = m.group("short")
+
+ # Normalize
+ if m.groupdict().get("N35"):
+ shortname = "N35"
+ elif re.match("N77", shortname):
+ shortname = "N7K"
+ elif re.match(r"N3K|N9K", shortname):
+ for info in inventory_table:
+ if "-R" in info["productid"]:
+ # Fretta Platform
+ shortname += "-F"
+ break
+ return shortname
+
+ def get_platform_defaults(self):
+ """Update ref with platform specific defaults"""
+ plat = self.get_platform_shortname()
+ if not plat:
+ return
+
+ ref = self._ref
+ ref["_platform_shortname"] = plat
+ # Remove excluded commands (no platform support for command)
+ for k in ref["commands"]:
+ if plat in ref[k].get("_exclude", ""):
+ ref["commands"].remove(k)
+
+ # Update platform-specific settings for each item in ref
+ plat_spec_cmds = [k for k in ref["commands"] if plat in ref[k]]
+ for k in plat_spec_cmds:
+ for plat_key in ref[k][plat]:
+ ref[k][plat_key] = ref[k][plat][plat_key]
+
+ def normalize_defaults(self):
+ """Update ref defaults with normalized data"""
+ ref = self._ref
+ for k in ref["commands"]:
+ if "default" in ref[k] and ref[k]["default"]:
+ kind = ref[k]["kind"]
+ if "int" == kind:
+ ref[k]["default"] = int(ref[k]["default"])
+ elif "list" == kind:
+ ref[k]["default"] = [str(i) for i in ref[k]["default"]]
+ elif "dict" == kind:
+ for key, v in ref[k]["default"].items():
+ if v:
+ v = str(v)
+ ref[k]["default"][key] = v
+
+ def execute_show_command(self, command, format):
+ """Generic show command helper.
+ Warning: 'CLI command error' exceptions are caught, must be handled by caller.
+ Return device output as a newline-separated string or None.
+ """
+ cmds = [{"command": command, "output": format}]
+ output = None
+ try:
+ output = run_commands(self._module, cmds)
+ if output:
+ output = output[0]
+ except ConnectionError as exc:
+ if "CLI command error" in repr(exc):
+ # CLI may be feature disabled
+ output = repr(exc)
+ else:
+ raise
+ return output
+
+ def pattern_match_existing(self, output, k):
+ """Pattern matching helper for `get_existing`.
+ `k` is the command name string. Use the pattern from cmd_ref to
+ find a matching string in the output.
+ Return regex match object or None.
+ """
+ ref = self._ref
+ pattern = re.compile(ref[k]["getval"])
+ multiple = "multiple" in ref[k].keys()
+ match_lines = [re.search(pattern, line) for line in output]
+ if "dict" == ref[k]["kind"]:
+ match = [m for m in match_lines if m]
+ if not match:
+ return None
+ if len(match) > 1 and not multiple:
+ raise ValueError(
+ "get_existing: multiple matches found for property {0}".format(
+ k
+ )
+ )
+ else:
+ match = [m.groups() for m in match_lines if m]
+ if not match:
+ return None
+ if len(match) > 1 and not multiple:
+ raise ValueError(
+ "get_existing: multiple matches found for property {0}".format(
+ k
+ )
+ )
+ for item in match:
+ index = match.index(item)
+ match[index] = list(item) # tuple to list
+
+ # Handle config strings that nvgen with the 'no' prefix.
+ # Example match behavior:
+ # When pattern is: '(no )*foo *(\S+)*$' AND
+ # When output is: 'no foo' -> match: ['no ', None]
+ # When output is: 'foo 50' -> match: [None, '50']
+ if None is match[index][0]:
+ match[index].pop(0)
+ elif "no" in match[index][0]:
+ match[index].pop(0)
+ if not match:
+ return None
+
+ return match
+
+ def set_context(self, context=None):
+ """Update ref with command context.
+ """
+ if context is None:
+ context = []
+ ref = self._ref
+ # Process any additional context that this propoerty might require.
+ # 1) Global context from NxosCmdRef _template.
+ # 2) Context passed in using context arg.
+ ref["_context"] = ref["_template"].get("context", [])
+ for cmd in context:
+ ref["_context"].append(cmd)
+ # Last key in context is the resource key
+ ref["_resource_key"] = context[-1] if context else ref["_resource_key"]
+
+ def get_existing(self, cache_output=None):
+ """Update ref with existing command states from the device.
+ Store these states in each command's 'existing' key.
+ """
+ ref = self._ref
+ if ref.get("_cli_is_feature_disabled"):
+ # Add context to proposed if state is present
+ if ref["_state"] in self.present_states:
+ [ref["_proposed"].append(ctx) for ctx in ref["_context"]]
+ return
+
+ show_cmd = ref["_template"]["get_command"]
+ if cache_output:
+ output = cache_output
+ else:
+ output = self.execute_show_command(show_cmd, "text") or []
+ self.cache_existing = output
+
+ # Add additional command context if needed.
+ if ref["_context"]:
+ output = CustomNetworkConfig(indent=2, contents=output)
+ output = output.get_section(ref["_context"])
+
+ if not output:
+ # Add context to proposed if state is present
+ if ref["_state"] in self.present_states:
+ [ref["_proposed"].append(ctx) for ctx in ref["_context"]]
+ return
+
+ # We need to remove the last item in context for state absent case.
+ if ref["_state"] in self.absent_states and ref["_context"]:
+ if (
+ ref["_resource_key"]
+ and ref["_resource_key"] == ref["_context"][-1]
+ ):
+ if ref["_context"][-1] in output:
+ ref["_context"][-1] = "no " + ref["_context"][-1]
+ else:
+ del ref["_context"][-1]
+ return
+
+ # Walk each cmd in ref, use cmd pattern to discover existing cmds
+ output = output.split("\n")
+ for k in ref["commands"]:
+ match = self.pattern_match_existing(output, k)
+ if not match:
+ continue
+ ref[k]["existing"] = {}
+ for item in match:
+ index = match.index(item)
+ kind = ref[k]["kind"]
+ if "int" == kind:
+ ref[k]["existing"][index] = int(item[0])
+ elif "list" == kind:
+ ref[k]["existing"][index] = [str(i) for i in item[0]]
+ elif "dict" == kind:
+ # The getval pattern should contain regex named group keys that
+ # match up with the setval named placeholder keys; e.g.
+ # getval: my-cmd (?P<foo>\d+) bar (?P<baz>\d+)
+ # setval: my-cmd {foo} bar {baz}
+ ref[k]["existing"][index] = {}
+ for key in item.groupdict().keys():
+ ref[k]["existing"][index][key] = str(item.group(key))
+ elif "str" == kind:
+ ref[k]["existing"][index] = item[0]
+ else:
+ raise ValueError(
+ "get_existing: unknown 'kind' value specified for key '{0}'".format(
+ k
+ )
+ )
+
+ def get_playvals(self):
+ """Update ref with values from the playbook.
+ Store these values in each command's 'playval' key.
+ """
+ ref = self._ref
+ module = self._module
+ params = {}
+ if module.params.get("config"):
+ # Resource module builder packs playvals under 'config' key
+ param_data = module.params.get("config")
+ params["global"] = param_data
+ for key in param_data.keys():
+ if isinstance(param_data[key], list):
+ params[key] = param_data[key]
+ else:
+ params["global"] = module.params
+ for k in ref.keys():
+ for level in params.keys():
+ if isinstance(params[level], dict):
+ params[level] = [params[level]]
+ for item in params[level]:
+ if k in item and item[k] is not None:
+ if not ref[k].get("playval"):
+ ref[k]["playval"] = {}
+ playval = item[k]
+ index = params[level].index(item)
+ # Normalize each value
+ if "int" == ref[k]["kind"]:
+ playval = int(playval)
+ elif "list" == ref[k]["kind"]:
+ playval = [str(i) for i in playval]
+ elif "dict" == ref[k]["kind"]:
+ for key, v in playval.items():
+ playval[key] = str(v)
+ ref[k]["playval"][index] = playval
+
+ def build_cmd_set(self, playval, existing, k):
+ """Helper function to create list of commands to configure device
+ Return a list of commands
+ """
+ ref = self._ref
+ proposed = ref["_proposed"]
+ cmd = None
+ kind = ref[k]["kind"]
+ if "int" == kind:
+ cmd = ref[k]["setval"].format(playval)
+ elif "list" == kind:
+ cmd = ref[k]["setval"].format(*(playval))
+ elif "dict" == kind:
+ # The setval pattern should contain placeholder keys that
+ # match up with the getval regex named group keys; e.g.
+ # getval: my-cmd (?P<foo>\d+) bar (?P<baz>\d+)
+ # setval: my-cmd {foo} bar {baz}
+ cmd = ref[k]["setval"].format(**playval)
+ elif "str" == kind:
+ if "deleted" in playval:
+ if existing:
+ cmd = "no " + ref[k]["setval"].format(existing)
+ else:
+ cmd = ref[k]["setval"].format(playval)
+ else:
+ raise ValueError(
+ "get_proposed: unknown 'kind' value specified for key '{0}'".format(
+ k
+ )
+ )
+ if cmd:
+ if ref["_state"] in self.absent_states and not re.search(
+ r"^no", cmd
+ ):
+ cmd = "no " + cmd
+ # Commands may require parent commands for proper context.
+ # Global _template context is replaced by parameter context
+ [proposed.append(ctx) for ctx in ref["_context"]]
+ [proposed.append(ctx) for ctx in ref[k].get("context", [])]
+ proposed.append(cmd)
+
+ def get_proposed(self):
+ """Compare playbook values against existing states and create a list
+ of proposed commands.
+ Return a list of raw cli command strings.
+ """
+ ref = self._ref
+ # '_proposed' may be empty list or contain initializations; e.g. ['feature foo']
+ proposed = ref["_proposed"]
+
+ if ref["_context"] and ref["_context"][-1].startswith("no"):
+ [proposed.append(ctx) for ctx in ref["_context"]]
+ return proposed
+
+ # Create a list of commands that have playbook values
+ play_keys = [k for k in ref["commands"] if "playval" in ref[k]]
+
+ def compare(playval, existing):
+ if ref["_state"] in self.present_states:
+ if existing is None:
+ return False
+ elif playval == existing:
+ return True
+ elif (
+ isinstance(existing, dict) and playval in existing.values()
+ ):
+ return True
+
+ if ref["_state"] in self.absent_states:
+ if isinstance(existing, dict) and all(
+ x is None for x in existing.values()
+ ):
+ existing = None
+ if existing is None or playval not in existing.values():
+ return True
+ return False
+
+ # Compare against current state
+ for k in play_keys:
+ playval = ref[k]["playval"]
+ # Create playval copy to avoid RuntimeError
+ # dictionary changed size during iteration error
+ playval_copy = deepcopy(playval)
+ existing = ref[k].get("existing", ref[k]["default"])
+ multiple = "multiple" in ref[k].keys()
+
+ # Multiple Instances:
+ if isinstance(existing, dict) and multiple:
+ for ekey, evalue in existing.items():
+ if isinstance(evalue, dict):
+ # Remove values set to string 'None' from dvalue
+ evalue = dict(
+ (k, v) for k, v in evalue.items() if v != "None"
+ )
+ for pkey, pvalue in playval.items():
+ if compare(pvalue, evalue):
+ if playval_copy.get(pkey):
+ del playval_copy[pkey]
+ if not playval_copy:
+ continue
+ # Single Instance:
+ else:
+ for pkey, pval in playval.items():
+ if compare(pval, existing):
+ if playval_copy.get(pkey):
+ del playval_copy[pkey]
+ if not playval_copy:
+ continue
+
+ playval = playval_copy
+ # Multiple Instances:
+ if isinstance(existing, dict):
+ for dkey, dvalue in existing.items():
+ for pval in playval.values():
+ self.build_cmd_set(pval, dvalue, k)
+ # Single Instance:
+ else:
+ for pval in playval.values():
+ self.build_cmd_set(pval, existing, k)
+
+ # Remove any duplicate commands before returning.
+ # pylint: disable=unnecessary-lambda
+ cmds = sorted(set(proposed), key=lambda x: proposed.index(x))
+ return cmds
+
+
+def nxosCmdRef_import_check():
+ """Return import error messages or empty string"""
+ msg = ""
+ if PY2:
+ if not HAS_ORDEREDDICT and sys.version_info[:2] < (2, 7):
+ msg += "Mandatory python library 'ordereddict' is not present, try 'pip install ordereddict'\n"
+ if not HAS_YAML:
+ msg += "Mandatory python library 'yaml' is not present, try 'pip install yaml'\n"
+ elif PY3:
+ if not HAS_YAML:
+ msg += "Mandatory python library 'PyYAML' is not present, try 'pip install PyYAML'\n"
+ return msg
+
+
+def is_json(cmd):
+ return to_text(cmd).endswith("| json")
+
+
+def is_text(cmd):
+ return not is_json(cmd)
+
+
+def is_local_nxapi(module):
+ provider = module.params.get("provider")
+ if provider:
+ return provider.get("transport") == "nxapi"
+ return False
+
+
+def to_command(module, commands):
+ if is_local_nxapi(module):
+ default_output = "json"
+ else:
+ default_output = "text"
+
+ transform = ComplexList(
+ dict(
+ command=dict(key=True),
+ output=dict(default=default_output),
+ prompt=dict(type="list"),
+ answer=dict(type="list"),
+ newline=dict(type="bool", default=True),
+ sendonly=dict(type="bool", default=False),
+ check_all=dict(type="bool", default=False),
+ ),
+ module,
+ )
+
+ commands = transform(to_list(commands))
+
+ for item in commands:
+ if is_json(item["command"]):
+ item["output"] = "json"
+
+ return commands
+
+
+def get_config(module, flags=None):
+ flags = [] if flags is None else flags
+
+ conn = get_connection(module)
+ return conn.get_config(flags=flags)
+
+
+def run_commands(module, commands, check_rc=True):
+ conn = get_connection(module)
+ return conn.run_commands(to_command(module, commands), check_rc)
+
+
+def load_config(module, config, return_error=False, opts=None, replace=None):
+ conn = get_connection(module)
+ return conn.load_config(config, return_error, opts, replace=replace)
+
+
+def get_capabilities(module):
+ conn = get_connection(module)
+ return conn.get_capabilities()
+
+
+def get_diff(
+ self,
+ candidate=None,
+ running=None,
+ diff_match="line",
+ diff_ignore_lines=None,
+ path=None,
+ diff_replace="line",
+):
+ conn = self.get_connection()
+ return conn.get_diff(
+ candidate=candidate,
+ running=running,
+ diff_match=diff_match,
+ diff_ignore_lines=diff_ignore_lines,
+ path=path,
+ diff_replace=diff_replace,
+ )
+
+
+def normalize_interface(name):
+ """Return the normalized interface name
+ """
+ if not name:
+ return
+
+ def _get_number(name):
+ digits = ""
+ for char in name:
+ if char.isdigit() or char in "/.":
+ digits += char
+ return digits
+
+ if name.lower().startswith("et"):
+ if_type = "Ethernet"
+ elif name.lower().startswith("vl"):
+ if_type = "Vlan"
+ elif name.lower().startswith("lo"):
+ if_type = "loopback"
+ elif name.lower().startswith("po"):
+ if_type = "port-channel"
+ elif name.lower().startswith("nv"):
+ if_type = "nve"
+ else:
+ if_type = None
+
+ number_list = name.split(" ")
+ if len(number_list) == 2:
+ number = number_list[-1].strip()
+ else:
+ number = _get_number(name)
+
+ if if_type:
+ proper_interface = if_type + number
+ else:
+ proper_interface = name
+
+ return proper_interface
+
+
+def get_interface_type(interface):
+ """Gets the type of interface
+ """
+ if interface.upper().startswith("ET"):
+ return "ethernet"
+ elif interface.upper().startswith("VL"):
+ return "svi"
+ elif interface.upper().startswith("LO"):
+ return "loopback"
+ elif interface.upper().startswith("MG"):
+ return "management"
+ elif interface.upper().startswith("MA"):
+ return "management"
+ elif interface.upper().startswith("PO"):
+ return "portchannel"
+ elif interface.upper().startswith("NV"):
+ return "nve"
+ else:
+ return "unknown"
+
+
+def default_intf_enabled(name="", sysdefs=None, mode=None):
+ """Get device/version/interface-specific default 'enabled' state.
+ L3:
+ - Most L3 intfs default to 'shutdown'. Loopbacks default to 'no shutdown'.
+ - Some legacy platforms default L3 intfs to 'no shutdown'.
+ L2:
+ - User-System-Default 'system default switchport shutdown' defines the
+ enabled state for L2 intf's. USD defaults may be different on some platforms.
+ - An intf may be explicitly defined as L2 with 'switchport' or it may be
+ implicitly defined as L2 when USD 'system default switchport' is defined.
+ """
+ if not name:
+ return None
+ if sysdefs is None:
+ sysdefs = {}
+ default = False
+
+ if re.search("port-channel|loopback", name):
+ default = True
+ else:
+ if mode is None:
+ # intf 'switchport' cli is not present so use the user-system-default
+ mode = sysdefs.get("mode")
+
+ if mode == "layer3":
+ default = sysdefs.get("L3_enabled")
+ elif mode == "layer2":
+ default = sysdefs.get("L2_enabled")
+ return default
+
+
+def read_module_context(module):
+ conn = get_connection(module)
+ return conn.read_module_context(module._name)
+
+
+def save_module_context(module, module_context):
+ conn = get_connection(module)
+ return conn.save_module_context(module._name, module_context)
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/bgp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/bgp_global.py
new file mode 100644
index 00000000..eeab8592
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/bgp_global.py
@@ -0,0 +1,1455 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The Bgp_global parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import (
+ NetworkTemplate,
+)
+
+
+def _tmplt_confederation_peers(proc):
+ cmd = "confederation peers"
+ for peer in proc.get("confederation", {})["peers"]:
+ cmd += " {0}".format(peer)
+ return cmd
+
+
+def _tmplt_path_attribute(proc):
+ cmd = "path-attribute {action}".format(**proc)
+
+ if "type" in proc:
+ cmd += " {type}".format(**proc)
+ elif "range" in proc:
+ cmd += " range {start} {end}".format(**proc["range"])
+ cmd += " in"
+
+ return cmd
+
+
+class Bgp_globalTemplate(NetworkTemplate):
+ def __init__(self, lines=None):
+ super(Bgp_globalTemplate, self).__init__(lines=lines, tmplt=self)
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "as_number",
+ "getval": re.compile(
+ r"""
+ ^router\sbgp\s(?P<as_number>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "router bgp {{ as_number }}",
+ "result": {
+ "as_number": "{{ as_number }}",
+ },
+ "shared": True
+ },
+ {
+ "name": "vrf",
+ "getval": re.compile(
+ r"""
+ \s+vrf
+ \s(?P<vrf>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf {{ vrf }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "vrf": "{{ vrf }}"
+ }
+ }
+ },
+ "shared": True,
+ },
+ {
+ "name": "affinity_group.group_id",
+ "getval": re.compile(
+ r"""
+ \s+affinity-group
+ \sactivate\s(?P<group_id>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "affinity-group activate {{ affinity_group.group_id }}",
+ "result": {
+ "affinity_group": {
+ "group_id": "{{ group_id }}",
+ },
+ }
+ },
+ {
+ "name": "bestpath.always_compare_med",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\s(?P<always_compare_med>always-compare-med)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath always-compare-med",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "always_compare_med": "{{ not not always_compare_med }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.as_path.ignore",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\sas-path\s(?P<ignore>ignore)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath as-path ignore",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "as_path": {
+ "ignore": "{{ not not ignore }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.as_path.multipath_relax",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\sas-path\s(?P<multipath_relax>multipath-relax)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath as-path multipath-relax",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "as_path": {
+ "multipath_relax": "{{ not not multipath_relax }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.compare_neighborid",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\s(?P<compare_neighborid>compare-neighborid)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath compare-neighborid",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "compare_neighborid": "{{ not not compare_neighborid }}",
+ }
+ }
+ }
+
+ }
+ },
+ {
+ "name": "bestpath.compare_routerid",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\s(?P<compare_routerid>compare-routerid)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath compare-routerid",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "compare_routerid": "{{ not not compare_routerid }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.cost_community_ignore",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\scost-community\s(?P<cost_community_ignore>ignore)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath cost-community ignore",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "cost_community_ignore": "{{ not not cost_community_ignore }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.igp_metric_ignore",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\sigp-metric\s(?P<igp_metric_ignore>ignore)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath igp-metric ignore",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "igp_metric_ignore": "{{ not not igp_metric_ignore }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.med.confed",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\smed\s(?P<confed>confed)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath med confed",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "med": {
+ "confed": "{{ not not confed }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.med.missing_as_worst",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\smed\s(?P<missing_as_worst>missing-as-worst)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath med missing-as-worst",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "med": {
+ "missing_as_worst": "{{ not not missing_as_worst }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bestpath.med.non_deterministic",
+ "getval": re.compile(
+ r"""
+ \s+bestpath\smed\s(?P<non_deterministic>non-deterministic)
+ $""", re.VERBOSE
+ ),
+ "setval": "bestpath med non-deterministic",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bestpath": {
+ "med": {
+ "non_deterministic": "{{ not not non_deterministic }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "cluster_id",
+ "getval": re.compile(
+ r"""
+ \s+cluster-id\s(?P<cluster_id>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "cluster-id {{ cluster_id }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "cluster_id": "{{ cluster_id }}",
+ }
+ }
+ }
+ },
+ {
+ "name": "confederation.identifier",
+ "getval": re.compile(
+ r"""
+ \s+confederation\sidentifier\s(?P<identifier>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "confederation identifier {{ confederation.identifier }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "confederation": {
+ "identifier": "{{ identifier }}",
+ },
+ }
+ }
+ },
+ },
+ {
+ "name": "confederation.peers",
+ "getval": re.compile(
+ r"""
+ \s+confederation\speers\s(?P<peers>.*)
+ $""", re.VERBOSE
+ ),
+ "setval": _tmplt_confederation_peers,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "confederation": {
+ "peers": "{{ peers }}",
+ },
+ }
+ },
+ }
+ },
+ {
+ "name": "disable_policy_batching",
+ "getval": re.compile(
+ r"""
+ \s+(?P<disable_policy_batching>disable-policy-batching)
+ $""", re.VERBOSE
+ ),
+ "setval": "disable-policy-batching",
+ "result": {
+ "disable_policy_batching": {
+ "set": "{{ not not disable_policy_batching }}",
+ },
+ }
+ },
+ {
+ "name": "disable_policy_batching.ipv4.prefix_list",
+ "getval": re.compile(
+ r"""
+ \s+disable-policy-batching\sipv4
+ \sprefix-list\s(?P<ipv4_prefix_list>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "disable-policy-batching ipv4 prefix-list {{ disable_policy_batching.ipv4.prefix_list }}",
+ "result": {
+ "disable_policy_batching": {
+ "ipv4": {
+ "prefix_list": "{{ ipv4_prefix_list }}",
+ }
+ },
+ }
+ },
+ {
+ "name": "disable_policy_batching.ipv6.prefix_list",
+ "getval": re.compile(
+ r"""
+ \s+disable-policy-batching\sipv6
+ \sprefix-list\s(?P<ipv6_prefix_list>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "disable-policy-batching ipv6 prefix-list {{ disable_policy_batching.ipv6.prefix_list }}",
+ "result": {
+ "disable_policy_batching": {
+ "ipv6": {
+ "prefix_list": "{{ ipv6_prefix_list }}",
+ }
+ },
+ }
+ },
+ {
+ "name": "disable_policy_batching.nexthop",
+ "getval": re.compile(
+ r"""
+ \s+disable-policy-batching\s(?P<nexthop>nexthop)
+ $""", re.VERBOSE
+ ),
+ "setval": "disable-policy-batching nexthop",
+ "result": {
+ "disable_policy_batching": {
+ "nexthop": "{{ not not nexthop }}",
+ },
+ }
+ },
+ {
+ "name": "dynamic_med_interval",
+ "getval": re.compile(
+ r"""
+ \s+dynamic-med-interval\s(?P<dynamic_med_interval>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "dynamic-med-interval {{ dynamic_med_interval }}",
+ "result": {
+ "dynamic_med_interval": "{{ dynamic_med_interval }}",
+ }
+ },
+ {
+ "name": "enforce_first_as",
+ "getval": re.compile(
+ r"""
+ \s+no\s(?P<enforce_first_as>enforce-first-as)
+ $""", re.VERBOSE
+ ),
+ "setval": "enforce-first-as",
+ "result": {
+ "enforce_first_as": "{{ not enforce_first_as }}",
+ }
+ },
+ {
+ "name": "enhanced_error",
+ "getval": re.compile(
+ r"""
+ \s+no\s(?P<enhanced_error>enhanced-error)
+ $""", re.VERBOSE
+ ),
+ "setval": "enhanced-error",
+ "result": {
+ "enhanced_error": "{{ not enhanced_error }}",
+ }
+ },
+ {
+ "name": "fast_external_fallover",
+ "getval": re.compile(
+ r"""
+ \s+no\s(?P<fast_external_fallover>fast-external-fallover)
+ $""", re.VERBOSE
+ ),
+ "setval": "fast-external-fallover",
+ "result": {
+ "fast_external_fallover": "{{ not fast_external_fallover }}",
+ }
+ },
+ {
+ "name": "flush_routes",
+ "getval": re.compile(
+ r"""
+ \s+(?P<flush_routes>flush-routes)
+ $""", re.VERBOSE
+ ),
+ "setval": "flush-routes",
+ "result": {
+ "flush_routes": "{{ not not flush_routes }}",
+ }
+ },
+ {
+ "name": "graceful_restart",
+ "getval": re.compile(
+ r"""
+ \s+no\s(?P<graceful_restart>graceful-restart)
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-restart",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "set": "{{ not graceful_restart }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "graceful_restart.restart_time",
+ "getval": re.compile(
+ r"""
+ \s+graceful-restart\srestart-time\s(?P<restart_time>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-restart restart-time {{ graceful_restart.restart_time }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "restart_time": "{{ restart_time }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "graceful_restart.stalepath_time",
+ "getval": re.compile(
+ r"""
+ \s+graceful-restart\sstalepath-time\s(?P<stalepath_time>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-restart stalepath-time {{ graceful_restart.stalepath_time }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "stalepath_time": "{{ stalepath_time }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "graceful_restart.helper",
+ "getval": re.compile(
+ r"""
+ \s+(?P<helper>graceful-restart-helper)
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-restart-helper",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "helper": "{{ not not helper }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "graceful_shutdown.activate",
+ "getval": re.compile(
+ r"""
+ \s+graceful-shutdown
+ \s(?P<activate>activate)
+ (\sroute-map
+ \s(?P<route_map>\S+))?
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-shutdown activate{{ ' route-map ' + graceful_shutdown.activate.route_map if graceful_shutdown.activate.route_map is defined }}",
+ "result": {
+ "graceful_shutdown": {
+ "activate": {
+ "set": "{{ True if activate is defined and route_map is undefined else None }}",
+ "route_map": "{{ route_map }}",
+ }
+ },
+ }
+ },
+ {
+ "name": "graceful_shutdown.aware",
+ "getval": re.compile(
+ r"""
+ \s+no\sgraceful-shutdown
+ \s(?P<aware>aware)
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-shutdown aware",
+ "result": {
+ "graceful_shutdown": {
+ "aware": "{{ not aware }}"
+ },
+ }
+ },
+ {
+ "name": "isolate",
+ "getval": re.compile(
+ r"""
+ \s+(?P<isolate>isolate)
+ (\s(?P<include_local>include-local))?
+ $""", re.VERBOSE
+ ),
+ "setval": "isolate{{ ' include-local' if isolate.include_local|d(False) is True }}",
+ "result": {
+ "isolate": {
+ "set": "{{ True if isolate is defined and include_local is not defined else None }}",
+ "include_local": "{{ not not include_local }}",
+ },
+ }
+ },
+ {
+ "name": "log_neighbor_changes",
+ "getval": re.compile(
+ r"""
+ \s+(?P<log_neighbor_changes>log-neighbor-changes)
+ $""", re.VERBOSE
+ ),
+ "setval": "log-neighbor-changes",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "log_neighbor_changes": "{{ not not log_neighbor_changes }}",
+ }
+ }
+ }
+ },
+ {
+ "name": "maxas_limit",
+ "getval": re.compile(
+ r"""
+ \s+maxas-limit\s(?P<maxas_limit>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "maxas-limit {{ maxas_limit }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "maxas_limit": "{{ maxas_limit }}",
+ }
+ }
+ }
+ },
+ # start neighbor parsers
+ {
+ "name": "neighbor_address",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "neighbor {{ neighbor_address }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "neighbor_address": "{{ neighbor_address }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "remote_as",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sremote-as\s(?P<remote_as>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "remote-as {{ remote_as }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "remote_as": "{{ remote_as }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "neighbor_affinity_group.group_id",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \saffinity-group\s(?P<group_id>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "affinity-group {{ neighbor_affinity_group.group_id }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "neighbor_affinity_group": {
+ "group_id": "{{ group_id }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bmp_activate_server",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sbmp-activate-server\s(?P<bmp_activate_server>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "bmp-activate-server {{ bmp_activate_server }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "bmp_activate_server": "{{ bmp_activate_server }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "capability",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \scapability\ssuppress\s(?P<suppress_4_byte_as>4-byte-as)
+ $""", re.VERBOSE
+ ),
+ "setval": "capability suppress 4-byte-as",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "capability": {
+ "suppress_4_byte_as": "{{ not not suppress_4_byte_as }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "description",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sdescription\s(?P<description>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "description {{ description }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "description": "{{ description }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "disable_connected_check",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \s(?P<disable_connected_check>disable-connected-check)
+ $""", re.VERBOSE
+ ),
+ "setval": "disable-connected-check",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "disable_connected_check": "{{ not not disable_connected_check }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "dont_capability_negotiate",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \s(?P<dont_capability_negotiate>dont-capability-negotiate)
+ $""", re.VERBOSE
+ ),
+ "setval": "dont-capability-negotiate",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "dont_capability_negotiate": "{{ not not dont_capability_negotiate}}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "dscp",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sdscp\s(?P<dscp>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "dscp {{ dscp }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "dscp": "{{ dscp }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "dynamic_capability",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \s(?P<dynamic_capability>dynamic-capability)
+ $""", re.VERBOSE
+ ),
+ "setval": "dynamic-capability",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "dynamic_capability": "{{ not not dynamic_capability }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "ebgp_multihop",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sebgp-multihop\s(?P<ebgp_multihop>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "ebgp-multihop {{ ebgp_multihop }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "ebgp_multihop": "{{ ebgp_multihop }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "graceful_shutdown",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sgraceful-shutdown
+ \s(?P<activate>activate)
+ (\sroute-map\s(?P<route_map>\S+))?
+ $""", re.VERBOSE
+ ),
+ "setval": "graceful-shutdown{{ (' route-map ' + graceful_shutdown.route_map) if graceful_shutdown.route_map is defined }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "graceful_shutdown": {
+ "activate": {
+ "set": "{{ True if activate is defined and route_map is undefined else None }}",
+ "route_map": "{{ route_map }}",
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "inherit.peer",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sinherit
+ \speer\s(?P<peer>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "inherit peer {{ inherit.peer }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "inherit": {
+ "peer": "{{ peer }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "inherit.peer_session",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sinherit
+ \speer-session\s(?P<peer_session>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "inherit peer-session {{ inherit.peer_session }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "inherit": {
+ "peer_session": "{{ peer_session }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "local_as",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \slocal-as\s(?P<local_as>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "local-as {{ local_as }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "local_as": "{{ local_as }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "log_neighbor_changes",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \s(?P<log_neighbor_changes>log-neighbor-changes)
+ (\s(?P<disable>disable))?
+ $""", re.VERBOSE
+ ),
+ "setval": "log-neighbor-changes{{ ' disable' if log_neighbor_changes.disable is defined }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "log_neighbor_changes": {
+ "set": "{{ True if log_neighbor_changes is defined and disable is undefined }}",
+ "disable": "{{ not not disable }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "low_memory",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \slow-memory\s(?P<exempt>exempt)
+ $""", re.VERBOSE
+ ),
+ "setval": "low-memory exempt",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "low_memory": {
+ "exempt": "{{ not not exempt }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "password",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \spassword\s(?P<encryption>\d+)
+ \s(?P<key>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "password{{ (' ' + password.encryption|string) if password.encryption is defined }} {{ password.key }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "password": {
+ "encryption": "{{ encryption }}",
+ "key": "{{ key }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "path_attribute",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \spath-attribute\s(?P<action>\S+)\s
+ (?P<type>\d+)?
+ (range\s(?P<start>\d+)\s(?P<end>\d+))?
+ \sin
+ $""", re.VERBOSE
+ ),
+ "setval": _tmplt_path_attribute,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "path_attribute": [
+ {
+ "action": "{{ action }}",
+ "type": "{{ type if type is defined else None }}",
+ "range": {
+ "start": "{{ start if start is defined }}",
+ "end": "{{ end if end is defined }}"
+ },
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "peer_type",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \speer-type\s(?P<peer_type>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "peer-type {{ neighbor.peer_type }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "peer_type": "{{ peer_type }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "remove_private_as",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \s(?P<remove_private_as>remove-private-as)
+ (\s(?P<all>all))?
+ (\s(?P<replace_as>replace-as))?
+ $""", re.VERBOSE
+ ),
+ "setval": "remove-private-as",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "remove_private_as": {
+ "set": "{{ True if remove_private_as is defined and replace_as is undefined and all is undefined else None }}",
+ "replace_as": "{{ not not replace_as }}",
+ "all": "{{ not not all }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "shutdown",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \s(?P<shutdown>shutdown)
+ $""", re.VERBOSE
+ ),
+ "setval": "shutdown",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "shutdown": "{{ not not shutdown }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "timers",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \stimers\s(?P<keepalive>\d+)\s(?P<holdtime>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "shutdown",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "timers": {
+ "keepalive": "{{ keepalive }}",
+ "holdtime": "{{ holdtime }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "transport",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \stransport\sconnection-mode
+ \s(?P<passive>passive)
+ $""", re.VERBOSE
+ ),
+ "setval": "transport connection-mode passive",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "transport": {
+ "connection_mode": {
+ "passive": "{{ not not passive }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "ttl_security",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \sttl-security\shops\s(?P<hops>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "ttl-security hops {{ ttl_security.hops }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "ttl_security": {
+ "hops": "{{ hops }}",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "update_source",
+ "getval": re.compile(
+ r"""
+ \s+neighbor\s(?P<neighbor_address>\S+)
+ \supdate-source\s(?P<update_source>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "update-source {{ update_source }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbors": {
+ "{{ neighbor_address }}": {
+ "update_source": "{{ update_source }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ # end neighbor parsers
+ {
+ "name": "neighbor_down.fib_accelerate",
+ "getval": re.compile(
+ r"""
+ \s+neighbor-down\s(?P<fib_accelerate>fib-accelerate)
+ $""", re.VERBOSE
+ ),
+ "setval": "neighbor-down fib-accelerate",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "neighbor_down": {
+ "fib_accelerate": "{{ not not fib_accelerate }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "nexthop.suppress_default_resolution",
+ "getval": re.compile(
+ r"""
+ \s+nexthop
+ \s(?P<suppress_default_resolution>suppress-default-resolution)
+ $""", re.VERBOSE
+ ),
+ "setval": "nexthop suppress-default-resolution",
+ "result": {
+ "nexthop": {
+ "suppress_default_resolution": "{{ not not suppress_default_resolution }}",
+ },
+ }
+ },
+ {
+ "name": "reconnect_interval",
+ "getval": re.compile(
+ r"""
+ \s+reconnect-interval
+ \s(?P<reconnect_interval>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "reconnect-interval {{ reconnect_interval }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "reconnect_interval": "{{ reconnect_interval }}",
+ }
+ }
+ }
+ },
+ {
+ "name": "router_id",
+ "getval": re.compile(
+ r"""
+ \s+router-id
+ \s(?P<router_id>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "router-id {{ router_id }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "router_id": "{{ router_id }}",
+ }
+ }
+ }
+ },
+ {
+ "name": "shutdown",
+ "getval": re.compile(
+ r"""
+ \s+(?P<shutdown>shutdown)
+ $""", re.VERBOSE
+ ),
+ "setval": "shutdown",
+ "result": {
+ "shutdown": "{{ not not shutdown }}",
+ }
+ },
+ {
+ "name": "suppress_fib_pending",
+ "getval": re.compile(
+ r"""
+ \s+no\s(?P<suppress_fib_pending>suppress-fib-pending)
+ $""", re.VERBOSE
+ ),
+ "setval": "suppress-fib-pending",
+ "result": {
+ "suppress_fib_pending": "{{ not suppress_fib_pending }}",
+ }
+ },
+ {
+ "name": "timers.bestpath_limit",
+ "getval": re.compile(
+ r"""
+ \s+timers\sbestpath-limit
+ \s(?P<timeout>\d+)
+ (\s(?P<always>always))?
+ $""", re.VERBOSE
+ ),
+ "setval": "timers bestpath-limit {{ timers.bestpath_limit.timeout }}{{ ' always' if timers.bestpath_limit.timeout is defined }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "bestpath_limit": {
+ "timeout": "{{ timeout }}",
+ "always": "{{ not not always }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "timers.bgp",
+ "getval": re.compile(
+ r"""
+ \s+timers\sbgp
+ \s(?P<keepalive>\d+)
+ (\s(?P<holdtime>\d+))?
+ $""", re.VERBOSE
+ ),
+ "setval": "timers bgp {{ timers.bgp.keepalive }} {{ timers.bgp.holdtime }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "bgp": {
+ "keepalive": "{{ keepalive }}",
+ "holdtime": "{{ holdtime }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "timers.prefix_peer_timeout",
+ "getval": re.compile(
+ r"""
+ \s+timers
+ \sprefix-peer-timeout\s(?P<prefix_peer_timeout>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "timers prefix-peer-timeout {{ timers.prefix_peer_timeout }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "prefix_peer_timeout": "{{ prefix_peer_timeout }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "timers.prefix_peer_wait",
+ "getval": re.compile(
+ r"""
+ \s+timers
+ \sprefix-peer-wait\s(?P<prefix_peer_wait>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "timers prefix-peer-wait {{ timers.prefix_peer_wait }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "prefix_peer_wait": "{{ prefix_peer_wait }}",
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "fabric_soo",
+ "getval": re.compile(
+ r"""
+ \s+fabric-soo
+ \s(?P<fabric_soo>\S+)
+ $""", re.VERBOSE
+ ),
+ "setval": "fabric-soo {{ fabric_soo }}",
+ "result": {
+ "fabric_soo": "{{ fabric_soo }}",
+ }
+ },
+ {
+ "name": "rd",
+ "getval": re.compile(
+ r"""
+ \s+rd\s(?P<dual>dual)
+ (\sid\s(?P<id>\d+))?
+ $""", re.VERBOSE
+ ),
+ "setval": "rd dual{{' id ' + rd.id if rd.id is defined }}",
+ "result": {
+ "rd": {
+ "dual": "{{ not not dual }}",
+ "id": "{{ id }}",
+ }
+ }
+ },
+ # VRF only
+ {
+ "name": "allocate_index",
+ "getval": re.compile(
+ r"""
+ \s+allocate-index\s(?P<allocate_index>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "allocate-index {{ allocate_index }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "allocate_index": "{{ allocate_index }}",
+ }
+ },
+ }
+ },
+ # VRF only
+ {
+ "name": "local_as",
+ "getval": re.compile(
+ r"""
+ \s+local-as\s(?P<local_as>\d+)
+ $""", re.VERBOSE
+ ),
+ "setval": "local-as {{ local_as }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "local_as": "{{ local_as }}",
+ }
+ }
+ }
+ },
+ ]
+ # fmt: on
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospf_interfaces.py
new file mode 100644
index 00000000..58575994
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospf_interfaces.py
@@ -0,0 +1,505 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+"""
+The Ospf_interfaces parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import (
+ NetworkTemplate,
+)
+
+
+def _tmplt_authentication(data):
+ auth = data.get("authentication")
+ cmd = "ip ospf authentication"
+
+ if auth.get("enable") is False:
+ cmd = "no " + cmd
+ else:
+ if auth.get("message_digest"):
+ cmd += " message-digest"
+ elif auth.get("null_auth"):
+ cmd += " null"
+ return cmd
+
+
+class Ospf_interfacesTemplate(NetworkTemplate):
+ def __init__(self, lines=None):
+ super(Ospf_interfacesTemplate, self).__init__(lines=lines, tmplt=self)
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "interface",
+ "getval": re.compile(r'''
+ ^interface
+ \s(?P<name>\S+)$''', re.VERBOSE),
+ "setval": "interface {{ name }}",
+ "result": {
+ "{{ name }}": {
+ "name": "{{ name }}",
+ "address_family": {},
+ },
+ },
+ "shared": True,
+ },
+ {
+ "name": "area",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip|ipv6)
+ \srouter\s(ospf|ospfv3)
+ \s(?P<process_id>\S+)
+ \sarea\s(?P<area_id>\S+)
+ (\s(?P<secondaries>secondaries\snone))?$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip' if afi == 'ipv4' else 'ipv6' }} "
+ "router {{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "{{ process_id }} area {{ area.area_id }}{{ ' secondaries none' if area.secondaries|default('True') == False}}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}",
+ "processes": {
+ "{{ process_id }}": {
+ "process_id": "{{ process_id }}",
+ "area": {
+ "area_id": "{{ area_id }}",
+ "secondaries": "{{ False if secondaries is defined else None }}",
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "processes_multi_areas",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip|ipv6)
+ \srouter\s(ospf|ospfv3)
+ \s(?P<process_id>\S+)
+ \smulti-area\s(?P<area>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip' if afi == 'ipv4' else 'ipv6' }} "
+ "router {{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "{{ process_id }} multi-area {{ area }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}",
+ "processes": {
+ "{{ process_id }}": {
+ "process_id": "{{ process_id }}",
+ "multi_areas": [
+ "{{ area }}",
+ ],
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "multi_areas",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip|ipv6)
+ \srouter\s(ospf|ospfv3)
+ \smulti-area\s(?P<area>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip' if afi == 'ipv4' else 'ipv6' }} "
+ "router {{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "multi-area {{ area }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}",
+ "multi_areas": [
+ "{{ area }}",
+ ]
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "authentication",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip|ipv6)
+ \s(ospf|ospfv3)
+ \s(?P<authentication>authentication)
+ (\s(?P<opt>(message-digest|null)))?$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_authentication,
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ 'ipv4' if afi == 'ip' else 'ipv6' }}",
+ "authentication": {
+ "enable": "{{ True if authentication is defined and opt is undefined }}",
+ "message_digest": "{{ True if opt == 'message-digest' else None }}",
+ "null_auth": "{{ True if opt == 'null' else None }}",
+ }
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "authentication.key_chain",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)
+ \sospf
+ \s(?P<authentication>authentication)
+ \skey-chain\s(?P<key_chain>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "ip ospf authentication key-chain {{ authentication.key_chain }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ afi|replace('ip', 'ipv4') }}",
+ "authentication": {
+ "key_chain": "{{ key_chain }}",
+ }
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "authentication_key",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)
+ \sospf
+ \sauthentication-key
+ \s(?P<encryption>\d)
+ \s(?P<key>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "ip ospf authentication-key "
+ "{{ authentication_key.encryption }} {{ authentication_key.key }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ afi|replace('ip', 'ipv4') }}",
+ "authentication_key": {
+ "encryption": "{{ encryption }}",
+ "key": "{{ key }}",
+ }
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "message_digest_key",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)
+ \sospf
+ \smessage-digest-key
+ \s(?P<key_id>\d+)
+ \smd5
+ \s(?P<encryption>\d)
+ \s(?P<key>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "ip ospf "
+ "message-digest-key {{ message_digest_key.key_id }} "
+ "md5 {{ message_digest_key.encryption|default('') }} {{ message_digest_key.key }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi }}": {
+ "afi": "{{ afi|replace('ip', 'ipv4') }}",
+ "message_digest_key": {
+ "key_id": "{{ key_id }}",
+ "encryption": "{{ encryption }}",
+ "key": "{{ key }}",
+ }
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "cost",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \scost\s(?P<cost>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "cost {{ cost }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "cost": "{{ cost }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "dead_interval",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \sdead-interval\s(?P<dead_interval>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "dead-interval {{ dead_interval }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "dead_interval": "{{ dead_interval }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "hello_interval",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \shello-interval\s(?P<hello_interval>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "hello-interval {{ hello_interval }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "hello_interval": "{{ hello_interval }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "instance",
+ "getval": re.compile(
+ r"""
+ \s+(ospf|ospfv3)
+ \sinstance\s(?P<instance>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "ospfv3 instance {{ instance }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "ipv6",
+ "instance": "{{ instance }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "mtu_ignore",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \s(?P<mtu_ignore>mtu-ignore)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "mtu-ignore",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "mtu_ignore": "{{ not not mtu_ignore }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "network",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \snetwork\s(?P<network>(broadcast|point-to-point))$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "network {{ network }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "network": "{{ network }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "passive_interface",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \s(?P<passive_interface>passive-interface)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "passive-interface",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "passive_interface": "{{ not not passive_interface }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "priority",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \spriority\s(?P<priority>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "priority {{ priority }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "priority": "{{ priority }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "retransmit_interval",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \sretransmit-interval\s(?P<retransmit_interval>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "retransmit-interval {{ retransmit_interval }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "retransmit_interval": "{{ retransmit_interval }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "shutdown",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \s(?P<shutdown>shutdown)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "shutdown",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "shutdown": "{{ not not shutdown }}",
+ },
+ }
+ }
+ }
+ },
+ {
+ "name": "transmit_delay",
+ "getval": re.compile(
+ r"""
+ \s+(?P<afi>ip)?
+ \s(ospf|ospfv3)
+ \stransmit-delay\s(?P<transmit_delay>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "{{ 'ip ' if afi == 'ipv4' else '' }}"
+ "{{ 'ospf' if afi == 'ipv4' else 'ospfv3' }} "
+ "transmit-delay {{ transmit_delay }}",
+ "result": {
+ "{{ name }}": {
+ "address_family": {
+ "{{ afi|d('ipv6') }}": {
+ "afi": "{{ 'ipv4' if afi is defined else 'ipv6' }}",
+ "transmit_delay": "{{ transmit_delay }}",
+ },
+ }
+ }
+ }
+ },
+ ]
+ # fmt: on
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv2.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv2.py
new file mode 100644
index 00000000..8aaf41a5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv2.py
@@ -0,0 +1,1105 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import (
+ NetworkTemplate,
+)
+
+
+def _tmplt_area_range(arange):
+ command = "area {area} range {range}".format(**arange)
+ if arange.get("not_advertise") is True:
+ command += " not-advertise"
+ if "cost" in arange:
+ command += " cost {cost}".format(**arange)
+ return command
+
+
+def _tmplt_default_information(proc):
+ command = "default-information originate"
+ if (
+ "always" in proc["default_information"]
+ and proc["default_information"]["always"]
+ ):
+ command += " always"
+ if "route_map" in proc["default_information"]:
+ command += " route-map"
+ command += " {default_information[route_map]}".format(**proc)
+ return command
+
+
+def _tmplt_log_adjacency_changes(proc):
+ command = "log-adjacency-changes"
+ if proc.get("log_adjacency_changes").get("detail", False) is True:
+ command += " detail"
+ return command
+
+
+def _tmplt_area_authentication(area):
+ command = "area {area_id} authentication".format(**area)
+ if area.get("authentication", {}).get("message_digest"):
+ command += " message-digest"
+ return command
+
+
+def _tmplt_max_lsa(proc):
+ max_lsa = proc["max_lsa"]
+ command = "max-lsa {max_non_self_generated_lsa}".format(**max_lsa)
+ if max_lsa.get("threshold"):
+ command += " {threshold}".format(**max_lsa)
+ if max_lsa.get("warning_only"):
+ command += " warning-only"
+ if max_lsa.get("ignore_time"):
+ command += " ignore-time {ignore_time}".format(**max_lsa)
+ if max_lsa.get("ignore_count"):
+ command += " ignore-count {ignore_count}".format(**max_lsa)
+ if max_lsa.get("reset_time"):
+ command += " reset-time {reset_time}".format(**max_lsa)
+ return command
+
+
+def _tmplt_default_information(proc):
+ default_information = proc["default_information"]["originate"]
+ command = "default-information originate"
+
+ if default_information.get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ if default_information.get("always"):
+ command += " always"
+ if default_information.get("route_map"):
+ command += " route-map {route_map}".format(**default_information)
+
+ return command
+
+
+def _tmplt_table_map(proc):
+ table_map = proc["table_map"]
+ command = "table-map"
+
+ if table_map.get("name"):
+ command += " {name}".format(**table_map)
+ if table_map.get("filter"):
+ command += " filter"
+
+ return command
+
+
+def _tmplt_max_metric(proc):
+ max_metric = proc["max_metric"]
+ command = "max-metric router-lsa"
+
+ if max_metric.get("router_lsa", {}).get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ external_lsa = max_metric.get("router_lsa", {}).get("external_lsa", {})
+ include_stub = max_metric.get("router_lsa", {}).get("include_stub", {})
+ on_startup = max_metric.get("router_lsa", {}).get("on_startup", {})
+ summary_lsa = max_metric.get("router_lsa", {}).get("summary_lsa", {})
+ if external_lsa:
+ command += " external-lsa"
+ if external_lsa.get("max_metric_value"):
+ command += " {max_metric_value}".format(**external_lsa)
+ if include_stub:
+ command += " include-stub"
+ if on_startup:
+ command += " on-startup"
+ if on_startup.get("wait_period"):
+ command += " {wait_period}".format(**on_startup)
+ if on_startup.get("wait_for_bgp_asn"):
+ command += " wait-for bgp {wait_for_bgp_asn}".format(
+ **on_startup
+ )
+ if summary_lsa:
+ command += " summary-lsa"
+ if summary_lsa.get("max_metric_value"):
+ command += " {max_metric_value}".format(**summary_lsa)
+
+ return command
+
+
+def _tmplt_area_nssa(area):
+ nssa = area["nssa"]
+ command = "area {area_id} nssa".format(**area)
+ if nssa.get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ for attrib in [
+ "no_summary",
+ "no_redistribution",
+ "default_information_originate",
+ ]:
+ if nssa.get(attrib):
+ command += " {0}".format(attrib.replace("_", "-"))
+ return command
+
+
+def _tmplt_area_nssa_translate(area):
+ translate = area["nssa"]["translate"]["type7"]
+ command = "area {area_id} nssa translate type7".format(**area)
+ for attrib in ["always", "never", "supress_fa"]:
+ if translate.get(attrib):
+ command += " {0}".format(attrib.replace("_", "-"))
+ return command
+
+
+def _tmplt_area_ranges(arange):
+ command = "area {area_id} range {prefix}".format(**arange)
+ if arange.get("not_advertise") is True:
+ command += " not-advertise"
+ if "cost" in arange:
+ command += " cost {cost}".format(**arange)
+ return command
+
+
+def _tmplt_area_ranges(arange):
+ command = "area {area_id} range {prefix}".format(**arange)
+ if arange.get("not_advertise") is True:
+ command += " not-advertise"
+ if "cost" in arange:
+ command += " cost {cost}".format(**arange)
+ return command
+
+
+def _tmplt_summary_address(proc):
+ command = "summary-address {prefix}".format(**proc)
+ if proc.get("tag"):
+ command += " tag {tag}".format(**proc)
+ elif proc.get("not_advertise"):
+ command += " not-advertise"
+ return command
+
+
+def _tmplt_area_stub(area):
+ stub = area["stub"]
+ command = "area {area_id} stub".format(**area)
+ if stub.get("set") is False:
+ command = "no {0}".format(command)
+ elif stub.get("no_summary"):
+ command += " no-summary"
+ return command
+
+
+def _tmplt_redistribute(redis):
+ command = "redistribute {protocol}".format(**redis)
+ if redis.get("id"):
+ command += " {id}".format(**redis)
+ if redis.get("route_map"):
+ command += " route-map {route_map}".format(**redis)
+ return command
+
+
+def _tmplt_capability_vrf_lite(proc):
+ command = "capability vrf-lite"
+ vrf_lite = proc["capability"]["vrf_lite"]
+ if vrf_lite.get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ if vrf_lite.get("evpn"):
+ command += " evpn"
+ return command
+
+
+class Ospfv2Template(NetworkTemplate):
+ def __init__(self, lines=None):
+ super(Ospfv2Template, self).__init__(lines=lines, tmplt=self)
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "vrf",
+ "getval": re.compile(
+ r"""
+ \s+vrf
+ \s(?P<vrf>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf {{ vrf }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "vrf": "{{ vrf }}"
+ }
+ }
+ },
+ "shared": True,
+ },
+ {
+ "name": "bfd",
+ "getval": re.compile(
+ r"""
+ \s+(?P<bfd>bfd)$""",
+ re.VERBOSE,
+ ),
+ "setval": "bfd",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "bfd": "{{ not not bfd }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "process_id",
+ "getval": re.compile(
+ r"""
+ ospf(?:v3)*\s
+ (?P<process_id>\S+)""",
+ re.VERBOSE,
+ ),
+ "setval": "router ospf {{ process_id }}",
+ "result": {
+ "process_id": "{{ process_id }}",
+ },
+ "shared": True,
+ },
+ {
+ "name": "down_bit_ignore",
+ "getval": re.compile(
+ r"""
+ \s+(?P<down_bit_ignore>down-bit-ignore)$""",
+ re.VERBOSE,
+ ),
+ "setval": "down-bit-ignore",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "down_bit_ignore": "{{ not not down_bit_ignore }}",
+ }
+ }
+ },
+ },
+ {
+ "name": "capability.vrf_lite",
+ "getval": re.compile(
+ r"""
+ \s+capability
+ \s(?P<vrf_lite>vrf-lite)
+ \s*(?P<evpn>evpn)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_capability_vrf_lite,
+ "remval": "capability vrf-lite",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "capability": {
+ "vrf_lite": {
+ "set": "{{ True if vrf_lite is defined and evpn is undefined else None }}",
+ "evpn": "{{ not not evpn }}",
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "auto_cost",
+ "getval": re.compile(
+ r"""
+ \s+auto-cost\sreference-bandwidth\s
+ (?P<acrb>\d+)\s(?P<unit>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": (
+ "auto-cost reference-bandwidth"
+ " {{ auto_cost.reference_bandwidth }}"
+ " {{ auto_cost.unit }}"
+ ),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "auto_cost": {
+ "reference_bandwidth": "{{ acrb }}",
+ "unit": "{{ unit }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "flush_routes",
+ "getval": re.compile(
+ r"""
+ \s+(?P<flush_routes>flush-routes)$""",
+ re.VERBOSE,
+ ),
+ "setval": "flush-routes",
+ "result": {
+ "flush_routes": "{{ not not flush_routes }}"
+ },
+ },
+ {
+ "name": "graceful_restart.set",
+ "getval": re.compile(
+ r"""
+ \s+(?P<graceful_restart>no\sgraceful-restart)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart",
+ "remval": "no graceful-restart",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "set": "{{ not graceful_restart }}"
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "graceful_restart.helper_disable",
+ "getval": re.compile(
+ r"""
+ \s+graceful-restart
+ \s+(?P<helper_disable>helper-disable)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart helper-disable",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "helper_disable": "{{ not not helper_disable }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "graceful_restart.grace_period",
+ "getval": re.compile(
+ r"""
+ \s+graceful-restart
+ \s+grace-period
+ \s+(?P<grace_period>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart helper-disable",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "grace_period": "{{ grace_period }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "isolate",
+ "getval": re.compile(
+ r"""
+ \s+(?P<isolate>isolate)$""",
+ re.VERBOSE,
+ ),
+ "setval": "isolate",
+ "result": {"isolate": "{{ not not isolate }}"},
+ },
+ {
+ "name": "log_adjacency_changes",
+ "getval": re.compile(
+ r"""
+ \s+(?P<log>log-adjacency-changes)
+ \s*(?P<detail>detail)*$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_log_adjacency_changes,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "log_adjacency_changes": {
+ "log": "{{ True if log is defined and detail is undefined else None }}",
+ "detail": "{{ True if detail is defined else None }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "max_lsa",
+ "getval": re.compile(
+ r"""
+ \s+max-lsa
+ \s(?P<max_gen_lsa>\d+)
+ \s*(?P<threshold>\d*)
+ \s*(?P<warning_only>warning-only)*
+ \s*(ignore-time)*\s*(?P<ig_time>\d*)
+ \s*(ignore-count)*\s*(?P<ig_count>\d*)
+ \s*(reset-time)*\s*(?P<rst_time>\d*)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_max_lsa,
+ "remval": "max-lsa {{ max_lsa.max_non_self_generated_lsa }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "max_lsa": {
+ "max_non_self_generated_lsa": "{{ max_gen_lsa }}",
+ "threshold": "{{ threshold }}",
+ "ignore_time": "{{ ig_time }}",
+ "ignore_count": "{{ ig_count }}",
+ "reset_time": "{{ rst_time }}",
+ "warning_only": "{{ not not warning_only }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "mpls.traffic_eng.areas",
+ "getval": re.compile(
+ r"""
+ \s+mpls\straffic-eng\sarea
+ \s(?P<area_id>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("mpls traffic-eng area {{ area_id }}"),
+ "result": {
+ "mpls": {
+ "traffic_eng": {
+ "areas": [
+ {
+ "area_id": "{{ area_id }}",
+ }
+ ]
+ }
+ }
+ },
+ },
+ {
+ "name": "mpls.traffic_eng.router_id",
+ "getval": re.compile(
+ r"""
+ \s+mpls\straffic-eng\srouter-id
+ \s(?P<router_id>\S+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": (
+ "mpls traffic-eng router-id" " {{ mpls.traffic_eng.router_id }}"
+ ),
+ "result": {"mpls": {"traffic_eng": {"router_id": "{{ router_id }}"}}},
+ },
+ {
+ "name": "mpls.traffic_eng.multicast_intact",
+ "getval": re.compile(
+ r"""
+ \s+mpls\straffic-eng
+ \s(?P<multicast_intact>multicast-intact)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("mpls traffic-eng multicast-intact"),
+ "result": {
+ "mpls": {
+ "traffic_eng": {
+ "multicast_intact": "{{ not not multicast_intact }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "name_lookup",
+ "getval": re.compile(
+ r"""
+ \s+(?P<name_lookup>name-lookup)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("name-lookup"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "name_lookup": "{{ not not name_lookup }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "passive_interface.default",
+ "getval": re.compile(
+ r"""
+ \s+passive-interface
+ \s+(?P<default>default)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("passive-interface default"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "passive_interface": {"default": "{{ not not default }}"}
+ }
+ }
+ },
+ },
+ {
+ "name": "rfc1583compatibility",
+ "getval": re.compile(
+ r"""
+ \s+(?P<rfc>rfc1583compatibility)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("rfc1583compatibility"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "rfc1583compatibility": "{{ not not rfc }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "router_id",
+ "getval": re.compile(
+ r"""
+ \s+router-id
+ \s(?P<router_id>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("router-id" " {{ router_id }}"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "router_id": "{{ router_id }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "shutdown",
+ "getval": re.compile(
+ r"""
+ \s+(?P<shutdown>shutdown)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("shutdown"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "shutdown": "{{ not not shutdown }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "default_information.originate",
+ "getval": re.compile(
+ r"""
+ \s+default-information
+ \s(?P<originate>originate)
+ \s*(?P<always>always)*
+ \s*(route-map)*
+ \s*(?P<route_map>\S+)*$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_default_information,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "default_information": {
+ "originate": {
+ "set": "{{ True if originate is defined and always is undefined and route_map is undefined else None }}",
+ "always": "{{ not not always }}",
+ "route_map": "{{ route_map }}",
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "default_metric",
+ "getval": re.compile(
+ r"""
+ \s+default-metric
+ \s(?P<default_metric>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("default-metric {{ default_metric }}"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "default_metric": "{{ default_metric }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "distance",
+ "getval": re.compile(
+ r"""
+ \s+distance
+ \s(?P<distance>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("distance {{ distance }}"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "distance": "{{ distance }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "table_map",
+ "getval": re.compile(
+ r"""
+ \s+table-map
+ \s(?P<rmap>\S+)
+ \s*(?P<filter>filter)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_table_map,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "table_map": {
+ "name": "{{ rmap }}",
+ "filter": "{{ not not filter }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.lsa_arrival",
+ "getval": re.compile(
+ r"""
+ \s+timers
+ \slsa-arrival
+ \s(?P<lsa_arrival_val>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("timers lsa-arrival {{ timers.lsa_arrival }}"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "lsa_arrival": "{{ lsa_arrival_val }}"
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.lsa_group_pacing",
+ "getval": re.compile(
+ r"""
+ \s+timers
+ \slsa-group-pacing
+ \s(?P<lsa_group_pacing>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "timers lsa-group-pacing {{ timers.lsa_group_pacing }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "lsa_group_pacing": "{{ lsa_group_pacing }}"
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.throttle.lsa",
+ "getval": re.compile(
+ r"""
+ \s+timers\sthrottle\slsa
+ \s(?P<start>\d+)
+ \s(?P<hold>\d+)
+ \s(?P<max>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "timers throttle lsa {{ timers.throttle.lsa.start_interval }}"
+ " {{ timers.throttle.lsa.hold_interval }}"
+ " {{ timers.throttle.lsa.max_interval }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "throttle": {
+ "lsa": {
+ "start_interval": "{{ start }}",
+ "hold_interval": "{{ hold }}",
+ "max_interval": "{{ max }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.throttle.spf",
+ "getval": re.compile(
+ r"""
+ \s+timers\sthrottle\sspf
+ \s(?P<initial>\d+)
+ \s(?P<min>\d+)
+ \s(?P<max>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "timers throttle spf {{ timers.throttle.spf.initial_spf_delay }}"
+ " {{ timers.throttle.spf.min_hold_time }}"
+ " {{ timers.throttle.spf.max_wait_time }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "throttle": {
+ "spf": {
+ "initial_spf_delay": "{{ initial }}",
+ "min_hold_time": "{{ min }}",
+ "max_wait_time": "{{ max }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.default_cost",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)\s
+ default-cost\s(?P<default_cost>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "area {{ area_id }} default-cost {{ default_cost }}",
+ "compval": "default_cost",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "default_cost": "{{ default_cost|int }}",
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.authentication",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \s(?P<auth>authentication)
+ \s*(?P<md>message-digest)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_authentication,
+ "remval": "area {{ area_id }} authentication",
+ "compval": "authentication",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "authentication": {
+ "set": "{{ True if auth is defined and md is undefined }}",
+ "message_digest": "{{ True if md is defined else False }}"
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.filter_list",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \sfilter-list
+ \sroute-map\s(?P<rmap>\S+)
+ \s(?P<dir>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "area {{ area_id }} filter-list route-map {{ route_map }} {{ direction }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "filter_list": [
+ {
+ "route_map": "{{ rmap }}",
+ "direction": "{{ dir }}",
+ },
+ ],
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "redistribute",
+ "getval": re.compile(
+ r"""
+ \s+redistribute
+ \s(?P<protocol>\S+)
+ \s*(?P<id>\S+)*
+ \sroute-map\s(?P<rmap>\S+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_redistribute,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "redistribute": [
+ {
+ "protocol": "{{ protocol }}",
+ "id": "{{ id }}",
+ "route_map": "{{ rmap }}",
+ },
+ ]
+ }
+ }
+ },
+ },
+ {
+ "name": "area.nssa",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \s(?P<nssa>nssa)
+ \s*(?P<no_sum>no-summary)*
+ \s*(?P<no_redis>no-redistribution)*
+ \s*(?P<def_info>default-information-originate)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_nssa,
+ "remval": "area {{ area_id }} nssa",
+ "compval": "nssa",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "nssa": {
+ "set": "{{ True if nssa is defined and no_sum is undefined and no_redis is undefined and def_info is undefined }}",
+ "no_summary": "{{ not not no_sum }}",
+ "no_redistribution": "{{ not not no_redis }}",
+ "default_information_originate": "{{ not not def_info }}",
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.nssa.translate",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)\snssa
+ \stranslate
+ \stype7
+ \s(?P<choice>always|never)
+ \s*(?P<supress_fa>supress-fa)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_nssa_translate,
+ "compval": "nssa.translate",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "nssa": {
+ "translate": {
+ "type7": {
+ "always": '{{ True if choice == "always" else None }}',
+ "never": '{{ True if choice == "never" else None }}',
+ "supress_fa": "{{ not not supress_fa }}",
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.ranges",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \srange\s(?P<prefix>\S+)
+ \s*(cost)*\s*(?P<cost>\d+)*
+ \s*(?P<not_adver>not-advertise)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_ranges,
+ "remval": "area {{ area_id }} range {{ prefix }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "ranges": [
+ {
+ "prefix": "{{ prefix }}",
+ "cost": "{{ cost }}",
+ "not_advertise": "{{ not not not_adver }}",
+ },
+ ],
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "summary_address",
+ "getval": re.compile(
+ r"""
+ \s+summary-address
+ \s(?P<prefix>\S+)
+ \s*(?P<not_adver>not-advertise)*
+ \s*(tag)*\s*(?P<tag>\d+)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_summary_address,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "summary_address": [
+ {
+ "prefix": "{{ prefix }}",
+ "not_advertise": "{{ not not not_adver }}",
+ "tag": "{{ tag }}",
+ },
+ ]
+ }
+ }
+ },
+ },
+ {
+ "name": "area.stub",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \s(?P<stub>stub)
+ \s*(?P<no_summary>no-summary)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_stub,
+ "remval": "area {{ area_id }} stub",
+ "compval": "stub",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "stub": {
+ "set": "{{ True if stub is defined and no_summary is undefined else None }}",
+ "no_summary": "{{ not not no_summary }}",
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "maximum_paths",
+ "getval": re.compile(
+ r"""
+ \s+maximum-paths
+ \s(?P<maximum_paths>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("maximum-paths {{ maximum_paths }}"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {"maximum_paths": "{{ maximum_paths }}"}
+ }
+ },
+ },
+ {
+ "name": "max_metric",
+ "getval": re.compile(
+ r"""
+ \s+max-metric
+ \s+(?P<router_lsa>router-lsa)
+ \s*(?P<external_lsa>external-lsa)*
+ \s*(?P<max_metric_value>\d+)*
+ \s*(?P<include_stub>include-stub)*
+ \s*(?P<on_startup>on-startup)*
+ \s*(?P<wait_period>\d+)*
+ \s*(wait-for\sbgp)*
+ \s*(?P<bgp_asn>\d+)*
+ \s*(?P<summary_lsa>summary-lsa)*
+ \s*(?P<sum_lsa_max_metric_value>\d+)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_max_metric,
+ "remval": "max-metric router-lsa",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "max_metric": {
+ "router_lsa": {
+ "set": "{{ True if router_lsa is defined and external_lsa is undefined else None }}",
+ "external_lsa": {
+ "set": "{{ True if external_lsa is defined and max_metric_value is undefined else None }}",
+ "max_metric_value": "{{ max_metric_value }}",
+ },
+ "include_stub": "{{ not not include_stub }}",
+ "on_startup": {
+ "set": "{{ True if on_startup is defined and (wait_period and bgp_asn) is undefined else None }}",
+ "wait_period": "{{ wait_period }}",
+ "wait_for_bgp_asn": "{{ bgp_asn }}",
+ },
+ "summary_lsa": {
+ "set": "{{ True if summary_lsa is defined and sum_lsa_max_metric_value is undefined else None }}",
+ "max_metric_value": "{{ sum_lsa_max_metric_value }}",
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ # fmt: on
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv3.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv3.py
new file mode 100644
index 00000000..f2bd7cad
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/rm_templates/ospfv3.py
@@ -0,0 +1,951 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# 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
+
+"""
+The Ospfv3 parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import (
+ NetworkTemplate,
+)
+
+
+def _tmplt_area_nssa(area):
+ nssa = area["nssa"]
+ command = "area {area_id} nssa".format(**area)
+ if nssa.get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ for attrib in [
+ "no_summary",
+ "no_redistribution",
+ "default_information_originate",
+ ]:
+ if nssa.get(attrib):
+ command += " {0}".format(attrib.replace("_", "-"))
+ if nssa.get("route_map"):
+ command += " route-map {route_map}".format(**nssa)
+ return command
+
+
+def _tmplt_area_nssa_translate(area):
+ translate = area["nssa"]["translate"]["type7"]
+ command = "area {area_id} nssa translate type7".format(**area)
+ for attrib in ["always", "never", "supress_fa"]:
+ if translate.get(attrib):
+ command += " {0}".format(attrib.replace("_", "-"))
+ return command
+
+
+def _tmplt_area_stub(area):
+ stub = area["stub"]
+ command = "area {area_id} stub".format(**area)
+ if stub.get("set") is False:
+ command = "no {0}".format(command)
+ elif stub.get("no_summary"):
+ command += " no-summary"
+ return command
+
+
+def _tmplt_log_adjacency_changes(proc):
+ command = "log-adjacency-changes"
+ if proc.get("log_adjacency_changes").get("detail", False) is True:
+ command += " detail"
+ return command
+
+
+def _tmplt_max_lsa(proc):
+ max_lsa = proc["max_lsa"]
+ command = "max-lsa {max_non_self_generated_lsa}".format(**max_lsa)
+ if max_lsa.get("threshold"):
+ command += " {threshold}".format(**max_lsa)
+ if max_lsa.get("warning_only"):
+ command += " warning-only"
+ if max_lsa.get("ignore_time"):
+ command += " ignore-time {ignore_time}".format(**max_lsa)
+ if max_lsa.get("ignore_count"):
+ command += " ignore-count {ignore_count}".format(**max_lsa)
+ if max_lsa.get("reset_time"):
+ command += " reset-time {reset_time}".format(**max_lsa)
+ return command
+
+
+def _tmplt_max_metric(proc):
+ max_metric = proc["max_metric"]
+ command = "max-metric router-lsa"
+
+ if max_metric.get("router_lsa", {}).get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ external_lsa = max_metric.get("router_lsa", {}).get("external_lsa", {})
+ stub_prefix_lsa = max_metric.get("router_lsa", {}).get(
+ "stub_prefix_lsa", {}
+ )
+ on_startup = max_metric.get("router_lsa", {}).get("on_startup", {})
+ inter_area_prefix_lsa = max_metric.get("router_lsa", {}).get(
+ "inter_area_prefix_lsa", {}
+ )
+ if external_lsa:
+ command += " external-lsa"
+ if external_lsa.get("max_metric_value"):
+ command += " {max_metric_value}".format(**external_lsa)
+ if stub_prefix_lsa:
+ command += " stub-prefix-lsa"
+ if on_startup:
+ command += " on-startup"
+ if on_startup.get("wait_period"):
+ command += " {wait_period}".format(**on_startup)
+ if on_startup.get("wait_for_bgp_asn"):
+ command += " wait-for bgp {wait_for_bgp_asn}".format(
+ **on_startup
+ )
+ if inter_area_prefix_lsa:
+ command += " inter-area-prefix-lsa"
+ if inter_area_prefix_lsa.get("max_metric_value"):
+ command += " {max_metric_value}".format(
+ **inter_area_prefix_lsa
+ )
+
+ return command
+
+
+def _tmplt_area_ranges(arange):
+ command = "area {area_id} range {prefix}".format(**arange)
+ if arange.get("not_advertise") is True:
+ command += " not-advertise"
+ if "cost" in arange:
+ command += " cost {cost}".format(**arange)
+ return command
+
+
+def _tmplt_default_information(proc):
+ default_information = proc["default_information"]["originate"]
+ command = "default-information originate"
+
+ if default_information.get("set") is False:
+ command = "no {0}".format(command)
+ else:
+ if default_information.get("always"):
+ command += " always"
+ if default_information.get("route_map"):
+ command += " route-map {route_map}".format(**default_information)
+
+ return command
+
+
+def _tmplt_redistribute(redis):
+ command = "redistribute {protocol}".format(**redis)
+ if redis.get("id"):
+ command += " {id}".format(**redis)
+ command += " route-map {route_map}".format(**redis)
+ return command
+
+
+def _tmplt_summary_address(proc):
+ command = "summary-address {prefix}".format(**proc)
+ if proc.get("tag"):
+ command += " tag {tag}".format(**proc)
+ elif proc.get("not_advertise"):
+ command += " not-advertise"
+ return command
+
+
+def _tmplt_table_map(proc):
+ table_map = proc["table_map"]
+ command = "table-map {name}".format(**table_map)
+ if table_map.get("filter"):
+ command += " filter"
+
+ return command
+
+
+class Ospfv3Template(NetworkTemplate):
+ def __init__(self, lines=None):
+ super(Ospfv3Template, self).__init__(lines=lines, tmplt=self)
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "vrf",
+ "getval": re.compile(
+ r"""
+ \s+vrf
+ \s(?P<vrf>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "vrf {{ vrf }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "vrf": "{{ vrf }}"
+ }
+ }
+ },
+ "shared": True,
+ },
+ {
+ "name": "process_id",
+ "getval": re.compile(
+ r"""
+ ospfv3
+ \s(?P<process_id>\S+)""",
+ re.VERBOSE,
+ ),
+ "setval": "router ospfv3 {{ process_id }}",
+ "result": {
+ "process_id": "{{ process_id }}",
+ },
+ "shared": True,
+ },
+ {
+ "name": "router_id",
+ "getval": re.compile(
+ r"""
+ \s+router-id
+ \s(?P<router_id>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "router-id {{ router_id }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "router_id": "{{ router_id }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "address_family",
+ "getval": re.compile(
+ r"""
+ \s+address-family
+ \s(?P<afi>\S+)
+ \s(?P<safi>\S+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "address-family {{ afi }} {{ safi }}",
+ "result": {
+ "address_family": {
+ "afi": "{{ afi }}",
+ "safi": "{{ safi }}",
+ }
+ },
+ 'shared': True,
+ },
+ {
+ "name": "area.default_cost",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)\s
+ default-cost\s(?P<default_cost>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "area {{ area_id }} default-cost {{ default_cost }}",
+ "compval": "default_cost",
+ "result": {
+ 'address_family': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "default_cost": "{{ default_cost|int }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.filter_list",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \sfilter-list
+ \sroute-map\s(?P<rmap>\S+)
+ \s(?P<dir>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "area {{ area_id }} filter-list route-map {{ route_map }} {{ direction }}",
+ "result": {
+ 'address_family': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "filter_list": [
+ {
+ "route_map": "{{ rmap }}",
+ "direction": "{{ dir }}",
+ },
+ ],
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.ranges",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \srange\s(?P<prefix>\S+)
+ \s*(cost)*\s*(?P<cost>\d+)*
+ \s*(?P<not_adver>not-advertise)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_ranges,
+ "remval": "area {{ area_id }} range {{ prefix }}",
+ "result": {
+ "address_family": {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "ranges": [
+ {
+ "prefix": "{{ prefix }}",
+ "cost": "{{ cost }}",
+ "not_advertise": "{{ not not not_adver }}",
+ },
+ ],
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "default_information.originate",
+ "getval": re.compile(
+ r"""
+ \s+default-information
+ \s(?P<originate>originate)
+ \s*(?P<always>always)*
+ \s*(route-map)*
+ \s*(?P<route_map>\S+)*$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_default_information,
+ "result": {
+ "address_family": {
+ "default_information": {
+ "originate": {
+ "set": "{{ True if originate is defined and always is undefined and route_map is undefined else None }}",
+ "always": "{{ not not always }}",
+ "route_map": "{{ route_map }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "distance",
+ "getval": re.compile(
+ r"""
+ \s+distance
+ \s(?P<distance>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": "distance {{ distance }}",
+ "result": {
+ "address_family": {
+ "distance": "{{ distance }}"
+ }
+ },
+ },
+ {
+ "name": "maximum_paths",
+ "getval": re.compile(
+ r"""
+ \s+maximum-paths
+ \s(?P<maximum_paths>\d+)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("maximum-paths {{ maximum_paths }}"),
+ "result": {
+ "address_family": {
+ "maximum_paths": "{{ maximum_paths }}"
+ }
+ },
+ },
+ {
+ "name": "redistribute",
+ "getval": re.compile(
+ r"""
+ \s+redistribute
+ \s(?P<protocol>\S+)
+ \s*(?P<id>\S+)*
+ \sroute-map\s(?P<rmap>\S+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_redistribute,
+ "result": {
+ "address_family": {
+ "redistribute": [
+ {
+ "protocol": "{{ protocol }}",
+ "id": "{{ id }}",
+ "route_map": "{{ rmap }}",
+ },
+ ]
+ }
+ },
+ },
+ {
+ "name": "summary_address",
+ "getval": re.compile(
+ r"""
+ \s+summary-address
+ \s(?P<prefix>\S+)
+ \s*(?P<not_adver>not-advertise)*
+ \s*(tag)*\s*(?P<tag>\d+)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_summary_address,
+ "result": {
+ "address_family": {
+ "summary_address": [
+ {
+ "prefix": "{{ prefix }}",
+ "not_advertise": "{{ not not not_adver }}",
+ "tag": "{{ tag }}",
+ },
+ ]
+ }
+ },
+ },
+ {
+ "name": "table_map",
+ "getval": re.compile(
+ r"""
+ \s+table-map
+ \s(?P<rmap>\S+)
+ \s*(?P<filter>filter)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_table_map,
+ "result": {
+ "address_family": {
+ "table_map": {
+ "name": "{{ rmap }}",
+ "filter": "{{ not not filter }}",
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.throttle.spf",
+ "getval": re.compile(
+ r"""
+ \s+timers\sthrottle\sspf
+ \s(?P<initial>\d+)
+ \s(?P<min>\d+)
+ \s(?P<max>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "timers throttle spf {{ timers.throttle.spf.initial_spf_delay }}"
+ " {{ timers.throttle.spf.min_hold_time }}"
+ " {{ timers.throttle.spf.max_wait_time }}",
+ "result": {
+ "address_family": {
+ "timers": {
+ "throttle": {
+ "spf": {
+ "initial_spf_delay": "{{ initial }}",
+ "min_hold_time": "{{ min }}",
+ "max_wait_time": "{{ max }}",
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.nssa",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \s(?P<nssa>nssa)
+ \s*(?P<no_sum>no-summary)*
+ \s*(?P<no_redis>no-redistribution)*
+ \s*(?P<def_info>default-information-originate)*
+ \s*(route-map)*\s*(?P<rmap>\S+)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_nssa,
+ "remval": "area {{ area_id }} nssa",
+ "compval": "nssa",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "nssa": {
+ "set": "{{ True if nssa is defined and no_sum is undefined and no_redis is undefined and \
+ def_info is undefined and rmap is undefined }}",
+ "no_summary": "{{ not not no_sum }}",
+ "no_redistribution": "{{ not not no_redis }}",
+ "default_information_originate": "{{ not not def_info }}",
+ "route_map": "{{ rmap }}",
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.nssa.translate",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)\snssa
+ \stranslate
+ \stype7
+ \s(?P<choice>always|never)
+ \s*(?P<supress_fa>supress-fa)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_nssa_translate,
+ "compval": "nssa.translate",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "nssa": {
+ "translate": {
+ "type7": {
+ "always": '{{ True if choice == "always" else None }}',
+ "never": '{{ True if choice == "never" else None }}',
+ "supress_fa": "{{ not not supress_fa }}",
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.stub",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \s(?P<stub>stub)
+ \s*(?P<no_summary>no-summary)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_area_stub,
+ "remval": "area {{ area_id }} stub",
+ "compval": "stub",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "stub": {
+ "set": "{{ True if stub is defined and no_summary is undefined else None }}",
+ "no_summary": "{{ not not no_summary }}",
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "area.virtual_link",
+ "getval": re.compile(
+ r"""
+ \s+area\s(?P<area_id>\S+)
+ \svirtual-link
+ \s(?P<virtual_link>\S+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "area {{ area_id }} virtual-link {{ virtual_link }}",
+ "compval": "virtual_link",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "areas": {
+ "{{ area_id }}": {
+ "area_id": "{{ area_id }}",
+ "virtual_link": "{{ virtual_link }}",
+ },
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "auto_cost",
+ "getval": re.compile(
+ r"""
+ \s+auto-cost\sreference-bandwidth\s
+ (?P<acrb>\d+)\s(?P<unit>\S+)$""",
+ re.VERBOSE,
+ ),
+ "setval": (
+ "auto-cost reference-bandwidth"
+ " {{ auto_cost.reference_bandwidth }}"
+ " {{ auto_cost.unit }}"
+ ),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "auto_cost": {
+ "reference_bandwidth": "{{ acrb }}",
+ "unit": "{{ unit }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "flush_routes",
+ "getval": re.compile(
+ r"""
+ \s+(?P<flush_routes>flush-routes)$""",
+ re.VERBOSE,
+ ),
+ "setval": "flush-routes",
+ "result": {
+ "flush_routes": "{{ not not flush_routes }}"
+ },
+ },
+ {
+ "name": "graceful_restart.set",
+ "getval": re.compile(
+ r"""
+ \s+(?P<graceful_restart>no\sgraceful-restart)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart",
+ "remval": "no graceful-restart",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "set": "{{ not graceful_restart }}"
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "graceful_restart.helper_disable",
+ "getval": re.compile(
+ r"""
+ \s+graceful-restart
+ \s+(?P<helper_disable>helper-disable)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart helper-disable",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "helper_disable": "{{ not not helper_disable }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "graceful_restart.grace_period",
+ "getval": re.compile(
+ r"""
+ \s+graceful-restart
+ \s+grace-period
+ \s+(?P<grace_period>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart grace-period {{ graceful_restart.grace_period }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "grace_period": "{{ grace_period }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "graceful_restart.planned_only",
+ "getval": re.compile(
+ r"""
+ \s+no
+ \s+graceful-restart
+ \s+(?P<planned_only>planned-only)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "graceful-restart planned-only",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "graceful_restart": {
+ "planned_only": "{{ not planned_only }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "isolate",
+ "getval": re.compile(
+ r"""
+ \s+(?P<isolate>isolate)$""",
+ re.VERBOSE,
+ ),
+ "setval": "isolate",
+ "result": {"isolate": "{{ not not isolate }}"},
+ },
+ {
+ "name": "log_adjacency_changes",
+ "getval": re.compile(
+ r"""
+ \s+(?P<log>log-adjacency-changes)
+ \s*(?P<detail>detail)*$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_log_adjacency_changes,
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "log_adjacency_changes": {
+ "log": "{{ True if log is defined and detail is undefined else None }}",
+ "detail": "{{ True if detail is defined else None }}",
+ }
+ }
+ }
+ },
+ },
+ {
+
+ "name": "max_lsa",
+ "getval": re.compile(
+ r"""
+ \s+max-lsa
+ \s(?P<max_gen_lsa>\d+)
+ \s*(?P<threshold>\d*)
+ \s*(?P<warning_only>warning-only)*
+ \s*(ignore-time)*\s*(?P<ig_time>\d*)
+ \s*(ignore-count)*\s*(?P<ig_count>\d*)
+ \s*(reset-time)*\s*(?P<rst_time>\d*)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_max_lsa,
+ "remval": "max-lsa {{ max_lsa.max_non_self_generated_lsa }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "max_lsa": {
+ "max_non_self_generated_lsa": "{{ max_gen_lsa }}",
+ "threshold": "{{ threshold }}",
+ "ignore_time": "{{ ig_time }}",
+ "ignore_count": "{{ ig_count }}",
+ "reset_time": "{{ rst_time }}",
+ "warning_only": "{{ not not warning_only }}",
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "max_metric",
+ "getval": re.compile(
+ r"""
+ \s+max-metric
+ \s+(?P<router_lsa>router-lsa)
+ \s*(?P<external_lsa>external-lsa)*
+ \s*(?P<max_metric_value>\d+)*
+ \s*(?P<stub_prefix_lsa>stub-prefix-lsa)*
+ \s*(?P<on_startup>on-startup)*
+ \s*(?P<wait_period>\d+)*
+ \s*(wait-for\sbgp)*
+ \s*(?P<bgp_asn>\d+)*
+ \s*(?P<inter_area_prefix_lsa>inter-area-prefix-lsa)*
+ \s*(?P<max_metric_summary_lsa>\d+)*
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_max_metric,
+ "remval": "max-metric router-lsa",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "max_metric": {
+ "router_lsa": {
+ "set": "{{ True if router_lsa is defined and (external_lsa is undefined) and (inter_area_prefix_lsa is undefined) and \
+ (stub_prefix_lsa is undefined) and (on_startup is undefined) else None }}",
+ "external_lsa": {
+ "set": "{{ True if external_lsa is defined and max_metric_value is undefined else None }}",
+ "max_metric_value": "{{ max_metric_value }}",
+ },
+ "stub_prefix_lsa": "{{ not not stub_prefix_lsa }}",
+ "on_startup": {
+ "set": "{{ True if on_startup is defined and (wait_period and bgp_asn) is undefined else None }}",
+ "wait_period": "{{ wait_period }}",
+ "wait_for_bgp_asn": "{{ bgp_asn }}",
+ },
+ "inter_area_prefix_lsa": {
+ "set": "{{ True if inter_area_prefix_lsa is defined and max_metric_summary_lsa is undefined else None }}",
+ "max_metric_value": "{{ max_metric_summary_lsa }}",
+ },
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "name_lookup",
+ "getval": re.compile(
+ r"""
+ \s+(?P<name_lookup>name-lookup)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("name-lookup"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "name_lookup": "{{ not not name_lookup }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "passive_interface.default",
+ "getval": re.compile(
+ r"""
+ \s+passive-interface
+ \s+(?P<default>default)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("passive-interface default"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "passive_interface": {"default": "{{ not not default }}"}
+ }
+ }
+ },
+ },
+ {
+ "name": "shutdown",
+ "getval": re.compile(
+ r"""
+ \s+(?P<shutdown>shutdown)$""",
+ re.VERBOSE,
+ ),
+ "setval": ("shutdown"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "shutdown": "{{ not not shutdown }}"
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.lsa_arrival",
+ "getval": re.compile(
+ r"""
+ \s+timers
+ \slsa-arrival
+ \s(?P<lsa_arrival_val>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": ("timers lsa-arrival {{ timers.lsa_arrival }}"),
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "lsa_arrival": "{{ lsa_arrival_val }}"
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.lsa_group_pacing",
+ "getval": re.compile(
+ r"""
+ \s+timers
+ \slsa-group-pacing
+ \s(?P<lsa_group_pacing>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "timers lsa-group-pacing {{ timers.lsa_group_pacing }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "lsa_group_pacing": "{{ lsa_group_pacing }}"
+ }
+ }
+ }
+ },
+ },
+ {
+ "name": "timers.throttle.lsa",
+ "getval": re.compile(
+ r"""
+ \s+timers\sthrottle\slsa
+ \s(?P<start>\d+)
+ \s(?P<hold>\d+)
+ \s(?P<max>\d+)
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "timers throttle lsa {{ timers.throttle.lsa.start_interval }}"
+ " {{ timers.throttle.lsa.hold_interval }}"
+ " {{ timers.throttle.lsa.max_interval }}",
+ "result": {
+ "vrfs": {
+ '{{ "vrf_" + vrf|d() }}': {
+ "timers": {
+ "throttle": {
+ "lsa": {
+ "start_interval": "{{ start }}",
+ "hold_interval": "{{ hold }}",
+ "max_interval": "{{ max }}",
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ # fmt: on
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/telemetry.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/telemetry.py
new file mode 100644
index 00000000..371de062
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/telemetry/telemetry.py
@@ -0,0 +1,276 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The nxos telemetry utility library
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+from copy import deepcopy
+
+
+def get_module_params_subsection(module_params, tms_config, resource_key=None):
+ """
+ Helper method to get a specific module_params subsection
+ """
+ mp = {}
+ if tms_config == "TMS_GLOBAL":
+ relevant_keys = [
+ "certificate",
+ "compression",
+ "source_interface",
+ "vrf",
+ ]
+ for key in relevant_keys:
+ mp[key] = module_params[key]
+
+ if tms_config == "TMS_DESTGROUP":
+ mp["destination_groups"] = []
+ for destgrp in module_params["destination_groups"]:
+ if destgrp["id"] == resource_key:
+ mp["destination_groups"].append(destgrp)
+
+ if tms_config == "TMS_SENSORGROUP":
+ mp["sensor_groups"] = []
+ for sensor in module_params["sensor_groups"]:
+ if sensor["id"] == resource_key:
+ mp["sensor_groups"].append(sensor)
+
+ if tms_config == "TMS_SUBSCRIPTION":
+ mp["subscriptions"] = []
+ for sensor in module_params["subscriptions"]:
+ if sensor["id"] == resource_key:
+ mp["subscriptions"].append(sensor)
+
+ return mp
+
+
+def valiate_input(playvals, type, module):
+ """
+ Helper method to validate playbook values for destination groups
+ """
+ if type == "destination_groups":
+ if not playvals.get("id"):
+ msg = "Invalid playbook value: {0}.".format(playvals)
+ msg += " Parameter <id> under <destination_groups> is required"
+ module.fail_json(msg=msg)
+ if playvals.get("destination") and not isinstance(
+ playvals["destination"], dict
+ ):
+ msg = "Invalid playbook value: {0}.".format(playvals)
+ msg += " Parameter <destination> under <destination_groups> must be a dict"
+ module.fail_json(msg=msg)
+ if not playvals.get("destination") and len(playvals) > 1:
+ msg = "Invalid playbook value: {0}.".format(playvals)
+ msg += " Playbook entry contains unrecongnized parameters."
+ msg += " Make sure <destination> keys under <destination_groups> are specified as follows:"
+ msg += " destination: {ip: <ip>, port: <port>, protocol: <prot>, encoding: <enc>}}"
+ module.fail_json(msg=msg)
+
+ if type == "sensor_groups":
+ if not playvals.get("id"):
+ msg = "Invalid playbook value: {0}.".format(playvals)
+ msg += " Parameter <id> under <sensor_groups> is required"
+ module.fail_json(msg=msg)
+ if playvals.get("path") and "name" not in playvals["path"].keys():
+ msg = "Invalid playbook value: {0}.".format(playvals)
+ msg += (
+ " Parameter <path> under <sensor_groups> requires <name> key"
+ )
+ module.fail_json(msg=msg)
+
+
+def get_instance_data(key, cr_key, cr, existing_key):
+ """
+ Helper method to get instance data used to populate list structure in config
+ fact dictionary
+ """
+ data = {}
+ if existing_key is None:
+ instance = None
+ else:
+ instance = cr._ref[cr_key]["existing"][existing_key]
+
+ patterns = {
+ "destination_groups": r"destination-group (\d+)",
+ "sensor_groups": r"sensor-group (\d+)",
+ "subscriptions": r"subscription (\d+)",
+ }
+ if key in patterns.keys():
+ m = re.search(patterns[key], cr._ref["_resource_key"])
+ instance_key = m.group(1)
+ data = {"id": instance_key, cr_key: instance}
+
+ # Remove None values
+ data = dict((k, v) for k, v in data.items() if v is not None)
+ return data
+
+
+def cr_key_lookup(key, mo):
+ """
+ Helper method to get instance key value for Managed Object (mo)
+ """
+ cr_keys = [key]
+ if key == "destination_groups" and mo == "TMS_DESTGROUP":
+ cr_keys = ["destination"]
+ elif key == "sensor_groups" and mo == "TMS_SENSORGROUP":
+ cr_keys = ["data_source", "path"]
+ elif key == "subscriptions" and mo == "TMS_SUBSCRIPTION":
+ cr_keys = ["destination_group", "sensor_group"]
+
+ return cr_keys
+
+
+def normalize_data(cmd_ref):
+ """ Normalize playbook values and get_exisiting data """
+
+ playval = cmd_ref._ref.get("destination").get("playval")
+ existing = cmd_ref._ref.get("destination").get("existing")
+
+ dest_props = ["protocol", "encoding"]
+ if playval:
+ for prop in dest_props:
+ for key in playval.keys():
+ playval[key][prop] = playval[key][prop].lower()
+ if existing:
+ for key in existing.keys():
+ for prop in dest_props:
+ existing[key][prop] = existing[key][prop].lower()
+
+
+def remove_duplicate_context(cmds):
+ """ Helper method to remove duplicate telemetry context commands """
+ if not cmds:
+ return cmds
+ feature_indices = [
+ i for i, x in enumerate(cmds) if x == "feature telemetry"
+ ]
+ telemetry_indices = [i for i, x in enumerate(cmds) if x == "telemetry"]
+ if len(feature_indices) == 1 and len(telemetry_indices) == 1:
+ return cmds
+ if len(feature_indices) == 1 and not telemetry_indices:
+ return cmds
+ if len(telemetry_indices) == 1 and not feature_indices:
+ return cmds
+ if feature_indices and feature_indices[-1] > 1:
+ cmds.pop(feature_indices[-1])
+ return remove_duplicate_context(cmds)
+ if telemetry_indices and telemetry_indices[-1] > 1:
+ cmds.pop(telemetry_indices[-1])
+ return remove_duplicate_context(cmds)
+
+
+def get_setval_path(module_or_path_data):
+ """ Build setval for path parameter based on playbook inputs
+ Full Command:
+ - path {name} depth {depth} query-condition {query_condition} filter-condition {filter_condition}
+ Required:
+ - path {name}
+ Optional:
+ - depth {depth}
+ - query-condition {query_condition},
+ - filter-condition {filter_condition}
+ """
+ if isinstance(module_or_path_data, dict):
+ path = module_or_path_data
+ else:
+ path = module_or_path_data.params["config"]["sensor_groups"][0].get(
+ "path"
+ )
+ if path is None:
+ return path
+
+ setval = "path {name}"
+ if "depth" in path.keys():
+ if path.get("depth") != "None":
+ setval = setval + " depth {depth}"
+ if "query_condition" in path.keys():
+ if path.get("query_condition") != "None":
+ setval = setval + " query-condition {query_condition}"
+ if "filter_condition" in path.keys():
+ if path.get("filter_condition") != "None":
+ setval = setval + " filter-condition {filter_condition}"
+
+ return setval
+
+
+def remove_duplicate_commands(commands_list):
+ # Remove any duplicate commands.
+ # pylint: disable=unnecessary-lambda
+ return sorted(set(commands_list), key=lambda x: commands_list.index(x))
+
+
+def massage_data(have_or_want):
+ # Massage non global into a data structure that is indexed by id and
+ # normalized for destination_groups, sensor_groups and subscriptions.
+ data = deepcopy(have_or_want)
+ massaged = {}
+ massaged["destination_groups"] = {}
+ massaged["sensor_groups"] = {}
+ massaged["subscriptions"] = {}
+ from pprint import pprint
+
+ for subgroup in ["destination_groups", "sensor_groups", "subscriptions"]:
+ for item in data.get(subgroup, []):
+ id = str(item.get("id"))
+ if id not in massaged[subgroup].keys():
+ massaged[subgroup][id] = []
+ item.pop("id")
+ if not item:
+ item = None
+ else:
+ if item.get("destination"):
+ if item.get("destination").get("port"):
+ item["destination"]["port"] = str(
+ item["destination"]["port"]
+ )
+ if item.get("destination").get("protocol"):
+ item["destination"]["protocol"] = item["destination"][
+ "protocol"
+ ].lower()
+ if item.get("destination").get("encoding"):
+ item["destination"]["encoding"] = item["destination"][
+ "encoding"
+ ].lower()
+ if item.get("path"):
+ for key in [
+ "filter_condition",
+ "query_condition",
+ "depth",
+ ]:
+ if item.get("path").get(key) == "None":
+ del item["path"][key]
+ if item.get("path").get("depth") is not None:
+ item["path"]["depth"] = str(item["path"]["depth"])
+ if item.get("destination_group"):
+ item["destination_group"] = str(item["destination_group"])
+ if item.get("sensor_group"):
+ if item.get("sensor_group").get("id"):
+ item["sensor_group"]["id"] = str(
+ item["sensor_group"]["id"]
+ )
+ if item.get("sensor_group").get("sample_interval"):
+ item["sensor_group"]["sample_interval"] = str(
+ item["sensor_group"]["sample_interval"]
+ )
+ if item.get("destination_group") and item.get("sensor_group"):
+ item_copy = deepcopy(item)
+ del item_copy["sensor_group"]
+ del item["destination_group"]
+ massaged[subgroup][id].append(item_copy)
+ massaged[subgroup][id].append(item)
+ continue
+ if item.get("path") and item.get("data_source"):
+ item_copy = deepcopy(item)
+ del item_copy["data_source"]
+ del item["path"]
+ massaged[subgroup][id].append(item_copy)
+ massaged[subgroup][id].append(item)
+ continue
+ massaged[subgroup][id].append(item)
+ return massaged
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/utils.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/utils.py
new file mode 100644
index 00000000..d2da0aa6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/module_utils/network/nxos/utils/utils.py
@@ -0,0 +1,145 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import socket
+
+from ansible.module_utils.six import iteritems
+
+
+def search_obj_in_list(name, lst, identifier):
+ for o in lst:
+ if o[identifier] == name:
+ return o
+ return None
+
+
+def flatten_dict(x):
+ result = {}
+ if not isinstance(x, dict):
+ return result
+
+ for key, value in iteritems(x):
+ if isinstance(value, dict):
+ result.update(flatten_dict(value))
+ else:
+ result[key] = value
+
+ return result
+
+
+def validate_ipv4_addr(address):
+ address = address.split("/")[0]
+ try:
+ socket.inet_aton(address)
+ except socket.error:
+ return False
+ return address.count(".") == 3
+
+
+def validate_ipv6_addr(address):
+ address = address.split("/")[0]
+ try:
+ socket.inet_pton(socket.AF_INET6, address)
+ except socket.error:
+ return False
+ return True
+
+
+def normalize_interface(name):
+ """Return the normalized interface name
+ """
+ if not name:
+ return
+
+ def _get_number(name):
+ digits = ""
+ for char in name:
+ if char.isdigit() or char in "/.":
+ digits += char
+ return digits
+
+ if name.lower().startswith("et"):
+ if_type = "Ethernet"
+ elif name.lower().startswith("vl"):
+ if_type = "Vlan"
+ elif name.lower().startswith("lo"):
+ if_type = "loopback"
+ elif name.lower().startswith("po"):
+ if_type = "port-channel"
+ elif name.lower().startswith("nv"):
+ if_type = "nve"
+ else:
+ if_type = None
+
+ number_list = name.split(" ")
+ if len(number_list) == 2:
+ number = number_list[-1].strip()
+ else:
+ number = _get_number(name)
+
+ if if_type:
+ proper_interface = if_type + number
+ else:
+ proper_interface = name
+
+ return proper_interface
+
+
+def get_interface_type(interface):
+ """Gets the type of interface
+ """
+ if interface.upper().startswith("ET"):
+ return "ethernet"
+ elif interface.upper().startswith("VL"):
+ return "svi"
+ elif interface.upper().startswith("LO"):
+ return "loopback"
+ elif interface.upper().startswith("MG"):
+ return "management"
+ elif interface.upper().startswith("MA"):
+ return "management"
+ elif interface.upper().startswith("PO"):
+ return "portchannel"
+ elif interface.upper().startswith("NV"):
+ return "nve"
+ else:
+ return "unknown"
+
+
+def remove_rsvd_interfaces(interfaces):
+ """Exclude reserved interfaces from user management
+ """
+ if not interfaces:
+ return []
+ return [
+ i for i in interfaces if get_interface_type(i["name"]) != "management"
+ ]
+
+
+def vlan_range_to_list(vlans):
+ result = []
+ if vlans:
+ for part in vlans.split(","):
+ if part == "none":
+ break
+ if "-" in part:
+ a, b = part.split("-")
+ a, b = int(a), int(b)
+ result.extend(range(a, b + 1))
+ else:
+ a = int(part)
+ result.append(a)
+ return numerical_sort(result)
+ return result
+
+
+def numerical_sort(string_int_list):
+ """Sorts list of integers that are digits in numerical order.
+ """
+
+ as_int_list = []
+
+ for vlan in string_int_list:
+ as_int_list.append(int(vlan))
+ as_int_list.sort()
+ return as_int_list
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server.py
new file mode 100644
index 00000000..bfb33fd1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server.py
@@ -0,0 +1,364 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_aaa_server
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages AAA server global configuration.
+description:
+- Manages AAA server global configuration
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- The server_type parameter is always required.
+- If encrypt_type is not supplied, the global AAA server key will be stored as encrypted
+ (type 7).
+- Changes to the global AAA server key with encrypt_type=0 are not idempotent.
+- state=default will set the supplied parameters to their default values. The parameters
+ that you want to default must also be set to default. If global_key=default, the
+ global key will be removed.
+options:
+ server_type:
+ description:
+ - The server type is either radius or tacacs.
+ required: true
+ choices:
+ - radius
+ - tacacs
+ type: str
+ global_key:
+ description:
+ - Global AAA shared secret or keyword 'default'.
+ type: str
+ encrypt_type:
+ description:
+ - The state of encryption applied to the entered global key. O clear text, 7 encrypted.
+ Type-6 encryption is not supported.
+ choices:
+ - '0'
+ - '7'
+ type: str
+ deadtime:
+ description:
+ - Duration for which a non-reachable AAA server is skipped, in minutes or keyword
+ 'default. Range is 1-1440. Device default is 0.
+ type: str
+ server_timeout:
+ description:
+ - Global AAA server timeout period, in seconds or keyword 'default. Range is 1-60.
+ Device default is 5.
+ type: str
+ directed_request:
+ description:
+ - Enables direct authentication requests to AAA server or keyword 'default' Device
+ default is disabled.
+ choices:
+ - enabled
+ - disabled
+ - default
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - default
+ type: str
+"""
+
+EXAMPLES = """
+# Radius Server Basic settings
+- name: Radius Server Basic settings
+ cisco.nxos.nxos_aaa_server:
+ server_type: radius
+ server_timeout: 9
+ deadtime: 20
+ directed_request: enabled
+
+# Tacacs Server Basic settings
+- name: Tacacs Server Basic settings
+ cisco.nxos.nxos_aaa_server:
+ server_type: tacacs
+ server_timeout: 8
+ deadtime: 19
+ directed_request: disabled
+
+# Setting Global Key
+- name: AAA Server Global Key
+ cisco.nxos.nxos_aaa_server:
+ server_type: radius
+ global_key: test_key
+"""
+
+RETURN = """
+commands:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["radius-server deadtime 22", "radius-server timeout 11",
+ "radius-server directed-request"]
+"""
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+PARAM_TO_DEFAULT_KEYMAP = {
+ "server_timeout": "5",
+ "deadtime": "0",
+ "directed_request": "disabled",
+}
+
+
+def execute_show_command(command, module):
+ command = {"command": command, "output": "text"}
+
+ return run_commands(module, command)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_aaa_server_info(server_type, module):
+ aaa_server_info = {}
+ server_command = "show {0}-server".format(server_type)
+ request_command = "show {0}-server directed-request".format(server_type)
+ global_key_command = "show run | sec {0}".format(server_type)
+ aaa_regex = r".*{0}-server\skey\s\d\s+(?P<key>\S+).*".format(server_type)
+
+ server_body = execute_show_command(server_command, module)[0]
+
+ split_server = server_body.splitlines()
+
+ for line in split_server:
+ if line.startswith("timeout"):
+ aaa_server_info["server_timeout"] = line.split(":")[1]
+
+ elif line.startswith("deadtime"):
+ aaa_server_info["deadtime"] = line.split(":")[1]
+
+ request_body = execute_show_command(request_command, module)[0]
+
+ if bool(request_body):
+ aaa_server_info["directed_request"] = request_body.replace("\n", "")
+ else:
+ aaa_server_info["directed_request"] = "disabled"
+
+ key_body = execute_show_command(global_key_command, module)[0]
+
+ try:
+ match_global_key = re.match(aaa_regex, key_body, re.DOTALL)
+ group_key = match_global_key.groupdict()
+ aaa_server_info["global_key"] = group_key["key"].replace('"', "")
+ except (AttributeError, TypeError):
+ aaa_server_info["global_key"] = None
+
+ return aaa_server_info
+
+
+def config_aaa_server(params, server_type):
+ cmds = []
+
+ deadtime = params.get("deadtime")
+ server_timeout = params.get("server_timeout")
+ directed_request = params.get("directed_request")
+ encrypt_type = params.get("encrypt_type", "7")
+ global_key = params.get("global_key")
+
+ if deadtime is not None:
+ cmds.append("{0}-server deadtime {1}".format(server_type, deadtime))
+
+ if server_timeout is not None:
+ cmds.append(
+ "{0}-server timeout {1}".format(server_type, server_timeout)
+ )
+
+ if directed_request is not None:
+ if directed_request == "enabled":
+ cmds.append("{0}-server directed-request".format(server_type))
+ elif directed_request == "disabled":
+ cmds.append("no {0}-server directed-request".format(server_type))
+
+ if global_key is not None:
+ cmds.append(
+ "{0}-server key {1} {2}".format(
+ server_type, encrypt_type, global_key
+ )
+ )
+
+ return cmds
+
+
+def default_aaa_server(existing, params, server_type):
+ cmds = []
+
+ deadtime = params.get("deadtime")
+ server_timeout = params.get("server_timeout")
+ directed_request = params.get("directed_request")
+ global_key = params.get("global_key")
+ existing_key = existing.get("global_key")
+
+ if (
+ deadtime is not None
+ and existing.get("deadtime") != PARAM_TO_DEFAULT_KEYMAP["deadtime"]
+ ):
+ cmds.append("no {0}-server deadtime 1".format(server_type))
+
+ if (
+ server_timeout is not None
+ and existing.get("server_timeout")
+ != PARAM_TO_DEFAULT_KEYMAP["server_timeout"]
+ ):
+ cmds.append("no {0}-server timeout 1".format(server_type))
+
+ if (
+ directed_request is not None
+ and existing.get("directed_request")
+ != PARAM_TO_DEFAULT_KEYMAP["directed_request"]
+ ):
+ cmds.append("no {0}-server directed-request".format(server_type))
+
+ if global_key is not None and existing_key is not None:
+ cmds.append(
+ "no {0}-server key 7 {1}".format(server_type, existing_key)
+ )
+
+ return cmds
+
+
+def main():
+ argument_spec = dict(
+ server_type=dict(
+ type="str", choices=["radius", "tacacs"], required=True
+ ),
+ global_key=dict(type="str", no_log=True),
+ encrypt_type=dict(type="str", choices=["0", "7"]),
+ deadtime=dict(type="str"),
+ server_timeout=dict(type="str"),
+ directed_request=dict(
+ type="str", choices=["enabled", "disabled", "default"]
+ ),
+ state=dict(choices=["default", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ server_type = module.params["server_type"]
+ global_key = module.params["global_key"]
+ encrypt_type = module.params["encrypt_type"]
+ deadtime = module.params["deadtime"]
+ server_timeout = module.params["server_timeout"]
+ directed_request = module.params["directed_request"]
+ state = module.params["state"]
+
+ if encrypt_type and not global_key:
+ module.fail_json(msg="encrypt_type must be used with global_key.")
+
+ args = dict(
+ server_type=server_type,
+ global_key=global_key,
+ encrypt_type=encrypt_type,
+ deadtime=deadtime,
+ server_timeout=server_timeout,
+ directed_request=directed_request,
+ )
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ existing = get_aaa_server_info(server_type, module)
+
+ commands = []
+ if state == "present":
+ if deadtime:
+ try:
+ if int(deadtime) < 0 or int(deadtime) > 1440:
+ raise ValueError
+ except ValueError:
+ module.fail_json(
+ msg="deadtime must be an integer between 0 and 1440"
+ )
+
+ if server_timeout:
+ try:
+ if int(server_timeout) < 1 or int(server_timeout) > 60:
+ raise ValueError
+ except ValueError:
+ module.fail_json(
+ msg="server_timeout must be an integer between 1 and 60"
+ )
+
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = config_aaa_server(delta, server_type)
+ if command:
+ commands.append(command)
+
+ elif state == "default":
+ for key, value in proposed.items():
+ if key != "server_type" and value != "default":
+ module.fail_json(
+ msg='Parameters must be set to "default"'
+ "when state=default"
+ )
+ command = default_aaa_server(existing, proposed, server_type)
+ if command:
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server_host.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server_host.py
new file mode 100644
index 00000000..7949ca28
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_aaa_server_host.py
@@ -0,0 +1,383 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_aaa_server_host
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages AAA server host-specific configuration.
+description:
+- Manages AAA server host-specific configuration.
+version_added: 1.0.0
+author: Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Changes to the host key (shared secret) are not idempotent for type 0.
+- If C(state=absent) removes the whole host configuration.
+options:
+ server_type:
+ description:
+ - The server type is either radius or tacacs.
+ required: true
+ choices:
+ - radius
+ - tacacs
+ type: str
+ address:
+ description:
+ - Address or name of the radius or tacacs host.
+ required: true
+ type: str
+ key:
+ description:
+ - Shared secret for the specified host or keyword 'default'.
+ type: str
+ encrypt_type:
+ description:
+ - The state of encryption applied to the entered key. O for clear text, 7 for
+ encrypted. Type-6 encryption is not supported.
+ choices:
+ - '0'
+ - '7'
+ type: str
+ host_timeout:
+ description:
+ - Timeout period for specified host, in seconds or keyword 'default. Range is
+ 1-60.
+ type: str
+ auth_port:
+ description:
+ - Alternate UDP port for RADIUS authentication or keyword 'default'.
+ type: str
+ acct_port:
+ description:
+ - Alternate UDP port for RADIUS accounting or keyword 'default'.
+ type: str
+ tacacs_port:
+ description:
+ - Alternate TCP port TACACS Server or keyword 'default'.
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+EXAMPLES = """
+# Radius Server Host Basic settings
+- name: Radius Server Host Basic settings
+ cisco.nxos.nxos_aaa_server_host:
+ state: present
+ server_type: radius
+ address: 1.2.3.4
+ acct_port: 2084
+ host_timeout: 10
+
+# Radius Server Host Key Configuration
+- name: Radius Server Host Key Configuration
+ cisco.nxos.nxos_aaa_server_host:
+ state: present
+ server_type: radius
+ address: 1.2.3.4
+ key: hello
+ encrypt_type: 7
+
+# TACACS Server Host Configuration
+- name: Tacacs Server Host Configuration
+ cisco.nxos.nxos_aaa_server_host:
+ state: present
+ server_type: tacacs
+ tacacs_port: 89
+ host_timeout: 10
+ address: 5.6.7.8
+
+"""
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"address": "1.2.3.4", "auth_port": "2084",
+ "host_timeout": "10", "server_type": "radius"}
+existing:
+ description:
+ - k/v pairs of existing configuration
+ returned: always
+ type: dict
+ sample: {}
+end_state:
+ description: k/v pairs of configuration after module execution
+ returned: always
+ type: dict
+ sample: {"address": "1.2.3.4", "auth_port": "2084",
+ "host_timeout": "10", "server_type": "radius"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["radius-server host 1.2.3.4 auth-port 2084 timeout 10"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ device_info = get_capabilities(module)
+ network_api = device_info.get("network_api", "nxapi")
+
+ if network_api == "cliconf":
+ cmds = [command]
+ body = run_commands(module, cmds)
+ elif network_api == "nxapi":
+ cmds = {"command": command, "output": "text"}
+ body = run_commands(module, cmds)
+
+ return body
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_aaa_host_info(module, server_type, address):
+ aaa_host_info = {}
+ command = "show run | inc {0}-server.host.{1}".format(server_type, address)
+
+ body = execute_show_command(command, module)[0]
+ if body:
+ try:
+ if "radius" in body:
+ pattern = (
+ r"\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+auth-port (\d+))?"
+ r"(?:\s+acct-port (\d+))?(?:\s+authentication)?"
+ r"(?:\s+accounting)?(?:\s+timeout (\d+))?"
+ )
+ match = re.search(pattern, body)
+ aaa_host_info["key"] = match.group(1)
+ if aaa_host_info["key"]:
+ aaa_host_info["key"] = aaa_host_info["key"].replace(
+ '"', ""
+ )
+ aaa_host_info["encrypt_type"] = "7"
+ aaa_host_info["auth_port"] = match.group(2)
+ aaa_host_info["acct_port"] = match.group(3)
+ aaa_host_info["host_timeout"] = match.group(4)
+ elif "tacacs" in body:
+ pattern = (
+ r"\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+port (\d+))?"
+ r"(?:\s+timeout (\d+))?"
+ )
+ match = re.search(pattern, body)
+ aaa_host_info["key"] = match.group(1)
+ if aaa_host_info["key"]:
+ aaa_host_info["key"] = aaa_host_info["key"].replace(
+ '"', ""
+ )
+ aaa_host_info["encrypt_type"] = "7"
+ aaa_host_info["tacacs_port"] = match.group(2)
+ aaa_host_info["host_timeout"] = match.group(3)
+
+ aaa_host_info["server_type"] = server_type
+ aaa_host_info["address"] = address
+ except TypeError:
+ return {}
+ else:
+ return {}
+
+ return aaa_host_info
+
+
+def config_aaa_host(server_type, address, params, existing):
+ cmds = []
+ cmd_str = "{0}-server host {1}".format(server_type, address)
+ cmd_no_str = "no " + cmd_str
+
+ key = params.get("key")
+ enc_type = params.get("encrypt_type", "")
+
+ defval = False
+ nondef = False
+
+ if key:
+ if key != "default":
+ cmds.append(cmd_str + " key {0} {1}".format(enc_type, key))
+ else:
+ cmds.append(cmd_no_str + " key 7 {0}".format(existing.get("key")))
+
+ locdict = {
+ "auth_port": "auth-port",
+ "acct_port": "acct-port",
+ "tacacs_port": "port",
+ "host_timeout": "timeout",
+ }
+
+ # platform CLI needs the keywords in the following order
+ for key in ["auth_port", "acct_port", "tacacs_port", "host_timeout"]:
+ item = params.get(key)
+ if item:
+ if item != "default":
+ cmd_str += " {0} {1}".format(locdict.get(key), item)
+ nondef = True
+ else:
+ cmd_no_str += " {0} 1".format(locdict.get(key))
+ defval = True
+ if defval:
+ cmds.append(cmd_no_str)
+ if nondef or not existing:
+ cmds.append(cmd_str)
+
+ return cmds
+
+
+def main():
+ argument_spec = dict(
+ server_type=dict(choices=["radius", "tacacs"], required=True),
+ address=dict(type="str", required=True),
+ key=dict(type="str"),
+ encrypt_type=dict(type="str", choices=["0", "7"]),
+ host_timeout=dict(type="str"),
+ auth_port=dict(type="str"),
+ acct_port=dict(type="str"),
+ tacacs_port=dict(type="str"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ server_type = module.params["server_type"]
+ address = module.params["address"]
+ key = module.params["key"]
+ encrypt_type = module.params["encrypt_type"]
+ host_timeout = module.params["host_timeout"]
+ auth_port = module.params["auth_port"]
+ acct_port = module.params["acct_port"]
+ tacacs_port = module.params["tacacs_port"]
+ state = module.params["state"]
+
+ args = dict(
+ server_type=server_type,
+ address=address,
+ key=key,
+ encrypt_type=encrypt_type,
+ host_timeout=host_timeout,
+ auth_port=auth_port,
+ acct_port=acct_port,
+ tacacs_port=tacacs_port,
+ )
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ changed = False
+
+ if encrypt_type and not key:
+ module.fail_json(msg="encrypt_type must be used with key")
+
+ if tacacs_port and server_type != "tacacs":
+ module.fail_json(
+ msg="tacacs_port can only be used with server_type=tacacs"
+ )
+
+ if (auth_port or acct_port) and server_type != "radius":
+ module.fail_json(
+ msg="auth_port and acct_port can only be used"
+ "when server_type=radius"
+ )
+
+ existing = get_aaa_host_info(module, server_type, address)
+ end_state = existing
+
+ commands = []
+ delta = {}
+ if state == "present":
+ if not existing:
+ delta = proposed
+ else:
+ for key, value in proposed.items():
+ if key == "encrypt_type":
+ delta[key] = value
+ if value != existing.get(key):
+ if value != "default" or existing.get(key):
+ delta[key] = value
+
+ command = config_aaa_host(server_type, address, delta, existing)
+ if command:
+ commands.append(command)
+
+ elif state == "absent":
+ intersect = dict(set(proposed.items()).intersection(existing.items()))
+ if intersect.get("address") and intersect.get("server_type"):
+ command = "no {0}-server host {1}".format(
+ intersect.get("server_type"), intersect.get("address")
+ )
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+ end_state = get_aaa_host_info(module, server_type, address)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+ results["end_state"] = end_state
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl.py
new file mode 100644
index 00000000..84b5acbc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl.py
@@ -0,0 +1,698 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_acl
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manages access
+ list entries for ACLs.
+description:
+- Manages access list entries for ACLs.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+deprecated:
+ alternative: nxos_acls
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the ACE if it exists.
+- C(state=delete_acl) deletes the ACL if it exists.
+- For idempotency, use port numbers for the src/dest port params like I(src_port1)
+ and names for the well defined protocols for the I(proto) param.
+- Although this module is idempotent in that if the ace as presented in the task is
+ identical to the one on the switch, no changes will be made. If there is any difference,
+ what is in Ansible will be pushed (configured options will be overridden). This
+ is to improve security, but at the same time remember an ACE is removed, then re-added,
+ so if there is a change, the new ACE will be exactly what parameters you are sending
+ to the module.
+options:
+ seq:
+ description:
+ - Sequence number of the entry (ACE).
+ type: str
+ name:
+ description:
+ - Case sensitive name of the access list (ACL).
+ required: true
+ type: str
+ action:
+ description:
+ - Action of the ACE.
+ choices:
+ - permit
+ - deny
+ - remark
+ type: str
+ remark:
+ description:
+ - If action is set to remark, this is the description.
+ type: str
+ proto:
+ description:
+ - Port number or protocol (as supported by the switch).
+ type: str
+ src:
+ description:
+ - Source ip and mask using IP/MASK notation and supports keyword 'any'.
+ type: str
+ src_port_op:
+ description:
+ - Source port operands such as eq, neq, gt, lt, range.
+ choices:
+ - any
+ - eq
+ - gt
+ - lt
+ - neq
+ - range
+ type: str
+ src_port1:
+ description:
+ - Port/protocol and also first (lower) port when using range operand.
+ type: str
+ src_port2:
+ description:
+ - Second (end) port when using range operand.
+ type: str
+ dest:
+ description:
+ - Destination ip and mask using IP/MASK notation and supports the keyword 'any'.
+ type: str
+ dest_port_op:
+ description:
+ - Destination port operands such as eq, neq, gt, lt, range.
+ choices:
+ - any
+ - eq
+ - gt
+ - lt
+ - neq
+ - range
+ type: str
+ dest_port1:
+ description:
+ - Port/protocol and also first (lower) port when using range operand.
+ type: str
+ dest_port2:
+ description:
+ - Second (end) port when using range operand.
+ type: str
+ log:
+ description:
+ - Log matches against this entry.
+ choices:
+ - enable
+ type: str
+ urg:
+ description:
+ - Match on the URG bit.
+ choices:
+ - enable
+ type: str
+ ack:
+ description:
+ - Match on the ACK bit.
+ choices:
+ - enable
+ type: str
+ psh:
+ description:
+ - Match on the PSH bit.
+ choices:
+ - enable
+ type: str
+ rst:
+ description:
+ - Match on the RST bit.
+ choices:
+ - enable
+ type: str
+ syn:
+ description:
+ - Match on the SYN bit.
+ choices:
+ - enable
+ type: str
+ fin:
+ description:
+ - Match on the FIN bit.
+ choices:
+ - enable
+ type: str
+ established:
+ description:
+ - Match established connections.
+ choices:
+ - enable
+ type: str
+ fragments:
+ description:
+ - Check non-initial fragments.
+ choices:
+ - enable
+ type: str
+ time_range:
+ description:
+ - Name of time-range to apply.
+ type: str
+ precedence:
+ description:
+ - Match packets with given precedence.
+ choices:
+ - critical
+ - flash
+ - flash-override
+ - immediate
+ - internet
+ - network
+ - priority
+ - routine
+ type: str
+ dscp:
+ description:
+ - Match packets with given dscp value.
+ choices:
+ - af11
+ - af12
+ - af13
+ - af21
+ - af22
+ - af23
+ - af31
+ - af32
+ - af33
+ - af41
+ - af42
+ - af43
+ - cs1
+ - cs2
+ - cs3
+ - cs4
+ - cs5
+ - cs6
+ - cs7
+ - default
+ - ef
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ - delete_acl
+ type: str
+
+
+"""
+
+EXAMPLES = """
+# configure ACL ANSIBLE
+- cisco.nxos.nxos_acl:
+ name: ANSIBLE
+ seq: 10
+ action: permit
+ proto: tcp
+ src: 192.0.2.1/24
+ dest: any
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["ip access-list ANSIBLE", "10 permit tcp 192.0.2.1/24 any"]
+"""
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module, check_rc=True):
+ command += " | json"
+ cmds = [command]
+ body = run_commands(module, cmds, check_rc=check_rc)
+ return body
+
+
+def get_acl(module, acl_name, seq_number):
+ command = "show ip access-list"
+ new_acl = []
+ saveme = {}
+ acl_body = {}
+
+ body = execute_show_command(command, module, check_rc=False)
+ if "Structured output unsupported" in repr(body):
+ # Some older versions raise 501 and return a string when no ACLs exist
+ return {}, []
+
+ if body and body[0]:
+ all_acl_body = body[0]["TABLE_ip_ipv6_mac"]["ROW_ip_ipv6_mac"]
+ else:
+ # no access-lists configured on the device
+ return {}, []
+
+ if isinstance(all_acl_body, dict):
+ # Only 1 ACL configured.
+ if all_acl_body.get("acl_name") == acl_name:
+ acl_body = all_acl_body
+ else:
+ for acl in all_acl_body:
+ if acl.get("acl_name") == acl_name:
+ acl_body = acl
+ break
+
+ try:
+ acl_entries = acl_body["TABLE_seqno"]["ROW_seqno"]
+ acl_name = acl_body.get("acl_name")
+ except KeyError: # could be raised if no ACEs are configured for an ACL
+ return {}, [{"acl": "no_entries"}]
+
+ if isinstance(acl_entries, dict):
+ acl_entries = [acl_entries]
+
+ for each in acl_entries:
+ temp = {}
+ options = {}
+ remark = each.get("remark")
+
+ temp["name"] = acl_name
+ temp["seq"] = str(each.get("seqno"))
+
+ if remark:
+ temp["remark"] = remark
+ temp["action"] = "remark"
+ else:
+ temp["action"] = each.get("permitdeny")
+ temp["proto"] = str(
+ each.get("proto", each.get("proto_str", each.get("ip")))
+ )
+ temp["src"] = each.get("src_any", each.get("src_ip_prefix"))
+ temp["src_port_op"] = each.get("src_port_op")
+ temp["src_port1"] = each.get("src_port1_num")
+ temp["src_port2"] = each.get("src_port2_num")
+ temp["dest"] = each.get("dest_any", each.get("dest_ip_prefix"))
+ temp["dest_port_op"] = each.get("dest_port_op")
+ temp["dest_port1"] = each.get("dest_port1_num")
+ temp["dest_port2"] = each.get("dest_port2_num")
+
+ options["log"] = each.get("log")
+ options["urg"] = each.get("urg")
+ options["ack"] = each.get("ack")
+ options["psh"] = each.get("psh")
+ options["rst"] = each.get("rst")
+ options["syn"] = each.get("syn")
+ options["fin"] = each.get("fin")
+ options["established"] = each.get("established")
+ options["dscp"] = each.get("dscp_str")
+ options["precedence"] = each.get("precedence_str")
+ options["fragments"] = each.get("fragments")
+ options["time_range"] = each.get("timerange")
+
+ keep = {}
+ for key, value in temp.items():
+ if value:
+ keep[key] = value
+
+ options_no_null = {}
+ for key, value in options.items():
+ if value is not None:
+ options_no_null[key] = value
+ keep["options"] = options_no_null
+
+ if keep.get("seq") == seq_number:
+ saveme = dict(keep)
+
+ new_acl.append(keep)
+
+ return saveme, new_acl
+
+
+def _acl_operand(operand, srcp1, sprcp2):
+ sub_entry = " " + operand
+
+ if operand == "range":
+ sub_entry += " " + srcp1 + " " + sprcp2
+ else:
+ sub_entry += " " + srcp1
+
+ return sub_entry
+
+
+def config_core_acl(proposed):
+ seq = proposed.get("seq")
+ action = proposed.get("action")
+ remark = proposed.get("remark")
+ proto = proposed.get("proto")
+ src = proposed.get("src")
+ src_port_op = proposed.get("src_port_op")
+ src_port1 = proposed.get("src_port1")
+ src_port2 = proposed.get("src_port2")
+
+ dest = proposed.get("dest")
+ dest_port_op = proposed.get("dest_port_op")
+ dest_port1 = proposed.get("dest_port1")
+ dest_port2 = proposed.get("dest_port2")
+
+ ace_start_entries = [action, proto, src]
+ if not remark:
+ ace = seq + " " + " ".join(ace_start_entries)
+ if src_port_op:
+ ace += _acl_operand(src_port_op, src_port1, src_port2)
+ ace += " " + dest
+ if dest_port_op:
+ ace += _acl_operand(dest_port_op, dest_port1, dest_port2)
+ else:
+ ace = seq + " remark " + remark
+
+ return ace
+
+
+def config_acl_options(options):
+ ENABLE_ONLY = [
+ "psh",
+ "urg",
+ "log",
+ "ack",
+ "syn",
+ "established",
+ "rst",
+ "fin",
+ "fragments",
+ "log",
+ ]
+
+ OTHER = ["dscp", "precedence", "time-range"]
+ # packet-length is the only option not currently supported
+
+ if options.get("time_range"):
+ options["time-range"] = options.get("time_range")
+ options.pop("time_range")
+
+ command = ""
+ for option, value in options.items():
+ if option in ENABLE_ONLY:
+ if value == "enable":
+ command += " " + option
+ elif option in OTHER:
+ command += " " + option + " " + value
+ if command:
+ command = command.strip()
+ return command
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def main():
+ argument_spec = dict(
+ seq=dict(required=False, type="str"),
+ name=dict(required=True, type="str"),
+ action=dict(required=False, choices=["remark", "permit", "deny"]),
+ remark=dict(required=False, type="str"),
+ proto=dict(required=False, type="str"),
+ src=dict(required=False, type="str"),
+ src_port_op=dict(
+ required=False, choices=["any", "eq", "gt", "lt", "neq", "range"]
+ ),
+ src_port1=dict(required=False, type="str"),
+ src_port2=dict(required=False, type="str"),
+ dest=dict(required=False, type="str"),
+ dest_port_op=dict(
+ required=False, choices=["any", "eq", "gt", "lt", "neq", "range"]
+ ),
+ dest_port1=dict(required=False, type="str"),
+ dest_port2=dict(required=False, type="str"),
+ log=dict(required=False, choices=["enable"]),
+ urg=dict(required=False, choices=["enable"]),
+ ack=dict(required=False, choices=["enable"]),
+ psh=dict(required=False, choices=["enable"]),
+ rst=dict(required=False, choices=["enable"]),
+ syn=dict(required=False, choices=["enable"]),
+ fragments=dict(required=False, choices=["enable"]),
+ fin=dict(required=False, choices=["enable"]),
+ established=dict(required=False, choices=["enable"]),
+ time_range=dict(required=False),
+ precedence=dict(
+ required=False,
+ choices=[
+ "critical",
+ "flash",
+ "flash-override",
+ "immediate",
+ "internet",
+ "network",
+ "priority",
+ "routine",
+ ],
+ ),
+ dscp=dict(
+ required=False,
+ choices=[
+ "af11",
+ "af12",
+ "af13",
+ "af21",
+ "af22",
+ "af23",
+ "af31",
+ "af32",
+ "af33",
+ "af41",
+ "af42",
+ "af43",
+ "cs1",
+ "cs2",
+ "cs3",
+ "cs4",
+ "cs5",
+ "cs6",
+ "cs7",
+ "default",
+ "ef",
+ ],
+ ),
+ state=dict(
+ choices=["absent", "present", "delete_acl"], default="present"
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ results = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+ action = module.params["action"]
+ remark = module.params["remark"]
+ dscp = module.params["dscp"]
+ precedence = module.params["precedence"]
+ seq = module.params["seq"]
+ name = module.params["name"]
+ seq = module.params["seq"]
+
+ if action == "remark" and not remark:
+ module.fail_json(
+ msg="when state is action, remark param is also required"
+ )
+
+ REQUIRED = ["seq", "name", "action", "proto", "src", "dest"]
+ ABSENT = ["name", "seq"]
+ if state == "present":
+ if action and remark and seq:
+ pass
+ else:
+ for each in REQUIRED:
+ if module.params[each] is None:
+ module.fail_json(
+ msg="req'd params when state is present:",
+ params=REQUIRED,
+ )
+ elif state == "absent":
+ for each in ABSENT:
+ if module.params[each] is None:
+ module.fail_json(
+ msg="require params when state is absent", params=ABSENT
+ )
+ elif state == "delete_acl":
+ if module.params["name"] is None:
+ module.fail_json(msg="param name req'd when state is delete_acl")
+
+ if dscp and precedence:
+ module.fail_json(
+ msg="only one of the params dscp/precedence " "are allowed"
+ )
+
+ OPTIONS_NAMES = [
+ "log",
+ "urg",
+ "ack",
+ "psh",
+ "rst",
+ "syn",
+ "fin",
+ "established",
+ "dscp",
+ "precedence",
+ "fragments",
+ "time_range",
+ ]
+
+ CORE = [
+ "seq",
+ "name",
+ "action",
+ "proto",
+ "src",
+ "src_port_op",
+ "src_port1",
+ "src_port2",
+ "dest",
+ "dest_port_op",
+ "dest_port1",
+ "dest_port2",
+ "remark",
+ ]
+
+ proposed_core = dict(
+ (param, value)
+ for (param, value) in module.params.items()
+ if param in CORE and value is not None
+ )
+
+ proposed_options = dict(
+ (param, value)
+ for (param, value) in module.params.items()
+ if param in OPTIONS_NAMES and value is not None
+ )
+ proposed = {}
+ proposed.update(proposed_core)
+ proposed.update(proposed_options)
+
+ existing_options = {}
+
+ # getting existing existing_core=dict, acl=list, seq=list
+ existing_core, acl = get_acl(module, name, seq)
+ if existing_core:
+ existing_options = existing_core.get("options")
+ existing_core.pop("options")
+
+ commands = []
+ delta_core = {}
+ delta_options = {}
+
+ if not existing_core.get("remark"):
+ dcore = dict(
+ set(proposed_core.items()).difference(existing_core.items())
+ )
+ if not dcore:
+ # check the diff in the other way just in case
+ dcore = dict(
+ set(existing_core.items()).difference(proposed_core.items())
+ )
+ delta_core = dcore
+ if delta_core:
+ delta_options = proposed_options
+ else:
+ doptions = dict(
+ set(proposed_options.items()).difference(
+ existing_options.items()
+ )
+ )
+ # check the diff in the other way just in case
+ if not doptions:
+ doptions = dict(
+ set(existing_options.items()).difference(
+ proposed_options.items()
+ )
+ )
+ delta_options = doptions
+ else:
+ delta_core = dict(
+ set(proposed_core.items()).difference(existing_core.items())
+ )
+
+ if state == "present":
+ if delta_core or delta_options:
+ if existing_core: # if the ace exists already
+ commands.append(["no {0}".format(seq)])
+ if delta_options:
+ myacl_str = config_core_acl(proposed_core)
+ myacl_str += " " + config_acl_options(proposed_options)
+ else:
+ myacl_str = config_core_acl(proposed_core)
+ command = [myacl_str]
+ commands.append(command)
+ elif state == "absent":
+ if existing_core:
+ commands.append(["no {0}".format(seq)])
+ elif state == "delete_acl":
+ if acl and acl[0].get("acl") != "no_entries":
+ commands.append(["no ip access-list {0}".format(name)])
+
+ cmds = []
+ if commands:
+ preface = []
+ if state in ["present", "absent"]:
+ preface = ["ip access-list {0}".format(name)]
+ commands.insert(0, preface)
+
+ cmds = flatten_list(commands)
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ load_config(module, cmds)
+ results["changed"] = True
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interface.py
new file mode 100644
index 00000000..2a65fa32
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interface.py
@@ -0,0 +1,227 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_acl_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manages applying
+ ACLs to interfaces.
+description:
+- Manages applying ACLs to interfaces.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_acl_interfaces
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+options:
+ name:
+ description:
+ - Case sensitive name of the access list (ACL).
+ required: true
+ type: str
+ interface:
+ description:
+ - Full name of interface, e.g. I(Ethernet1/1).
+ required: true
+ type: str
+ direction:
+ description:
+ - Direction ACL to be applied in on the interface.
+ required: true
+ choices:
+ - ingress
+ - egress
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ required: false
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+
+
+"""
+
+EXAMPLES = """
+- name: apply egress acl to ethernet1/41
+ cisco.nxos.nxos_acl_interface:
+ name: ANSIBLE
+ interface: ethernet1/41
+ direction: egress
+ state: present
+"""
+
+RETURN = """
+acl_applied_to:
+ description: list of interfaces the ACL is applied to
+ returned: always
+ type: list
+ sample: [{"acl_type": "Router ACL", "direction": "egress",
+ "interface": "Ethernet1/41", "name": "ANSIBLE"}]
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface ethernet1/41", "ip access-group ANSIBLE out"]
+"""
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def check_for_acl_int_present(module, name, intf, direction):
+ # Need to Capitalize the interface name as the nxos
+ # output has capitalization
+ command = [
+ {
+ "command": "show running-config aclmgr | section {0}".format(
+ intf.title()
+ ),
+ "output": "text",
+ }
+ ]
+ body = run_commands(module, command)
+
+ if direction == "ingress":
+ mdir = "in"
+ elif direction == "egress":
+ mdir = "out"
+
+ match = re.search(
+ "ip access-group {0} {1}".format(name, mdir), str(body[0])
+ )
+ return bool(match)
+
+
+def apply_acl(proposed):
+ commands = []
+
+ commands.append("interface " + proposed.get("interface"))
+ direction = proposed.get("direction")
+ if direction == "egress":
+ cmd = "ip access-group {0} {1}".format(proposed.get("name"), "out")
+ elif direction == "ingress":
+ cmd = "ip access-group {0} {1}".format(proposed.get("name"), "in")
+ commands.append(cmd)
+
+ return commands
+
+
+def remove_acl(proposed):
+ commands = []
+
+ commands.append("interface " + proposed.get("interface"))
+ direction = proposed.get("direction")
+ if direction == "egress":
+ cmd = "no ip access-group {0} {1}".format(proposed.get("name"), "out")
+ elif direction == "ingress":
+ cmd = "no ip access-group {0} {1}".format(proposed.get("name"), "in")
+ commands.append(cmd)
+
+ return commands
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def main():
+ argument_spec = dict(
+ name=dict(required=True, type="str"),
+ interface=dict(required=True),
+ direction=dict(required=True, choices=["egress", "ingress"]),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ results = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+ name = module.params["name"]
+ interface = module.params["interface"].lower()
+ direction = module.params["direction"].lower()
+
+ proposed = dict(name=name, interface=interface, direction=direction)
+
+ existing = check_for_acl_int_present(module, name, interface, direction)
+
+ cmds = []
+ commands = []
+ if state == "present":
+ if not existing:
+ command = apply_acl(proposed)
+ if command:
+ commands.append(command)
+
+ elif state == "absent":
+ if existing:
+ command = remove_acl(proposed)
+ if command:
+ commands.append(command)
+
+ if commands:
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ load_config(module, cmds)
+ results["changed"] = True
+ if "configure" in cmds:
+ cmds.pop(0)
+ else:
+ cmds = []
+
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interfaces.py
new file mode 100644
index 00000000..74728194
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acl_interfaces.py
@@ -0,0 +1,437 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_acl_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_acl_interfaces
+short_description: ACL interfaces resource module
+description: Add and remove Access Control Lists on interfaces in NX-OS platform
+version_added: 1.0.0
+author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
+notes:
+- Tested against NX-OS 7.3.(0)D1(1) on VIRL
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section '^interface').
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A list of interfaces to be configured with ACLs
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description: Name of the interface
+ type: str
+ required: true
+ access_groups:
+ description: List of address family indicators with ACLs to be configured
+ on the interface
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description: Address Family Indicator of the ACLs to be configured
+ type: str
+ required: true
+ choices:
+ - ipv4
+ - ipv6
+ acls:
+ description: List of Access Control Lists for the interface
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description: Name of the ACL to be added/removed
+ type: str
+ required: true
+ direction:
+ description: Direction to be applied for the ACL
+ type: str
+ required: true
+ choices:
+ - in
+ - out
+ port:
+ description: Use ACL as port policy.
+ type: bool
+ state:
+ description: The state the configuration should be left in
+ type: str
+ choices:
+ - deleted
+ - gathered
+ - merged
+ - overridden
+ - rendered
+ - replaced
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# ------------
+#
+
+- name: Merge ACL interfaces configuration
+ cisco.nxos.nxos_acl_interfaces:
+ config:
+ - name: Ethernet1/2
+ access_groups:
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ direction: in
+
+ - name: Eth1/5
+ access_groups:
+ - afi: ipv4
+ acls:
+ - name: PortACL
+ direction: in
+ port: true
+
+ - name: ACL1v4
+ direction: out
+
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ direction: in
+ state: merged
+
+# After state:
+# ------------
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ip port access-group PortACL in
+# ip access-group ACL1v4 out
+# ipv6 traffic-filter ACL1v6 in
+
+# Using replaced
+
+# Before state:
+# ------------
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ip port access-group PortACL in
+# ip access-group ACL1v4 out
+# ipv6 traffic-filter ACL1v6 in
+
+- name: Replace interface configuration with given configuration
+ cisco.nxos.nxos_acl_interfaces:
+ config:
+ - name: Eth1/5
+ access_groups:
+ - afi: ipv4
+ acls:
+ - name: NewACLv4
+ direction: out
+
+ - name: Ethernet1/3
+ access_groups:
+ - afi: ipv6
+ acls:
+ - name: NewACLv6
+ direction: in
+ port: true
+ state: replaced
+
+# After state:
+# ------------
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/3
+# ipv6 port traffic-filter NewACLv6 in
+# interface Ethernet1/5
+# ip access-group NewACLv4 out
+
+# Using overridden
+
+# Before state:
+# ------------
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ip port access-group PortACL in
+# ip access-group ACL1v4 out
+# ipv6 traffic-filter ACL1v6 in
+
+- name: Override interface configuration with given configuration
+ cisco.nxos.nxos_acl_interfaces:
+ config:
+ - name: Ethernet1/3
+ access_groups:
+ - afi: ipv4
+ acls:
+ - name: ACL1v4
+ direction: out
+
+ - name: PortACL
+ port: true
+ direction: in
+ - afi: ipv6
+ acls:
+ - name: NewACLv6
+ direction: in
+ port: true
+ state: overridden
+
+# After state:
+# ------------
+# interface Ethernet1/3
+# ip access-group ACL1v4 out
+# ip port access-group PortACL in
+# ipv6 port traffic-filter NewACLv6 in
+
+# Using deleted to remove ACL config from specified interfaces
+
+# Before state:
+# -------------
+# interface Ethernet1/1
+# ip access-group ACL2v4 in
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ip port access-group PortACL in
+# ip access-group ACL1v4 out
+# ipv6 traffic-filter ACL1v6 in
+
+- name: Delete ACL configuration on interfaces
+ cisco.nxos.nxos_acl_interfaces:
+ config:
+ - name: Ethernet1/5
+ - name: Ethernet1/2
+ state: deleted
+
+# After state:
+# -------------
+# interface Ethernet1/1
+# ip access-group ACL2v4 in
+# interface Ethernet1/2
+# interface Ethernet1/5
+
+# Using deleted to remove ACL config from all interfaces
+
+# Before state:
+# -------------
+# interface Ethernet1/1
+# ip access-group ACL2v4 in
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ip port access-group PortACL in
+# ip access-group ACL1v4 out
+# ipv6 traffic-filter ACL1v6 in
+
+- name: Delete ACL configuration from all interfaces
+ cisco.nxos.nxos_acl_interfaces:
+ state: deleted
+
+# After state:
+# -------------
+# interface Ethernet1/1
+# interface Ethernet1/2
+# interface Ethernet1/5
+
+# Using parsed
+
+- name: Parse given configuration into structured format
+ cisco.nxos.nxos_acl_interfaces:
+ running_config: |
+ interface Ethernet1/2
+ ipv6 traffic-filter ACL1v6 in
+ interface Ethernet1/5
+ ipv6 traffic-filter ACL1v6 in
+ ip access-group ACL1v4 out
+ ip port access-group PortACL in
+ state: parsed
+
+# returns
+# parsed:
+# - name: Ethernet1/2
+# access_groups:
+# - afi: ipv6
+# acls:
+# - name: ACL1v6
+# direction: in
+# - name: Ethernet1/5
+# access_groups:
+# - afi: ipv4
+# acls:
+# - name: PortACL
+# direction: in
+# port: True
+# - name: ACL1v4
+# direction: out
+# - afi: ipv6
+# acls:
+# - name: ACL1v6
+# direction: in
+
+
+# Using gathered:
+
+# Before state:
+# ------------
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ipv6 traffic-filter ACL1v6 in
+# ip access-group ACL1v4 out
+# ip port access-group PortACL in
+
+- name: Gather existing configuration from device
+ cisco.nxos.nxos_acl_interfaces:
+ config:
+ state: gathered
+
+# returns
+# gathered:
+# - name: Ethernet1/2
+# access_groups:
+# - afi: ipv6
+# acls:
+# - name: ACL1v6
+# direction: in
+# - name: Ethernet1/5
+# access_groups:
+# - afi: ipv4
+# acls:
+# - name: PortACL
+# direction: in
+# port: True
+# - name: ACL1v4
+# direction: out
+# - afi: ipv6
+# acls:
+# - name: ACL1v6
+# direction: in
+
+
+# Using rendered
+
+- name: Render required configuration to be pushed to the device
+ cisco.nxos.nxos_acl_interfaces:
+ config:
+ - name: Ethernet1/2
+ access_groups:
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ direction: in
+
+ - name: Ethernet1/5
+ access_groups:
+ - afi: ipv4
+ acls:
+ - name: PortACL
+ direction: in
+ port: true
+ - name: ACL1v4
+ direction: out
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ direction: in
+ state: rendered
+
+# returns
+# rendered:
+# interface Ethernet1/2
+# ipv6 traffic-filter ACL1v6 in
+# interface Ethernet1/5
+# ipv6 traffic-filter ACL1v6 in
+# ip access-group ACL1v4 out
+# ip port access-group PortACL in
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface Ethernet1/2', 'ipv6 traffic-filter ACL1v6 out', 'ip port access-group PortACL in']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acl_interfaces.acl_interfaces import (
+ Acl_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.acl_interfaces.acl_interfaces import (
+ Acl_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=Acl_interfacesArgs.argument_spec,
+ supports_check_mode=True,
+ )
+
+ result = Acl_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acls.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acls.py
new file mode 100644
index 00000000..5414dfdd
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_acls.py
@@ -0,0 +1,819 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The module file for nxos_acls
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_acls
+short_description: ACLs resource module
+description: Manage named IP ACLs on the Cisco NX-OS platform
+version_added: 1.0.0
+author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
+notes:
+- Tested against NX-OS 7.3.(0)D1(1) on VIRL
+- As NX-OS allows configuring a rule again with different sequence numbers, the user
+ is expected to provide sequence numbers for the access control entries to preserve
+ idempotency. If no sequence number is given, the rule will be added as a new rule
+ by the device.
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section 'ip(v6)* access-list).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A dictionary of ACL options.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description: The Address Family Indicator (AFI) for the ACL.
+ type: str
+ required: true
+ choices:
+ - ipv4
+ - ipv6
+ acls:
+ description: A list of the ACLs.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description: Name of the ACL.
+ type: str
+ required: true
+ aces:
+ description: The entries within the ACL.
+ type: list
+ elements: dict
+ suboptions:
+ grant:
+ description: Action to be applied on the rule.
+ type: str
+ choices:
+ - permit
+ - deny
+ destination:
+ description: Specify the packet destination.
+ type: dict
+ suboptions:
+ address:
+ description: Destination network address.
+ type: str
+ any:
+ description: Any destination address.
+ type: bool
+ host:
+ description: Host IP address.
+ type: str
+ port_protocol:
+ description: Specify the destination port or protocol (only for
+ TCP and UDP).
+ type: dict
+ suboptions:
+ eq:
+ description: Match only packets on a given port number.
+ type: str
+ gt:
+ description: Match only packets with a greater port number.
+ type: str
+ lt:
+ description: Match only packets with a lower port number.
+ type: str
+ neq:
+ description: Match only packets not on a given port number.
+ type: str
+ range:
+ description: Match only packets in the range of port numbers.
+ type: dict
+ suboptions:
+ start:
+ description: Specify the start of the port range.
+ type: str
+ end:
+ description: Specify the end of the port range.
+ type: str
+ prefix:
+ description: Destination network prefix. Only for prefixes of
+ value less than 31 for ipv4 and 127 for ipv6. Prefixes of 32
+ (ipv4) and 128 (ipv6) should be given in the 'host' key.
+ type: str
+ wildcard_bits:
+ description: Destination wildcard bits.
+ type: str
+ dscp:
+ description: Match packets with given DSCP value.
+ type: str
+ fragments:
+ description: Check non-initial fragments.
+ type: bool
+ remark:
+ description: Access list entry comment.
+ type: str
+ sequence:
+ description: Sequence number.
+ type: int
+ source:
+ description: Specify the packet source.
+ type: dict
+ suboptions:
+ address:
+ description: Source network address.
+ type: str
+ any:
+ description: Any source address.
+ type: bool
+ host:
+ description: Host IP address.
+ type: str
+ port_protocol:
+ description: Specify the destination port or protocol (only for
+ TCP and UDP).
+ type: dict
+ suboptions:
+ eq:
+ description: Match only packets on a given port number.
+ type: str
+ gt:
+ description: Match only packets with a greater port number.
+ type: str
+ lt:
+ description: Match only packets with a lower port number.
+ type: str
+ neq:
+ description: Match only packets not on a given port number.
+ type: str
+ range:
+ description: Match only packets in the range of port numbers.
+ type: dict
+ suboptions:
+ start:
+ description: Specify the start of the port range.
+ type: str
+ end:
+ description: Specify the end of the port range.
+ type: str
+ prefix:
+ description: Source network prefix. Only for prefixes of mask
+ value less than 31 for ipv4 and 127 for ipv6. Prefixes of mask
+ 32 (ipv4) and 128 (ipv6) should be given in the 'host' key.
+ type: str
+ wildcard_bits:
+ description: Source wildcard bits.
+ type: str
+ log:
+ description: Log matches against this entry.
+ type: bool
+ precedence:
+ description: Match packets with given precedence value.
+ type: str
+ protocol:
+ description: Specify the protocol.
+ type: str
+ protocol_options:
+ description: All possible suboptions for the protocol chosen.
+ type: dict
+ suboptions:
+ icmp:
+ description: ICMP protocol options.
+ type: dict
+ suboptions:
+ administratively_prohibited:
+ description: Administratively prohibited
+ type: bool
+ alternate_address:
+ description: Alternate address
+ type: bool
+ conversion_error:
+ description: Datagram conversion
+ type: bool
+ dod_host_prohibited:
+ description: Host prohibited
+ type: bool
+ dod_net_prohibited:
+ description: Net prohibited
+ type: bool
+ echo:
+ description: Echo (ping)
+ type: bool
+ echo_reply:
+ description: Echo reply
+ type: bool
+ echo_request:
+ description: Echo request (ping)
+ type: bool
+ general_parameter_problem:
+ description: Parameter problem
+ type: bool
+ host_isolated:
+ description: Host isolated
+ type: bool
+ host_precedence_unreachable:
+ description: Host unreachable for precedence
+ type: bool
+ host_redirect:
+ description: Host redirect
+ type: bool
+ host_tos_redirect:
+ description: Host redirect for TOS
+ type: bool
+ host_tos_unreachable:
+ description: Host unreachable for TOS
+ type: bool
+ host_unknown:
+ description: Host unknown
+ type: bool
+ host_unreachable:
+ description: Host unreachable
+ type: bool
+ information_reply:
+ description: Information replies
+ type: bool
+ information_request:
+ description: Information requests
+ type: bool
+ mask_reply:
+ description: Mask replies
+ type: bool
+ mask_request:
+ description: Mask requests
+ type: bool
+ message_code:
+ description: ICMP message code
+ type: int
+ message_type:
+ description: ICMP message type
+ type: int
+ mobile_redirect:
+ description: Mobile host redirect
+ type: bool
+ net_redirect:
+ description: Network redirect
+ type: bool
+ net_tos_redirect:
+ description: Net redirect for TOS
+ type: bool
+ net_tos_unreachable:
+ description: Network unreachable for TOS
+ type: bool
+ net_unreachable:
+ description: Net unreachable
+ type: bool
+ network_unknown:
+ description: Network unknown
+ type: bool
+ no_room_for_option:
+ description: Parameter required but no room
+ type: bool
+ option_missing:
+ description: Parameter required but not present
+ type: bool
+ packet_too_big:
+ description: Fragmentation needed and DF set
+ type: bool
+ parameter_problem:
+ description: All parameter problems
+ type: bool
+ port_unreachable:
+ description: Port unreachable
+ type: bool
+ precedence_unreachable:
+ description: Precedence cutoff
+ type: bool
+ protocol_unreachable:
+ description: Protocol unreachable
+ type: bool
+ reassembly_timeout:
+ description: Reassembly timeout
+ type: bool
+ redirect:
+ description: All redirects
+ type: bool
+ router_advertisement:
+ description: Router discovery advertisements
+ type: bool
+ router_solicitation:
+ description: Router discovery solicitations
+ type: bool
+ source_quench:
+ description: Source quenches
+ type: bool
+ source_route_failed:
+ description: Source route failed
+ type: bool
+ time_exceeded:
+ description: All time exceeded.
+ type: bool
+ timestamp_reply:
+ description: Timestamp replies
+ type: bool
+ timestamp_request:
+ description: Timestamp requests
+ type: bool
+ traceroute:
+ description: Traceroute
+ type: bool
+ ttl_exceeded:
+ description: TTL exceeded
+ type: bool
+ unreachable:
+ description: All unreachables
+ type: bool
+ tcp:
+ description: TCP flags.
+ type: dict
+ suboptions:
+ ack:
+ description: Match on the ACK bit
+ type: bool
+ established:
+ description: Match established connections
+ type: bool
+ fin:
+ description: Match on the FIN bit
+ type: bool
+ psh:
+ description: Match on the PSH bit
+ type: bool
+ rst:
+ description: Match on the RST bit
+ type: bool
+ syn:
+ description: Match on the SYN bit
+ type: bool
+ urg:
+ description: Match on the URG bit
+ type: bool
+ igmp:
+ description: IGMP protocol options.
+ type: dict
+ suboptions:
+ dvmrp:
+ description: Distance Vector Multicast Routing Protocol
+ type: bool
+ host_query:
+ description: Host Query
+ type: bool
+ host_report:
+ description: Host Report
+ type: bool
+ state:
+ description:
+ - The state the configuration should be left in
+ type: str
+ choices:
+ - deleted
+ - gathered
+ - merged
+ - overridden
+ - rendered
+ - replaced
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+
+- name: Merge new ACLs configuration
+ cisco.nxos.nxos_acls:
+ config:
+ - afi: ipv4
+ acls:
+ - name: ACL1v4
+ aces:
+ - grant: deny
+ destination:
+ address: 192.0.2.64
+ wildcard_bits: 0.0.0.255
+ source:
+ any: true
+ port_protocol:
+ lt: 55
+ protocol: tcp
+ protocol_options:
+ tcp:
+ ack: true
+ fin: true
+ sequence: 50
+
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ aces:
+ - grant: permit
+ sequence: 10
+ source:
+ any: true
+ destination:
+ prefix: 2001:db8:12::/32
+ protocol: sctp
+ state: merged
+
+# After state:
+# ------------
+#
+# ip access-list ACL1v4
+# 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
+# ipv6 access-list ACL1v6
+# 10 permit sctp any any
+
+# Using replaced
+
+# Before state:
+# ----------------
+#
+# ip access-list ACL1v4
+# 10 permit ip any any
+# 20 deny udp any any
+# ip access-list ACL2v4
+# 10 permit ahp 192.0.2.0 0.0.0.255 any
+# ip access-list ACL1v6
+# 10 permit sctp any any
+# 20 remark IPv6 ACL
+# ip access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+- name: Replace existing ACL configuration with provided configuration
+ cisco.nxos.nxos_acls:
+ config:
+ - afi: ipv4
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ aces:
+ - sequence: 20
+ grant: permit
+ source:
+ any: true
+ destination:
+ any: true
+ protocol: pip
+
+ - remark: Replaced ACE
+
+ - name: ACL2v6
+ state: replaced
+
+# After state:
+# ---------------
+#
+# ipv6 access-list ACL1v6
+# 20 permit pip any any
+# 30 remark Replaced ACE
+# ipv6 access-list ACL2v6
+
+# Using overridden
+
+# Before state:
+# ----------------
+#
+# ip access-list ACL1v4
+# 10 permit ip any any
+# 20 deny udp any any
+# ip access-list ACL2v4
+# 10 permit ahp 192.0.2.0 0.0.0.255 any
+# ip access-list ACL1v6
+# 10 permit sctp any any
+# 20 remark IPv6 ACL
+# ip access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+- name: Override existing configuration with provided configuration
+ cisco.nxos.nxos_acls:
+ config:
+ - afi: ipv4
+ acls:
+ - name: NewACL
+ aces:
+ - grant: deny
+ source:
+ address: 192.0.2.0
+ wildcard_bits: 0.0.255.255
+ destination:
+ any: true
+ protocol: eigrp
+ - remark: Example for overridden state
+ state: overridden
+
+# After state:
+# ------------
+#
+# ip access-list NewACL
+# 10 deny eigrp 192.0.2.0 0.0.255.255 any
+# 20 remark Example for overridden state
+
+# Using deleted:
+#
+# Before state:
+# -------------
+#
+# ip access-list ACL1v4
+# 10 permit ip any any
+# 20 deny udp any any
+# ip access-list ACL2v4
+# 10 permit ahp 192.0.2.0 0.0.0.255 any
+# ip access-list ACL1v6
+# 10 permit sctp any any
+# 20 remark IPv6 ACL
+# ip access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+- name: Delete all ACLs
+ cisco.nxos.nxos_acls:
+ config:
+ state: deleted
+
+# After state:
+# -----------
+#
+
+
+# Before state:
+# -------------
+#
+# ip access-list ACL1v4
+# 10 permit ip any any
+# 20 deny udp any any
+# ip access-list ACL2v4
+# 10 permit ahp 192.0.2.0 0.0.0.255 any
+# ip access-list ACL1v6
+# 10 permit sctp any any
+# 20 remark IPv6 ACL
+# ip access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+- name: Delete all ACLs in given AFI
+ cisco.nxos.nxos_acls:
+ config:
+ - afi: ipv4
+ state: deleted
+
+# After state:
+# ------------
+#
+# ip access-list ACL1v6
+# 10 permit sctp any any
+# 20 remark IPv6 ACL
+# ip access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+
+
+# Before state:
+# -------------
+#
+# ip access-list ACL1v4
+# 10 permit ip any any
+# 20 deny udp any any
+# ip access-list ACL2v4
+# 10 permit ahp 192.0.2.0 0.0.0.255 any
+# ipv6 access-list ACL1v6
+# 10 permit sctp any any
+# 20 remark IPv6 ACL
+# ipv6 access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+- name: Delete specific ACLs
+ cisco.nxos.nxos_acls:
+ config:
+ - afi: ipv4
+ acls:
+ - name: ACL1v4
+ - name: ACL2v4
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ state: deleted
+
+# After state:
+# ------------
+# ipv6 access-list ACL2v6
+# 10 deny ipv6 any 2001:db8:3000::/36
+# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
+
+# Using parsed
+
+- name: Parse given config to structured data
+ cisco.nxos.nxos_acls:
+ running_config: |
+ ip access-list ACL1v4
+ 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
+ ipv6 access-list ACL1v6
+ 10 permit sctp any any
+ state: parsed
+
+# returns:
+# parsed:
+# - afi: ipv4
+# acls:
+# - name: ACL1v4
+# aces:
+# - grant: deny
+# destination:
+# address: 192.0.2.64
+# wildcard_bits: 0.0.0.255
+# source:
+# any: true
+# port_protocol:
+# lt: 55
+# protocol: tcp
+# protocol_options:
+# tcp:
+# ack: true
+# fin: true
+# sequence: 50
+#
+# - afi: ipv6
+# acls:
+# - name: ACL1v6
+# aces:
+# - grant: permit
+# sequence: 10
+# source:
+# any: true
+# destination:
+# prefix: 2001:db8:12::/32
+# protocol: sctp
+
+
+# Using gathered:
+
+# Before state:
+# ------------
+#
+# ip access-list ACL1v4
+# 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
+# ipv6 access-list ACL1v6
+# 10 permit sctp any any
+
+- name: Gather existing configuration
+ cisco.nxos.nxos_acls:
+ state: gathered
+
+# returns:
+# gathered:
+# - afi: ipv4
+# acls:
+# - name: ACL1v4
+# aces:
+# - grant: deny
+# destination:
+# address: 192.0.2.64
+# wildcard_bits: 0.0.0.255
+# source:
+# any: true
+# port_protocol:
+# lt: 55
+# protocol: tcp
+# protocol_options:
+# tcp:
+# ack: true
+# fin: true
+# sequence: 50
+
+# - afi: ipv6
+# acls:
+# - name: ACL1v6
+# aces:
+# - grant: permit
+# sequence: 10
+# source:
+# any: true
+# destination:
+# prefix: 2001:db8:12::/32
+# protocol: sctp
+
+
+# Using rendered
+
+- name: Render required configuration to be pushed to the device
+ cisco.nxos.nxos_acls:
+ config:
+ - afi: ipv4
+ acls:
+ - name: ACL1v4
+ aces:
+ - grant: deny
+ destination:
+ address: 192.0.2.64
+ wildcard_bits: 0.0.0.255
+ source:
+ any: true
+ port_protocol:
+ lt: 55
+ protocol: tcp
+ protocol_options:
+ tcp:
+ ack: true
+ fin: true
+ sequence: 50
+
+ - afi: ipv6
+ acls:
+ - name: ACL1v6
+ aces:
+ - grant: permit
+ sequence: 10
+ source:
+ any: true
+ destination:
+ prefix: 2001:db8:12::/32
+ protocol: sctp
+ state: rendered
+
+# returns:
+# rendered:
+# ip access-list ACL1v4
+# 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
+# ipv6 access-list ACL1v6
+# 10 permit sctp any any
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['ip access-list ACL1v4', '10 permit ip any any precedence critical log', '20 deny tcp any lt smtp host 192.0.2.64 ack fin']
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acls.acls import (
+ AclsArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.acls.acls import (
+ Acls,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=AclsArgs.argument_spec, supports_check_mode=True
+ )
+
+ result = Acls(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_banner.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_banner.py
new file mode 100644
index 00000000..84cc6d91
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_banner.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+# (c) 2017, Ansible by Red Hat, inc
+#
+# This file is part of Ansible by Red Hat
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+DOCUMENTATION = """
+module: nxos_banner
+author: Trishna Guha (@trishnaguha)
+short_description: Manage multiline banners on Cisco NXOS devices
+description:
+- This will configure both exec and motd banners on remote devices running Cisco NXOS.
+ It allows playbooks to add or remove banner text from the active running configuration.
+notes:
+- Since responses from the device are always read with surrounding whitespaces stripped,
+ tasks that configure banners with preceeding or trailing whitespaces will not be idempotent.
+version_added: 1.0.0
+options:
+ banner:
+ description:
+ - Specifies which banner that should be configured on the remote device.
+ required: true
+ choices:
+ - exec
+ - motd
+ type: str
+ text:
+ description:
+ - The banner text that should be present in the remote device running configuration.
+ This argument accepts a multiline string, with no empty lines. Requires I(state=present).
+ type: str
+ state:
+ description:
+ - Specifies whether or not the configuration is present in the current devices
+ active running configuration.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+extends_documentation_fragment:
+- cisco.nxos.nxos
+"""
+
+EXAMPLES = """
+- name: configure the exec banner
+ cisco.nxos.nxos_banner:
+ banner: exec
+ text: |
+ this is my exec banner
+ that contains a multiline
+ string
+ state: present
+- name: remove the motd banner
+ cisco.nxos.nxos_banner:
+ banner: motd
+ state: absent
+- name: Configure banner from file
+ cisco.nxos.nxos_banner:
+ banner: motd
+ text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}"
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always
+ type: list
+ sample:
+ - banner exec
+ - this is my exec banner
+ - that contains a multiline
+ - string
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_text
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+import re
+
+
+def execute_show_command(module, command):
+ format = "text"
+ cmds = [{"command": command, "output": format}]
+ output = run_commands(module, cmds)
+ return output
+
+
+def map_obj_to_commands(want, have, module):
+ commands = list()
+ state = module.params["state"]
+ platform_regex = "Nexus.*Switch"
+
+ if state == "absent":
+ if have.get("text") and not (
+ (have.get("text") == "User Access Verification")
+ or re.match(platform_regex, have.get("text"))
+ ):
+ commands.append("no banner %s" % module.params["banner"])
+
+ elif state == "present" and want.get("text") != have.get("text"):
+ banner_cmd = "banner %s @\n%s\n@" % (
+ module.params["banner"],
+ want["text"],
+ )
+ commands.append(banner_cmd)
+
+ return commands
+
+
+def map_config_to_obj(module):
+ command = "show banner %s" % module.params["banner"]
+ output = execute_show_command(module, command)[0]
+
+ if "Invalid command" in output:
+ module.fail_json(
+ msg="banner: %s may not be supported on this platform. Possible values are : exec | motd"
+ % module.params["banner"]
+ )
+
+ if isinstance(output, dict):
+ output = list(output.values())
+ if output != []:
+ output = output[0]
+ else:
+ output = ""
+ if isinstance(output, dict):
+ output = list(output.values())
+ if output != []:
+ output = output[0]
+ else:
+ output = ""
+ else:
+ output = output.rstrip()
+
+ obj = {"banner": module.params["banner"], "state": "absent"}
+ if output:
+ obj["text"] = output
+ obj["state"] = "present"
+ return obj
+
+
+def map_params_to_obj(module):
+ text = module.params["text"]
+ return {
+ "banner": module.params["banner"],
+ "text": to_text(text) if text else None,
+ "state": module.params["state"],
+ }
+
+
+def main():
+ """ main entry point for module execution
+ """
+ argument_spec = dict(
+ banner=dict(required=True, choices=["exec", "motd"]),
+ text=dict(),
+ state=dict(default="present", choices=["present", "absent"]),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ required_if = [("state", "present", ("text",))]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=required_if,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(module)
+ commands = map_obj_to_commands(want, have, module)
+ result["commands"] = commands
+
+ if commands:
+ if not module.check_mode:
+ msgs = load_config(module, commands, True)
+ if msgs:
+ for item in msgs:
+ if item:
+ if isinstance(item, dict):
+ err_str = item["clierror"]
+ else:
+ err_str = item
+ if (
+ "more than 40 lines" in err_str
+ or "buffer overflowed" in err_str
+ ):
+ load_config(module, commands)
+
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_global.py
new file mode 100644
index 00000000..54ce4ec6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_global.py
@@ -0,0 +1,336 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_bfd_global
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Bidirectional Forwarding Detection (BFD) global-level configuration
+description:
+- Manages Bidirectional Forwarding Detection (BFD) global-level configuration.
+version_added: 1.0.0
+author:
+- Chris Van Heuveln (@chrisvanheuveln)
+notes:
+- Tested against NXOSv 9.2(2)
+- BFD global will automatically enable 'feature bfd' if it is disabled.
+- BFD global does not have a 'state' parameter. All of the BFD commands are unique
+ and are defined if 'feature bfd' is enabled.
+options:
+ echo_interface:
+ description:
+ - Loopback interface used for echo frames.
+ - Valid values are loopback interface name or 'deleted'.
+ - Not supported on N5K/N6K
+ required: false
+ type: str
+ echo_rx_interval:
+ description:
+ - BFD Echo receive interval in milliseconds.
+ required: false
+ type: int
+ interval:
+ description:
+ - BFD interval timer values.
+ - Value must be a dict defining values for keys (tx, min_rx, and multiplier)
+ required: false
+ type: dict
+ slow_timer:
+ description:
+ - BFD slow rate timer in milliseconds.
+ required: false
+ type: int
+ startup_timer:
+ description:
+ - BFD delayed startup timer in seconds.
+ - Not supported on N5K/N6K/N7K
+ required: false
+ type: int
+ ipv4_echo_rx_interval:
+ description:
+ - BFD IPv4 session echo receive interval in milliseconds.
+ required: false
+ type: int
+ ipv4_interval:
+ description:
+ - BFD IPv4 interval timer values.
+ - Value must be a dict defining values for keys (tx, min_rx, and multiplier).
+ required: false
+ type: dict
+ ipv4_slow_timer:
+ description:
+ - BFD IPv4 slow rate timer in milliseconds.
+ required: false
+ type: int
+ ipv6_echo_rx_interval:
+ description:
+ - BFD IPv6 session echo receive interval in milliseconds.
+ required: false
+ type: int
+ ipv6_interval:
+ description:
+ - BFD IPv6 interval timer values.
+ - Value must be a dict defining values for keys (tx, min_rx, and multiplier).
+ required: false
+ type: dict
+ ipv6_slow_timer:
+ description:
+ - BFD IPv6 slow rate timer in milliseconds.
+ required: false
+ type: int
+ fabricpath_interval:
+ description:
+ - BFD fabricpath interval timer values.
+ - Value must be a dict defining values for keys (tx, min_rx, and multiplier).
+ required: false
+ type: dict
+ fabricpath_slow_timer:
+ description:
+ - BFD fabricpath slow rate timer in milliseconds.
+ required: false
+ type: int
+ fabricpath_vlan:
+ description:
+ - BFD fabricpath control vlan.
+ required: false
+ type: int
+"""
+EXAMPLES = """
+- cisco.nxos.nxos_bfd_global:
+ echo_interface: Ethernet1/2
+ echo_rx_interval: 50
+ interval:
+ tx: 50
+ min_rx: 50
+ multiplier: 4
+"""
+
+RETURN = """
+cmds:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["bfd echo-interface loopback1", "bfd slow-timer 2000"]
+"""
+
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ NxosCmdRef,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+BFD_CMD_REF = """
+# The cmd_ref is a yaml formatted list of module commands.
+# A leading underscore denotes a non-command variable; e.g. _template.
+# BFD does not have convenient json data so this cmd_ref uses raw cli configs.
+---
+_template: # _template holds common settings for all commands
+ # Enable feature bfd if disabled
+ feature: bfd
+ # Common get syntax for BFD commands
+ get_command: show run bfd all | incl '^(no )*bfd'
+
+echo_interface:
+ kind: str
+ getval: (no )*bfd echo-interface *(\\S+)*$
+ setval: 'bfd echo-interface {0}'
+ default: ~
+
+echo_rx_interval:
+ _exclude: ['N5K', 'N6K']
+ kind: int
+ getval: bfd echo-rx-interval (\\d+)$
+ setval: bfd echo-rx-interval {0}
+ default: 50
+ N3K:
+ default: 250
+
+interval:
+ kind: dict
+ getval: bfd interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
+ setval: bfd interval {tx} min_rx {min_rx} multiplier {multiplier}
+ default: &def_interval
+ tx: 50
+ min_rx: 50
+ multiplier: 3
+ N3K:
+ default: &n3k_def_interval
+ tx: 250
+ min_rx: 250
+ multiplier: 3
+
+slow_timer:
+ kind: int
+ getval: bfd slow-timer (\\d+)$
+ setval: bfd slow-timer {0}
+ default: 2000
+
+startup_timer:
+ _exclude: ['N5K', 'N6K', 'N7K']
+ kind: int
+ getval: bfd startup-timer (\\d+)$
+ setval: bfd startup-timer {0}
+ default: 5
+
+# IPv4/IPv6 specific commands
+ipv4_echo_rx_interval:
+ _exclude: ['N5K', 'N6K']
+ kind: int
+ getval: bfd ipv4 echo-rx-interval (\\d+)$
+ setval: bfd ipv4 echo-rx-interval {0}
+ default: 50
+ N3K:
+ default: 250
+
+ipv4_interval:
+ _exclude: ['N5K', 'N6K']
+ kind: dict
+ getval: bfd ipv4 interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
+ setval: bfd ipv4 interval {tx} min_rx {min_rx} multiplier {multiplier}
+ default: *def_interval
+ N3K:
+ default: *n3k_def_interval
+
+ipv4_slow_timer:
+ _exclude: ['N5K', 'N6K']
+ kind: int
+ getval: bfd ipv4 slow-timer (\\d+)$
+ setval: bfd ipv4 slow-timer {0}
+ default: 2000
+
+ipv6_echo_rx_interval:
+ _exclude: ['N35', 'N5K', 'N6K']
+ kind: int
+ getval: bfd ipv6 echo-rx-interval (\\d+)$
+ setval: bfd ipv6 echo-rx-interval {0}
+ default: 50
+ N3K:
+ default: 250
+
+ipv6_interval:
+ _exclude: ['N35', 'N5K', 'N6K']
+ kind: dict
+ getval: bfd ipv6 interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
+ setval: bfd ipv6 interval {tx} min_rx {min_rx} multiplier {multiplier}
+ default: *def_interval
+ N3K:
+ default: *n3k_def_interval
+
+ipv6_slow_timer:
+ _exclude: ['N35', 'N5K', 'N6K']
+ kind: int
+ getval: bfd ipv6 slow-timer (\\d+)$
+ setval: bfd ipv6 slow-timer {0}
+ default: 2000
+
+# Fabricpath Commands
+fabricpath_interval:
+ _exclude: ['N35', 'N3K', 'N9K']
+ kind: dict
+ getval: bfd fabricpath interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
+ setval: bfd fabricpath interval {tx} min_rx {min_rx} multiplier {multiplier}
+ default: *def_interval
+
+fabricpath_slow_timer:
+ _exclude: ['N35', 'N3K', 'N9K']
+ kind: int
+ getval: bfd fabricpath slow-timer (\\d+)$
+ setval: bfd fabricpath slow-timer {0}
+ default: 2000
+
+fabricpath_vlan:
+ _exclude: ['N35', 'N3K', 'N9K']
+ kind: int
+ getval: bfd fabricpath vlan (\\d+)$
+ setval: bfd fabricpath vlan {0}
+ default: 1
+"""
+
+
+def reorder_cmds(cmds):
+ """
+ There is a bug in some image versions where bfd echo-interface and
+ bfd echo-rx-interval need to be applied last for them to nvgen properly.
+ """
+ regex1 = re.compile(r"^bfd echo-interface")
+ regex2 = re.compile(r"^bfd echo-rx-interval")
+ filtered_cmds = [i for i in cmds if not regex1.match(i)]
+ filtered_cmds = [i for i in filtered_cmds if not regex2.match(i)]
+ echo_int_cmd = [i for i in cmds if regex1.match(i)]
+ echo_rx_cmd = [i for i in cmds if regex2.match(i)]
+ filtered_cmds.extend(echo_int_cmd)
+ filtered_cmds.extend(echo_rx_cmd)
+
+ return filtered_cmds
+
+
+def main():
+ argument_spec = dict(
+ echo_interface=dict(required=False, type="str"),
+ echo_rx_interval=dict(required=False, type="int"),
+ interval=dict(required=False, type="dict"),
+ slow_timer=dict(required=False, type="int"),
+ startup_timer=dict(required=False, type="int"),
+ ipv4_echo_rx_interval=dict(required=False, type="int"),
+ ipv4_interval=dict(required=False, type="dict"),
+ ipv4_slow_timer=dict(required=False, type="int"),
+ ipv6_echo_rx_interval=dict(required=False, type="int"),
+ ipv6_interval=dict(required=False, type="dict"),
+ ipv6_slow_timer=dict(required=False, type="int"),
+ fabricpath_interval=dict(required=False, type="dict"),
+ fabricpath_slow_timer=dict(required=False, type="int"),
+ fabricpath_vlan=dict(required=False, type="int"),
+ )
+ argument_spec.update(nxos_argument_spec)
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+ warnings = list()
+
+ cmd_ref = NxosCmdRef(module, BFD_CMD_REF)
+ cmd_ref.get_existing()
+ cmd_ref.get_playvals()
+ cmds = reorder_cmds(cmd_ref.get_proposed())
+
+ result = {
+ "changed": False,
+ "commands": cmds,
+ "warnings": warnings,
+ "check_mode": module.check_mode,
+ }
+ if cmds:
+ result["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_interfaces.py
new file mode 100644
index 00000000..3e885dc2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bfd_interfaces.py
@@ -0,0 +1,299 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_bfd_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_bfd_interfaces
+short_description: BFD interfaces resource module
+description: Manages attributes of Bidirectional Forwarding Detection (BFD) on the
+ interface.
+version_added: 1.0.0
+author: Chris Van Heuveln (@chrisvanheuveln)
+notes:
+- Tested against NX-OS 7.0(3)I5(1).
+- Feature bfd should be enabled for this module.
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section '^interface|^feature
+ bfd').
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: The provided configuration
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ type: str
+ description: The name of the interface.
+ bfd:
+ type: str
+ description:
+ - Enable/Disable Bidirectional Forwarding Detection (BFD) on the interface.
+ choices:
+ - enable
+ - disable
+ echo:
+ type: str
+ description:
+ - Enable/Disable BFD Echo functionality on the interface.
+ choices:
+ - enable
+ - disable
+ state:
+ description:
+ - The state of the configuration after module completion
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using deleted
+
+- name: Configure interfaces
+ cisco.nxos.nxos_bfd_interfaces:
+ state: deleted
+
+
+# Using merged
+
+- name: Configure interfaces
+ cisco.nxos.nxos_bfd_interfaces:
+ config:
+ - name: Ethernet1/1
+ bfd: enable
+ echo: enable
+ - name: Ethernet1/2
+ bfd: disable
+ echo: disable
+ state: merged
+
+
+# Using overridden
+
+- name: Configure interfaces
+ cisco.nxos.nxos_bfd_interfaces:
+ config:
+ - name: Ethernet1/1
+ bfd: enable
+ echo: enable
+ - name: Ethernet1/2
+ bfd: disable
+ echo: disable
+ state: overridden
+
+
+# Using replaced
+
+- name: Configure interfaces
+ cisco.nxos.nxos_bfd_interfaces:
+ config:
+ - name: Ethernet1/1
+ bfd: enable
+ echo: enable
+ - name: Ethernet1/2
+ bfd: disable
+ echo: disable
+ state: replaced
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_bfd_interfaces:
+ config:
+ - name: Ethernet1/800
+ bfd: enable
+ echo: enable
+ - name: Ethernet1/801
+ bfd: disable
+ echo: disable
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/800"
+# - "bfd"
+# - "bfd echo"
+# - "interface Ethernet1/801"
+# - "no bfd"
+# - "no bfd echo"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+
+# feature bfd
+# interface Ethernet1/800
+# no switchport
+# no bfd
+# no bfd echo
+# interface Ethernet1/801
+# no switchport
+# no bfd
+# interface Ethernet1/802
+# no switchport
+# no bfd echo
+# interface mgmt0
+# ip address dhcp
+# vrf member management
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_bfd_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# - bfd: disable
+# echo: disable
+# name: Ethernet1/800
+# - bfd: disable
+# echo: enable
+# name: Ethernet1/801
+# - bfd: enable
+# echo: disable
+# name: Ethernet1/802
+# - bfd: enable
+# echo: enable
+# name: mgmt0
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+
+# feature bfd
+# interface Ethernet1/1
+# no switchport
+# no bfd
+# interface Ethernet1/2
+# no switchport
+# no bfd echo
+# interface mgmt0
+# ip address dhcp
+# vrf member management
+
+- name: Gather bfd_interfaces facts from the device using nxos_bfd_interfaces
+ cisco.nxos.nxos_bfd_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# - name: Ethernet1/1
+# bfd: disable
+# echo: enable
+# - name: Ethernet1/3
+# echo: disable
+# bfd: enable
+# - name: mgmt0
+# bfd: enable
+# echo: enable
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface Ethernet1/1', 'no bfd', 'no bfd echo']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.bfd_interfaces.bfd_interfaces import (
+ Bfd_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.bfd_interfaces.bfd_interfaces import (
+ Bfd_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=Bfd_interfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Bfd_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp.py
new file mode 100644
index 00000000..a850d15b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp.py
@@ -0,0 +1,786 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_bgp
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2023-01-27) Manages BGP configuration.
+description:
+- Manages BGP configurations on NX-OS switches.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+deprecated:
+ alternative: nxos_bgp_global
+ why: Updated module released with more functionality.
+ removed_at_date: '2023-01-27'
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the whole BGP ASN configuration when C(vrf=default) or the
+ whole VRF instance within the BGP process when using a different VRF.
+- Default when supported restores params default value.
+- Configuring global params is only permitted if C(vrf=default).
+options:
+ asn:
+ description:
+ - BGP autonomous system number. Valid values are String, Integer in ASPLAIN or
+ ASDOT notation.
+ required: true
+ type: str
+ vrf:
+ description:
+ - Name of the VRF. The name 'default' is a valid VRF representing the global BGP.
+ default: 'default'
+ type: str
+ bestpath_always_compare_med:
+ description:
+ - Enable/Disable MED comparison on paths from different autonomous systems.
+ type: bool
+ bestpath_aspath_multipath_relax:
+ description:
+ - Enable/Disable load sharing across the providers with different (but equal-length)
+ AS paths.
+ type: bool
+ bestpath_compare_routerid:
+ description:
+ - Enable/Disable comparison of router IDs for identical eBGP paths.
+ type: bool
+ bestpath_compare_neighborid:
+ description:
+ - Enable/Disable neighborid. Use this when more paths available than max path
+ config.
+ type: bool
+ bestpath_cost_community_ignore:
+ description:
+ - Enable/Disable Ignores the cost community for BGP best-path calculations.
+ type: bool
+ bestpath_med_confed:
+ description:
+ - Enable/Disable enforcement of bestpath to do a MED comparison only between paths
+ originated within a confederation.
+ type: bool
+ bestpath_med_missing_as_worst:
+ description:
+ - Enable/Disable assigns the value of infinity to received routes that do not
+ carry the MED attribute, making these routes the least desirable.
+ type: bool
+ bestpath_med_non_deterministic:
+ description:
+ - Enable/Disable deterministic selection of the best MED pat from among the paths
+ from the same autonomous system.
+ type: bool
+ cluster_id:
+ description:
+ - Route Reflector Cluster-ID.
+ type: str
+ confederation_id:
+ description:
+ - Routing domain confederation AS.
+ type: str
+ confederation_peers:
+ description:
+ - AS confederation parameters.
+ type: list
+ elements: str
+ disable_policy_batching:
+ description:
+ - Enable/Disable the batching evaluation of prefix advertisement to all peers.
+ type: bool
+ disable_policy_batching_ipv4_prefix_list:
+ description:
+ - Enable/Disable the batching evaluation of prefix advertisements to all peers
+ with prefix list.
+ type: str
+ disable_policy_batching_ipv6_prefix_list:
+ description:
+ - Enable/Disable the batching evaluation of prefix advertisements to all peers
+ with prefix list.
+ type: str
+ enforce_first_as:
+ description:
+ - Enable/Disable enforces the neighbor autonomous system to be the first AS number
+ listed in the AS path attribute for eBGP. On NX-OS, this property is only supported
+ in the global BGP context.
+ type: bool
+ event_history_cli:
+ description:
+ - Enable/Disable cli event history buffer.
+ choices:
+ - size_small
+ - size_medium
+ - size_large
+ - size_disable
+ - default
+ - 'true'
+ - 'false'
+ type: str
+ event_history_detail:
+ description:
+ - Enable/Disable detail event history buffer.
+ choices:
+ - size_small
+ - size_medium
+ - size_large
+ - size_disable
+ - default
+ - 'true'
+ - 'false'
+ type: str
+ event_history_events:
+ description:
+ - Enable/Disable event history buffer.
+ choices:
+ - size_small
+ - size_medium
+ - size_large
+ - size_disable
+ - default
+ - 'true'
+ - 'false'
+ type: str
+ event_history_periodic:
+ description:
+ - Enable/Disable periodic event history buffer.
+ choices:
+ - size_small
+ - size_medium
+ - size_large
+ - size_disable
+ - default
+ - 'true'
+ - 'false'
+ type: str
+ fast_external_fallover:
+ description:
+ - Enable/Disable immediately reset the session if the link to a directly connected
+ BGP peer goes down. Only supported in the global BGP context.
+ type: bool
+ flush_routes:
+ description:
+ - Enable/Disable flush routes in RIB upon controlled restart. On NX-OS, this property
+ is only supported in the global BGP context.
+ type: bool
+ graceful_restart:
+ description:
+ - Enable/Disable graceful restart.
+ type: bool
+ graceful_restart_helper:
+ description:
+ - Enable/Disable graceful restart helper mode.
+ type: bool
+ graceful_restart_timers_restart:
+ description:
+ - Set maximum time for a restart sent to the BGP peer.
+ type: str
+ graceful_restart_timers_stalepath_time:
+ description:
+ - Set maximum time that BGP keeps the stale routes from the restarting BGP peer.
+ type: str
+ isolate:
+ description:
+ - Enable/Disable isolate this router from BGP perspective.
+ type: bool
+ local_as:
+ description:
+ - Local AS number to be used within a VRF instance.
+ type: str
+ log_neighbor_changes:
+ description:
+ - Enable/Disable message logging for neighbor up/down event.
+ type: bool
+ maxas_limit:
+ description:
+ - Specify Maximum number of AS numbers allowed in the AS-path attribute. Valid
+ values are between 1 and 512.
+ type: str
+ neighbor_down_fib_accelerate:
+ description:
+ - Enable/Disable handle BGP neighbor down event, due to various reasons.
+ type: bool
+ reconnect_interval:
+ description:
+ - The BGP reconnection interval for dropped sessions. Valid values are between
+ 1 and 60.
+ type: str
+ router_id:
+ description:
+ - Router Identifier (ID) of the BGP router VRF instance.
+ type: str
+ shutdown:
+ description:
+ - Administratively shutdown the BGP protocol.
+ type: bool
+ suppress_fib_pending:
+ description:
+ - Enable/Disable advertise only routes programmed in hardware to peers.
+ type: bool
+ timer_bestpath_limit:
+ description:
+ - Specify timeout for the first best path after a restart, in seconds.
+ type: str
+ timer_bgp_hold:
+ description:
+ - Set BGP hold timer.
+ type: str
+ timer_bgp_keepalive:
+ description:
+ - Set BGP keepalive timer.
+ type: str
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+
+EXAMPLES = """
+- name: Configure a simple ASN
+ cisco.nxos.nxos_bgp:
+ asn: 65535
+ vrf: test
+ router_id: 192.0.2.1
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["router bgp 65535", "vrf test", "router-id 192.0.2.1"]
+"""
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+BOOL_PARAMS = [
+ "bestpath_always_compare_med",
+ "bestpath_aspath_multipath_relax",
+ "bestpath_compare_neighborid",
+ "bestpath_compare_routerid",
+ "bestpath_cost_community_ignore",
+ "bestpath_med_confed",
+ "bestpath_med_missing_as_worst",
+ "bestpath_med_non_deterministic",
+ "disable_policy_batching",
+ "enforce_first_as",
+ "fast_external_fallover",
+ "flush_routes",
+ "graceful_restart",
+ "graceful_restart_helper",
+ "isolate",
+ "log_neighbor_changes",
+ "neighbor_down_fib_accelerate",
+ "shutdown",
+ "suppress_fib_pending",
+]
+GLOBAL_PARAMS = [
+ "disable_policy_batching",
+ "disable_policy_batching_ipv4_prefix_list",
+ "disable_policy_batching_ipv6_prefix_list",
+ "enforce_first_as",
+ "event_history_cli",
+ "event_history_detail",
+ "event_history_events",
+ "event_history_periodic",
+ "fast_external_fallover",
+ "flush_routes",
+ "isolate",
+ "suppress_fib_pending",
+ "shutdown",
+]
+PARAM_TO_DEFAULT_KEYMAP = {
+ "timer_bgp_keepalive": "60",
+ "timer_bgp_hold": "180",
+ "timer_bestpath_limit": "300",
+ "graceful_restart": True,
+ "graceful_restart_timers_restart": "120",
+ "graceful_restart_timers_stalepath_time": "300",
+ "reconnect_interval": "60",
+ "suppress_fib_pending": True,
+ "fast_external_fallover": True,
+ "enforce_first_as": True,
+ "event_history_cli": True,
+ "event_history_detail": False,
+ "event_history_events": True,
+ "event_history_periodic": True,
+ "maxas_limit": "",
+ "router_id": "",
+ "cluster_id": "",
+ "disable_policy_batching_ipv4_prefix_list": "",
+ "disable_policy_batching_ipv6_prefix_list": "",
+ "local_as": "",
+ "confederation_id": "",
+}
+PARAM_TO_COMMAND_KEYMAP = {
+ "asn": "router bgp",
+ "bestpath_always_compare_med": "bestpath always-compare-med",
+ "bestpath_aspath_multipath_relax": "bestpath as-path multipath-relax",
+ "bestpath_compare_neighborid": "bestpath compare-neighborid",
+ "bestpath_compare_routerid": "bestpath compare-routerid",
+ "bestpath_cost_community_ignore": "bestpath cost-community ignore",
+ "bestpath_med_confed": "bestpath med confed",
+ "bestpath_med_missing_as_worst": "bestpath med missing-as-worst",
+ "bestpath_med_non_deterministic": "bestpath med non-deterministic",
+ "cluster_id": "cluster-id",
+ "confederation_id": "confederation identifier",
+ "confederation_peers": "confederation peers",
+ "disable_policy_batching": "disable-policy-batching",
+ "disable_policy_batching_ipv4_prefix_list": "disable-policy-batching ipv4 prefix-list",
+ "disable_policy_batching_ipv6_prefix_list": "disable-policy-batching ipv6 prefix-list",
+ "enforce_first_as": "enforce-first-as",
+ "event_history_cli": "event-history cli",
+ "event_history_detail": "event-history detail",
+ "event_history_events": "event-history events",
+ "event_history_periodic": "event-history periodic",
+ "fast_external_fallover": "fast-external-fallover",
+ "flush_routes": "flush-routes",
+ "graceful_restart": "graceful-restart",
+ "graceful_restart_helper": "graceful-restart-helper",
+ "graceful_restart_timers_restart": "graceful-restart restart-time",
+ "graceful_restart_timers_stalepath_time": "graceful-restart stalepath-time",
+ "isolate": "isolate",
+ "local_as": "local-as",
+ "log_neighbor_changes": "log-neighbor-changes",
+ "maxas_limit": "maxas-limit",
+ "neighbor_down_fib_accelerate": "neighbor-down fib-accelerate",
+ "reconnect_interval": "reconnect-interval",
+ "router_id": "router-id",
+ "shutdown": "shutdown",
+ "suppress_fib_pending": "suppress-fib-pending",
+ "timer_bestpath_limit": "timers bestpath-limit",
+ "timer_bgp_hold": "timers bgp",
+ "timer_bgp_keepalive": "timers bgp",
+ "vrf": "vrf",
+}
+
+
+def get_value(arg, config):
+ command = PARAM_TO_COMMAND_KEYMAP.get(arg)
+
+ if command.split()[0] == "event-history":
+ has_size = re.search(
+ r"^\s+{0} size\s(?P<value>.*)$".format(command), config, re.M
+ )
+
+ if command == "event-history detail":
+ value = False
+ else:
+ value = "size_small"
+
+ if has_size:
+ value = "size_%s" % has_size.group("value")
+
+ elif arg in ["enforce_first_as", "fast_external_fallover"]:
+ no_command_re = re.compile(r"no\s+{0}\s*".format(command), re.M)
+ value = True
+
+ if no_command_re.search(config):
+ value = False
+
+ elif arg in BOOL_PARAMS:
+ has_command = re.search(r"^\s+{0}\s*$".format(command), config, re.M)
+ value = False
+
+ if has_command:
+ value = True
+ else:
+ command_val_re = re.compile(
+ r"(?:{0}\s)(?P<value>.*)".format(command), re.M
+ )
+ value = ""
+
+ has_command = command_val_re.search(config)
+ if has_command:
+ found_value = has_command.group("value")
+
+ if arg == "confederation_peers":
+ value = found_value.split()
+ elif arg == "timer_bgp_keepalive":
+ value = found_value.split()[0]
+ elif arg == "timer_bgp_hold":
+ split_values = found_value.split()
+ if len(split_values) == 2:
+ value = split_values[1]
+ elif found_value:
+ value = found_value
+
+ return value
+
+
+def get_existing(module, args, warnings):
+ existing = {}
+ netcfg = CustomNetworkConfig(
+ indent=2, contents=get_config(module, flags=["bgp all"])
+ )
+
+ asn_re = re.compile(
+ r".*router\sbgp\s(?P<existing_asn>\d+(\.\d+)?).*", re.S
+ )
+ asn_match = asn_re.match(str(netcfg))
+
+ if asn_match:
+ existing_asn = asn_match.group("existing_asn")
+ bgp_parent = "router bgp {0}".format(existing_asn)
+
+ if module.params["vrf"] != "default":
+ parents = [bgp_parent, "vrf {0}".format(module.params["vrf"])]
+ else:
+ parents = [bgp_parent]
+
+ config = netcfg.get_section(parents)
+ if config:
+ for arg in args:
+ if arg != "asn" and (
+ module.params["vrf"] == "default"
+ or arg not in GLOBAL_PARAMS
+ ):
+ existing[arg] = get_value(arg, config)
+
+ existing["asn"] = existing_asn
+ if module.params["vrf"] == "default":
+ existing["vrf"] = "default"
+
+ if (
+ not existing
+ and module.params["vrf"] != "default"
+ and module.params["state"] == "present"
+ ):
+ msg = "VRF {0} doesn't exist.".format(module.params["vrf"])
+ warnings.append(msg)
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key in table:
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = table.get(key)
+
+ return new_dict
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, value in proposed_commands.items():
+ if value is True:
+ commands.append(key)
+ elif value is False:
+ commands.append("no {0}".format(key))
+ elif value == "default":
+ default_value = PARAM_TO_DEFAULT_KEYMAP.get(key)
+ existing_value = existing_commands.get(key)
+
+ if default_value:
+ commands.append("{0} {1}".format(key, default_value))
+ elif existing_value:
+ if key == "confederation peers":
+ existing_value = " ".join(existing_value)
+ commands.append("no {0} {1}".format(key, existing_value))
+ elif not value:
+ existing_value = existing_commands.get(key)
+ if existing_value:
+ commands.append("no {0} {1}".format(key, existing_value))
+ elif key == "confederation peers":
+ commands.append("{0} {1}".format(key, value))
+ elif key.startswith("timers bgp"):
+ command = "timers bgp {0} {1}".format(
+ proposed["timer_bgp_keepalive"], proposed["timer_bgp_hold"]
+ )
+ if command not in commands:
+ commands.append(command)
+ else:
+ if value.startswith("size"):
+ value = value.replace("_", " ")
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+
+ parents = []
+ if commands:
+ commands = fix_commands(commands)
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+ elif proposed:
+ if module.params["vrf"] != "default":
+ commands.append("vrf {0}".format(module.params["vrf"]))
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ else:
+ commands.append("router bgp {0}".format(module.params["asn"]))
+
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, existing, candidate):
+ commands = []
+ parents = []
+ if module.params["vrf"] == "default":
+ commands.append("no router bgp {0}".format(module.params["asn"]))
+ elif existing.get("vrf") == module.params["vrf"]:
+ commands.append("no vrf {0}".format(module.params["vrf"]))
+ parents = ["router bgp {0}".format(module.params["asn"])]
+
+ candidate.add(commands, parents=parents)
+
+
+def fix_commands(commands):
+ local_as_command = ""
+ confederation_id_command = ""
+ confederation_peers_command = ""
+
+ for command in commands:
+ if "local-as" in command:
+ local_as_command = command
+ elif "confederation identifier" in command:
+ confederation_id_command = command
+ elif "confederation peers" in command:
+ confederation_peers_command = command
+
+ if local_as_command and confederation_id_command:
+ if "no" in confederation_id_command:
+ commands.pop(commands.index(local_as_command))
+ commands.pop(commands.index(confederation_id_command))
+ commands.append(confederation_id_command)
+ commands.append(local_as_command)
+ else:
+ commands.pop(commands.index(local_as_command))
+ commands.pop(commands.index(confederation_id_command))
+ commands.append(local_as_command)
+ commands.append(confederation_id_command)
+
+ if confederation_peers_command and confederation_id_command:
+ if local_as_command:
+ if "no" in local_as_command:
+ commands.pop(commands.index(local_as_command))
+ commands.pop(commands.index(confederation_id_command))
+ commands.pop(commands.index(confederation_peers_command))
+ commands.append(confederation_id_command)
+ commands.append(confederation_peers_command)
+ commands.append(local_as_command)
+ else:
+ commands.pop(commands.index(local_as_command))
+ commands.pop(commands.index(confederation_id_command))
+ commands.pop(commands.index(confederation_peers_command))
+ commands.append(local_as_command)
+ commands.append(confederation_id_command)
+ commands.append(confederation_peers_command)
+ else:
+ commands.pop(commands.index(confederation_peers_command))
+ commands.pop(commands.index(confederation_id_command))
+ commands.append(confederation_id_command)
+ commands.append(confederation_peers_command)
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ asn=dict(required=True, type="str"),
+ vrf=dict(required=False, type="str", default="default"),
+ bestpath_always_compare_med=dict(required=False, type="bool"),
+ bestpath_aspath_multipath_relax=dict(required=False, type="bool"),
+ bestpath_compare_neighborid=dict(required=False, type="bool"),
+ bestpath_compare_routerid=dict(required=False, type="bool"),
+ bestpath_cost_community_ignore=dict(required=False, type="bool"),
+ bestpath_med_confed=dict(required=False, type="bool"),
+ bestpath_med_missing_as_worst=dict(required=False, type="bool"),
+ bestpath_med_non_deterministic=dict(required=False, type="bool"),
+ cluster_id=dict(required=False, type="str"),
+ confederation_id=dict(required=False, type="str"),
+ confederation_peers=dict(required=False, type="list", elements="str"),
+ disable_policy_batching=dict(required=False, type="bool"),
+ disable_policy_batching_ipv4_prefix_list=dict(
+ required=False, type="str"
+ ),
+ disable_policy_batching_ipv6_prefix_list=dict(
+ required=False, type="str"
+ ),
+ enforce_first_as=dict(required=False, type="bool"),
+ event_history_cli=dict(
+ required=False,
+ choices=[
+ "true",
+ "false",
+ "default",
+ "size_small",
+ "size_medium",
+ "size_large",
+ "size_disable",
+ ],
+ ),
+ event_history_detail=dict(
+ required=False,
+ choices=[
+ "true",
+ "false",
+ "default",
+ "size_small",
+ "size_medium",
+ "size_large",
+ "size_disable",
+ ],
+ ),
+ event_history_events=dict(
+ required=False,
+ choices=[
+ "true",
+ "false",
+ "default",
+ "size_small",
+ "size_medium",
+ "size_large",
+ "size_disable",
+ ],
+ ),
+ event_history_periodic=dict(
+ required=False,
+ choices=[
+ "true",
+ "false",
+ "default",
+ "size_small",
+ "size_medium",
+ "size_large",
+ "size_disable",
+ ],
+ ),
+ fast_external_fallover=dict(required=False, type="bool"),
+ flush_routes=dict(required=False, type="bool"),
+ graceful_restart=dict(required=False, type="bool"),
+ graceful_restart_helper=dict(required=False, type="bool"),
+ graceful_restart_timers_restart=dict(required=False, type="str"),
+ graceful_restart_timers_stalepath_time=dict(
+ required=False, type="str"
+ ),
+ isolate=dict(required=False, type="bool"),
+ local_as=dict(required=False, type="str"),
+ log_neighbor_changes=dict(required=False, type="bool"),
+ maxas_limit=dict(required=False, type="str"),
+ neighbor_down_fib_accelerate=dict(required=False, type="bool"),
+ reconnect_interval=dict(required=False, type="str"),
+ router_id=dict(required=False, type="str"),
+ shutdown=dict(required=False, type="bool"),
+ suppress_fib_pending=dict(required=False, type="bool"),
+ timer_bestpath_limit=dict(required=False, type="str"),
+ timer_bgp_hold=dict(required=False, type="str"),
+ timer_bgp_keepalive=dict(required=False, type="str"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ )
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_together=[["timer_bgp_hold", "timer_bgp_keepalive"]],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+
+ if module.params["vrf"] != "default":
+ for param in GLOBAL_PARAMS:
+ if module.params[param]:
+ module.fail_json(
+ msg='Global params can be modified only under "default" VRF.',
+ vrf=module.params["vrf"],
+ global_param=param,
+ )
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing = get_existing(module, args, warnings)
+
+ if existing.get("asn") and state == "present":
+ if existing.get("asn") != module.params["asn"]:
+ module.fail_json(
+ msg="Another BGP ASN already exists.",
+ proposed_asn=module.params["asn"],
+ existing_asn=existing.get("asn"),
+ )
+
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key not in ["asn", "vrf"]:
+ if str(value).lower() == "default":
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key, "default")
+ if key == "confederation_peers":
+ if value[0] == "default":
+ if existing.get(key):
+ proposed[key] = "default"
+ else:
+ v = set([int(i) for i in value])
+ ex = set([int(i) for i in existing.get(key)])
+ if v != ex:
+ proposed[key] = " ".join(str(s) for s in v)
+ else:
+ if existing.get(key) != value:
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ elif existing.get("asn") == module.params["asn"]:
+ state_absent(module, existing, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ if not module.check_mode:
+ load_config(module, candidate)
+ result["changed"] = True
+ result["commands"] = candidate
+ else:
+ result["commands"] = []
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_af.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_af.py
new file mode 100644
index 00000000..6f24acfe
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_af.py
@@ -0,0 +1,935 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_bgp_af
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages BGP Address-family configuration.
+description:
+- Manages BGP Address-family configurations on NX-OS switches.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the whole BGP ASN configuration
+- Default, where supported, restores params default value.
+options:
+ asn:
+ description:
+ - BGP autonomous system number. Valid values are String, Integer in ASPLAIN or
+ ASDOT notation.
+ required: true
+ type: str
+ vrf:
+ description:
+ - Name of the VRF. The name 'default' is a valid VRF representing the global bgp.
+ default: 'default'
+ type: str
+ afi:
+ description:
+ - Address Family Identifier.
+ required: true
+ choices:
+ - ipv4
+ - ipv6
+ - vpnv4
+ - vpnv6
+ - l2vpn
+ type: str
+ safi:
+ description:
+ - Sub Address Family Identifier.
+ required: true
+ choices:
+ - unicast
+ - multicast
+ - evpn
+ type: str
+ additional_paths_install:
+ description:
+ - Install a backup path into the forwarding table and provide prefix independent
+ convergence (PIC) in case of a PE-CE link failure.
+ type: bool
+ additional_paths_receive:
+ description:
+ - Enables the receive capability of additional paths for all of the neighbors
+ under this address family for which the capability has not been disabled.
+ type: bool
+ additional_paths_selection:
+ description:
+ - Configures the capability of selecting additional paths for a prefix. Valid
+ values are a string defining the name of the route-map.
+ type: str
+ additional_paths_send:
+ description:
+ - Enables the send capability of additional paths for all of the neighbors under
+ this address family for which the capability has not been disabled.
+ type: bool
+ advertise_l2vpn_evpn:
+ description:
+ - Advertise evpn routes.
+ type: bool
+ client_to_client:
+ description:
+ - Configure client-to-client route reflection.
+ type: bool
+ dampen_igp_metric:
+ description:
+ - Specify dampen value for IGP metric-related changes, in seconds. Valid values
+ are integer and keyword 'default'.
+ type: str
+ dampening_state:
+ description:
+ - Enable/disable route-flap dampening.
+ type: bool
+ dampening_half_time:
+ description:
+ - Specify decay half-life in minutes for route-flap dampening. Valid values are
+ integer and keyword 'default'.
+ type: str
+ dampening_max_suppress_time:
+ description:
+ - Specify max suppress time for route-flap dampening stable route. Valid values
+ are integer and keyword 'default'.
+ type: str
+ dampening_reuse_time:
+ description:
+ - Specify route reuse time for route-flap dampening. Valid values are integer
+ and keyword 'default'.
+ type: str
+ dampening_routemap:
+ description:
+ - Specify route-map for route-flap dampening. Valid values are a string defining
+ the name of the route-map.
+ type: str
+ dampening_suppress_time:
+ description:
+ - Specify route suppress time for route-flap dampening. Valid values are integer
+ and keyword 'default'.
+ type: str
+ default_information_originate:
+ description:
+ - Default information originate.
+ type: bool
+ default_metric:
+ description:
+ - Sets default metrics for routes redistributed into BGP. Valid values are Integer
+ or keyword 'default'
+ type: str
+ distance_ebgp:
+ description:
+ - Sets the administrative distance for eBGP routes. Valid values are Integer or
+ keyword 'default'.
+ type: str
+ distance_ibgp:
+ description:
+ - Sets the administrative distance for iBGP routes. Valid values are Integer or
+ keyword 'default'.
+ type: str
+ distance_local:
+ description:
+ - Sets the administrative distance for local BGP routes. Valid values are Integer
+ or keyword 'default'.
+ type: str
+ inject_map:
+ description:
+ - An array of route-map names which will specify prefixes to inject. Each array
+ entry must first specify the inject-map name, secondly an exist-map name, and
+ optionally the copy-attributes keyword which indicates that attributes should
+ be copied from the aggregate. For example [['lax_inject_map', 'lax_exist_map'],
+ ['nyc_inject_map', 'nyc_exist_map', 'copy-attributes'], ['fsd_inject_map', 'fsd_exist_map']].
+ type: list
+ elements: list
+ maximum_paths:
+ description:
+ - Configures the maximum number of equal-cost paths for load sharing. Valid value
+ is an integer in the range 1-64.
+ type: str
+ maximum_paths_ibgp:
+ description:
+ - Configures the maximum number of ibgp equal-cost paths for load sharing. Valid
+ value is an integer in the range 1-64.
+ type: str
+ networks:
+ description:
+ - Networks to configure. Valid value is a list of network prefixes to advertise.
+ The list must be in the form of an array. Each entry in the array must include
+ a prefix address and an optional route-map. For example [['10.0.0.0/16', 'routemap_LA'],
+ ['192.168.1.1', 'Chicago'], ['192.168.2.0/24'], ['192.168.3.0/24', 'routemap_NYC']].
+ type: list
+ elements: list
+ next_hop_route_map:
+ description:
+ - Configure a route-map for valid nexthops. Valid values are a string defining
+ the name of the route-map.
+ type: str
+ redistribute:
+ description:
+ - A list of redistribute directives. Multiple redistribute entries are allowed.
+ The list must be in the form of a nested array. the first entry of each array
+ defines the source-protocol to redistribute from; the second entry defines a
+ route-map name. A route-map is highly advised but may be optional on some platforms,
+ in which case it may be omitted from the array list. For example [['direct',
+ 'rm_direct'], ['lisp', 'rm_lisp']].
+ type: list
+ elements: list
+ suppress_inactive:
+ description:
+ - Advertises only active routes to peers.
+ type: bool
+ table_map:
+ description:
+ - Apply table-map to filter routes downloaded into URIB. Valid values are a string.
+ type: str
+ table_map_filter:
+ description:
+ - Filters routes rejected by the route-map and does not download them to the RIB.
+ type: bool
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ retain_route_target:
+ description:
+ - Retains all of the routes or the routes which are part of configured route-map.
+ Valid values are route-map names or keyword C(all) or keyword C(default). C(all)
+ retains all the routes regardless of Target-VPN community. C(default) will disable
+ the retain route target option. If you are using route-map name please ensure
+ that the name is not same as C(all) and C(default).
+ type: str
+ version_added: 1.1.0
+"""
+EXAMPLES = """
+# configure a simple address-family
+- cisco.nxos.nxos_bgp_af:
+ asn: 65535
+ vrf: TESTING
+ afi: ipv4
+ safi: unicast
+ advertise_l2vpn_evpn: true
+ state: present
+ retain_route_target: all
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["router bgp 65535", "vrf TESTING",
+ "address-family ipv4 unicast", "advertise l2vpn evpn",
+ "retain route-target all"]
+"""
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+BOOL_PARAMS = [
+ "additional_paths_install",
+ "additional_paths_receive",
+ "additional_paths_send",
+ "advertise_l2vpn_evpn",
+ "dampening_state",
+ "default_information_originate",
+ "suppress_inactive",
+]
+PARAM_TO_DEFAULT_KEYMAP = {
+ "maximum_paths": "1",
+ "maximum_paths_ibgp": "1",
+ "client_to_client": True,
+ "distance_ebgp": "20",
+ "distance_ibgp": "200",
+ "distance_local": "220",
+ "dampen_igp_metric": "600",
+}
+PARAM_TO_COMMAND_KEYMAP = {
+ "asn": "router bgp",
+ "afi": "address-family",
+ "safi": "address-family",
+ "additional_paths_install": "additional-paths install backup",
+ "additional_paths_receive": "additional-paths receive",
+ "additional_paths_selection": "additional-paths selection route-map",
+ "additional_paths_send": "additional-paths send",
+ "advertise_l2vpn_evpn": "advertise l2vpn evpn",
+ "client_to_client": "client-to-client reflection",
+ "dampen_igp_metric": "dampen-igp-metric",
+ "dampening_state": "dampening",
+ "dampening_half_time": "dampening",
+ "dampening_max_suppress_time": "dampening",
+ "dampening_reuse_time": "dampening",
+ "dampening_routemap": "dampening route-map",
+ "dampening_suppress_time": "dampening",
+ "default_information_originate": "default-information originate",
+ "default_metric": "default-metric",
+ "distance_ebgp": "distance",
+ "distance_ibgp": "distance",
+ "distance_local": "distance",
+ "inject_map": "inject-map",
+ "maximum_paths": "maximum-paths",
+ "maximum_paths_ibgp": "maximum-paths ibgp",
+ "networks": "network",
+ "redistribute": "redistribute",
+ "next_hop_route_map": "nexthop route-map",
+ "suppress_inactive": "suppress-inactive",
+ "table_map": "table-map",
+ "table_map_filter": "table-map-filter",
+ "vrf": "vrf",
+ "retain_route_target": "retain route-target",
+}
+DAMPENING_PARAMS = [
+ "dampening_half_time",
+ "dampening_suppress_time",
+ "dampening_reuse_time",
+ "dampening_max_suppress_time",
+]
+
+
+def get_value(arg, config, module):
+ command = PARAM_TO_COMMAND_KEYMAP[arg]
+ command_val_re = re.compile(
+ r"(?:{0}\s)(?P<value>.*)$".format(command), re.M
+ )
+ has_command_val = command_val_re.search(config)
+
+ if arg in ["networks", "redistribute", "inject_map"]:
+ value = []
+ for ele in command_val_re.findall(config):
+ tl = ele.split()
+ if "exist-map" in tl:
+ tl.remove("exist-map")
+ elif "route-map" in tl:
+ tl.remove("route-map")
+ value.append(tl)
+
+ elif command == "distance":
+ distance_re = (
+ r".*distance\s(?P<d_ebgp>\w+)\s(?P<d_ibgp>\w+)\s(?P<d_local>\w+)"
+ )
+ match_distance = re.match(distance_re, config, re.DOTALL)
+
+ value = ""
+ if match_distance:
+ distance_group = match_distance.groupdict()
+
+ if arg == "distance_ebgp":
+ value = distance_group["d_ebgp"]
+ elif arg == "distance_ibgp":
+ value = distance_group["d_ibgp"]
+ elif arg == "distance_local":
+ value = distance_group["d_local"]
+
+ elif command.split()[0] == "dampening":
+ value = ""
+ if arg == "dampen_igp_metric" or arg == "dampening_routemap":
+ if command in config:
+ value = has_command_val.group("value")
+ else:
+ dampening_re = r".*dampening\s(?P<half>\w+)\s(?P<reuse>\w+)\s(?P<suppress>\w+)\s(?P<max_suppress>\w+)"
+ match_dampening = re.match(dampening_re, config, re.DOTALL)
+ if match_dampening:
+ dampening_group = match_dampening.groupdict()
+
+ if arg == "dampening_half_time":
+ value = dampening_group["half"]
+ elif arg == "dampening_reuse_time":
+ value = dampening_group["reuse"]
+ elif arg == "dampening_suppress_time":
+ value = dampening_group["suppress"]
+ elif arg == "dampening_max_suppress_time":
+ value = dampening_group["max_suppress"]
+ else:
+ if arg == "dampening_state":
+ value = True if "dampening" in config else False
+ elif arg == "table_map_filter":
+ tmf_regex = re.compile(r"\s+table-map.*filter$", re.M)
+ value = False
+ if tmf_regex.search(config):
+ value = True
+
+ elif arg == "table_map":
+ tm_regex = re.compile(
+ r"(?:table-map\s)(?P<value>\S+)(\sfilter)?$", re.M
+ )
+ has_tablemap = tm_regex.search(config)
+ value = ""
+ if has_tablemap:
+ value = has_tablemap.group("value")
+
+ elif arg == "client_to_client":
+ no_command_re = re.compile(r"^\s+no\s{0}\s*$".format(command), re.M)
+ value = True
+
+ if no_command_re.search(config):
+ value = False
+
+ elif arg == "retain_route_target":
+ value = ""
+ if command in config:
+ route_target = has_command_val.group("value")
+ if route_target:
+ if route_target == "all":
+ value = "all"
+ elif "route-map" in route_target:
+ value = route_target.replace("route-map ", "")
+
+ elif arg in BOOL_PARAMS:
+ command_re = re.compile(r"^\s+{0}\s*$".format(command), re.M)
+ value = False
+
+ if command_re.search(config):
+ value = True
+
+ else:
+ value = ""
+
+ if has_command_val:
+ value = has_command_val.group("value")
+
+ return value
+
+
+def get_existing(module, args, warnings):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+
+ asn_regex = re.compile(
+ r".*router\sbgp\s(?P<existing_asn>\d+(\.\d+)?).*", re.DOTALL
+ )
+ match_asn = asn_regex.match(str(netcfg))
+
+ if match_asn:
+ existing_asn = match_asn.group("existing_asn")
+ parents = ["router bgp {0}".format(existing_asn)]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ parents.append(
+ "address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ )
+ config = netcfg.get_section(parents)
+
+ if config:
+ for arg in args:
+ if arg not in ["asn", "afi", "safi", "vrf"]:
+ gv = get_value(arg, config, module)
+ if gv:
+ existing[arg] = gv
+ else:
+ if (
+ arg != "client_to_client"
+ and arg in PARAM_TO_DEFAULT_KEYMAP.keys()
+ ):
+ existing[arg] = PARAM_TO_DEFAULT_KEYMAP.get(arg)
+ else:
+ existing[arg] = gv
+
+ existing["asn"] = existing_asn
+ existing["afi"] = module.params["afi"]
+ existing["safi"] = module.params["safi"]
+ existing["vrf"] = module.params["vrf"]
+ else:
+ warnings.append(
+ "The BGP process {0} didn't exist but the task just created it.".format(
+ module.params["asn"]
+ )
+ )
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = value
+
+ return new_dict
+
+
+def fix_proposed(module, proposed, existing):
+ commands = list()
+ command = ""
+ fixed_proposed = {}
+ for key, value in proposed.items():
+ if key in DAMPENING_PARAMS:
+ if value != "default":
+ command = "dampening {0} {1} {2} {3}".format(
+ proposed.get("dampening_half_time"),
+ proposed.get("dampening_reuse_time"),
+ proposed.get("dampening_suppress_time"),
+ proposed.get("dampening_max_suppress_time"),
+ )
+ else:
+ if existing.get(key):
+ command = "no dampening {0} {1} {2} {3}".format(
+ existing["dampening_half_time"],
+ existing["dampening_reuse_time"],
+ existing["dampening_suppress_time"],
+ existing["dampening_max_suppress_time"],
+ )
+ if "default" in command:
+ command = ""
+ elif key.startswith("distance"):
+ command = "distance {0} {1} {2}".format(
+ proposed.get("distance_ebgp"),
+ proposed.get("distance_ibgp"),
+ proposed.get("distance_local"),
+ )
+ else:
+ fixed_proposed[key] = value
+
+ if command:
+ if command not in commands:
+ commands.append(command)
+
+ return fixed_proposed, commands
+
+
+def default_existing(existing_value, key, value):
+ commands = []
+ if key == "network":
+ for network in existing_value:
+ if len(network) == 2:
+ commands.append(
+ "no network {0} route-map {1}".format(
+ network[0], network[1]
+ )
+ )
+ elif len(network) == 1:
+ commands.append("no network {0}".format(network[0]))
+
+ elif key == "inject-map":
+ for maps in existing_value:
+ if len(maps) == 2:
+ commands.append(
+ "no inject-map {0} exist-map {1}".format(maps[0], maps[1])
+ )
+ elif len(maps) == 3:
+ commands.append(
+ "no inject-map {0} exist-map {1} "
+ "copy-attributes".format(maps[0], maps[1])
+ )
+
+ elif key == "redistribute":
+ for maps in existing_value:
+ commands.append(
+ "no redistribute {0} route-map {1}".format(maps[0], maps[1])
+ )
+
+ elif key == "retain route-target":
+ if existing_value == "all":
+ commands.append("no {0} {1}".format(key, existing_value))
+ elif existing_value != "default":
+ commands.append("no {0} route-map {1}".format(key, existing_value))
+
+ else:
+ commands.append("no {0} {1}".format(key, existing_value))
+ return commands
+
+
+def get_network_command(existing, key, value):
+ commands = []
+ existing_networks = existing.get("networks", [])
+ for inet in value:
+ if not isinstance(inet, list):
+ inet = [inet]
+ if inet not in existing_networks:
+ if len(inet) == 1:
+ command = "{0} {1}".format(key, inet[0])
+ elif len(inet) == 2:
+ command = "{0} {1} route-map {2}".format(key, inet[0], inet[1])
+ if command:
+ commands.append(command)
+ for enet in existing_networks:
+ if enet not in value:
+ if len(enet) == 1:
+ command = "no {0} {1}".format(key, enet[0])
+ elif len(enet) == 2:
+ command = "no {0} {1} route-map {2}".format(
+ key, enet[0], enet[1]
+ )
+ if command:
+ commands.append(command)
+ return commands
+
+
+def get_inject_map_command(existing, key, value):
+ commands = []
+ existing_maps = existing.get("inject_map", [])
+ for maps in value:
+ if not isinstance(maps, list):
+ maps = [maps]
+ if maps not in existing_maps:
+ if len(maps) == 2:
+ command = "inject-map {0} exist-map {1}".format(
+ maps[0], maps[1]
+ )
+ elif len(maps) == 3:
+ command = (
+ "inject-map {0} exist-map {1} "
+ "copy-attributes".format(maps[0], maps[1])
+ )
+ if command:
+ commands.append(command)
+ for emaps in existing_maps:
+ if emaps not in value:
+ if len(emaps) == 2:
+ command = "no inject-map {0} exist-map {1}".format(
+ emaps[0], emaps[1]
+ )
+ elif len(emaps) == 3:
+ command = (
+ "no inject-map {0} exist-map {1} "
+ "copy-attributes".format(emaps[0], emaps[1])
+ )
+ if command:
+ commands.append(command)
+ return commands
+
+
+def get_redistribute_command(existing, key, value):
+ commands = []
+ existing_rules = existing.get("redistribute", [])
+ for rule in value:
+ if not isinstance(rule, list):
+ rule = [rule]
+ if rule not in existing_rules:
+ command = "redistribute {0} route-map {1}".format(rule[0], rule[1])
+ commands.append(command)
+ for erule in existing_rules:
+ if erule not in value:
+ command = "no redistribute {0} route-map {1}".format(
+ erule[0], erule[1]
+ )
+ commands.append(command)
+ return commands
+
+
+def get_table_map_command(module, existing, key, value):
+ commands = []
+ if key == "table-map":
+ if value != "default":
+ command = "{0} {1}".format(key, module.params["table_map"])
+ if (
+ module.params["table_map_filter"] is not None
+ and module.params["table_map_filter"] != "default"
+ ):
+ command += " filter"
+ commands.append(command)
+ else:
+ if existing.get("table_map"):
+ command = "no {0} {1}".format(key, existing.get("table_map"))
+ commands.append(command)
+ return commands
+
+
+def get_retain_route_target_command(existing, key, value):
+ commands = []
+ if key == "retain route-target":
+ if value != "default":
+ if value == "all":
+ command = "{0} {1}".format(key, value)
+ else:
+ command = "{0} route-map {1}".format(key, value)
+ else:
+ existing_value = existing.get("retain_route_target")
+ if existing_value == "all":
+ command = "no {0} {1}".format(key, existing_value)
+ else:
+ command = "no {0} route-map {1}".format(key, existing_value)
+ commands.append(command)
+ return commands
+
+
+def get_default_table_map_filter(existing):
+ commands = []
+ existing_table_map_filter = existing.get("table_map_filter")
+ if existing_table_map_filter:
+ existing_table_map = existing.get("table_map")
+ if existing_table_map:
+ command = "table-map {0}".format(existing_table_map)
+ commands.append(command)
+ return commands
+
+
+def state_present(module, existing, proposed, candidate):
+ fixed_proposed, commands = fix_proposed(module, proposed, existing)
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, fixed_proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+ for key, value in proposed_commands.items():
+ if key == "address-family":
+ addr_family_command = "address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ if addr_family_command not in commands:
+ commands.append(addr_family_command)
+
+ elif key.startswith("table-map"):
+ table_map_commands = get_table_map_command(
+ module, existing, key, value
+ )
+ if table_map_commands:
+ commands.extend(table_map_commands)
+
+ elif value is True:
+ commands.append(key)
+
+ elif value is False:
+ commands.append("no {0}".format(key))
+
+ elif value == "default":
+ if key in PARAM_TO_DEFAULT_KEYMAP:
+ commands.append(
+ "{0} {1}".format(key, PARAM_TO_DEFAULT_KEYMAP[key])
+ )
+
+ elif existing_commands.get(key):
+ if key == "table-map-filter":
+ default_tmf_command = get_default_table_map_filter(
+ existing
+ )
+
+ if default_tmf_command:
+ commands.extend(default_tmf_command)
+ else:
+ existing_value = existing_commands.get(key)
+ default_command = default_existing(
+ existing_value, key, value
+ )
+ if default_command:
+ commands.extend(default_command)
+ else:
+ if key == "network":
+ network_commands = get_network_command(existing, key, value)
+ if network_commands:
+ commands.extend(network_commands)
+
+ elif key == "inject-map":
+ inject_map_commands = get_inject_map_command(
+ existing, key, value
+ )
+ if inject_map_commands:
+ commands.extend(inject_map_commands)
+
+ elif key == "redistribute":
+ redistribute_commands = get_redistribute_command(
+ existing, key, value
+ )
+ if redistribute_commands:
+ commands.extend(redistribute_commands)
+
+ elif key == "retain route-target":
+ retain_route_target_commands = get_retain_route_target_command(
+ existing, key, value
+ )
+ if retain_route_target_commands:
+ commands.extend(retain_route_target_commands)
+
+ else:
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+
+ if commands:
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ addr_family_command = "address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ parents.append(addr_family_command)
+ if addr_family_command in commands:
+ commands.remove(addr_family_command)
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, candidate):
+ commands = []
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ commands.append(
+ "no address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ )
+ candidate.add(commands, parents=parents)
+
+
+def main():
+ argument_spec = dict(
+ asn=dict(required=True, type="str"),
+ vrf=dict(required=False, type="str", default="default"),
+ safi=dict(
+ required=True, type="str", choices=["unicast", "multicast", "evpn"]
+ ),
+ afi=dict(
+ required=True,
+ type="str",
+ choices=["ipv4", "ipv6", "vpnv4", "vpnv6", "l2vpn"],
+ ),
+ additional_paths_install=dict(required=False, type="bool"),
+ additional_paths_receive=dict(required=False, type="bool"),
+ additional_paths_selection=dict(required=False, type="str"),
+ additional_paths_send=dict(required=False, type="bool"),
+ advertise_l2vpn_evpn=dict(required=False, type="bool"),
+ client_to_client=dict(required=False, type="bool"),
+ dampen_igp_metric=dict(required=False, type="str"),
+ dampening_state=dict(required=False, type="bool"),
+ dampening_half_time=dict(required=False, type="str"),
+ dampening_max_suppress_time=dict(required=False, type="str"),
+ dampening_reuse_time=dict(required=False, type="str"),
+ dampening_routemap=dict(required=False, type="str"),
+ dampening_suppress_time=dict(required=False, type="str"),
+ default_information_originate=dict(required=False, type="bool"),
+ default_metric=dict(required=False, type="str"),
+ distance_ebgp=dict(required=False, type="str"),
+ distance_ibgp=dict(required=False, type="str"),
+ distance_local=dict(required=False, type="str"),
+ inject_map=dict(required=False, type="list", elements="list"),
+ maximum_paths=dict(required=False, type="str"),
+ maximum_paths_ibgp=dict(required=False, type="str"),
+ networks=dict(required=False, type="list", elements="list"),
+ next_hop_route_map=dict(required=False, type="str"),
+ redistribute=dict(required=False, type="list", elements="list"),
+ suppress_inactive=dict(required=False, type="bool"),
+ table_map=dict(required=False, type="str"),
+ table_map_filter=dict(required=False, type="bool"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ retain_route_target=dict(required=False, type="str"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ mutually_exclusive = [
+ ("dampening_state", "dampening_routemap"),
+ ("dampening_state", "dampening_half_time"),
+ ("dampening_state", "dampening_suppress_time"),
+ ("dampening_state", "dampening_reuse_time"),
+ ("dampening_state", "dampening_max_suppress_time"),
+ ("dampening_routemap", "dampening_half_time"),
+ ("dampening_routemap", "dampening_suppress_time"),
+ ("dampening_routemap", "dampening_reuse_time"),
+ ("dampening_routemap", "dampening_max_suppress_time"),
+ ]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ required_together=[
+ DAMPENING_PARAMS,
+ ["distance_ibgp", "distance_ebgp", "distance_local"],
+ ],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+
+ if module.params["advertise_l2vpn_evpn"]:
+ if module.params["vrf"] == "default":
+ module.fail_json(
+ msg="It is not possible to advertise L2VPN "
+ "EVPN in the default VRF. Please specify "
+ "another one.",
+ vrf=module.params["vrf"],
+ )
+
+ if module.params["table_map_filter"] and not module.params["table_map"]:
+ module.fail_json(
+ msg="table_map param is needed when using"
+ " table_map_filter filter."
+ )
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing = get_existing(module, args, warnings)
+
+ if existing.get("asn") and state == "present":
+ if existing.get("asn") != module.params["asn"]:
+ module.fail_json(
+ msg="Another BGP ASN already exists.",
+ proposed_asn=module.params["asn"],
+ existing_asn=existing.get("asn"),
+ )
+
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ for arg in ["networks", "inject_map", "redistribute"]:
+ if proposed_args.get(arg):
+ if proposed_args[arg][0] == "default":
+ proposed_args[arg] = "default"
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key not in ["asn", "vrf"]:
+ if str(value).lower() == "default":
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key, "default")
+ if existing.get(key) != value:
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ elif state == "absent" and existing:
+ state_absent(module, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ if not module.check_mode:
+ load_config(module, candidate)
+ result["changed"] = True
+ result["commands"] = candidate
+ else:
+ result["commands"] = []
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_global.py
new file mode 100644
index 00000000..fcb8fbbc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_global.py
@@ -0,0 +1,1657 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The module file for nxos_bgp_global
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+module: nxos_bgp_global
+short_description: BGP Global resource module.
+description:
+- This module manages global BGP configuration on devices running Cisco NX-OS.
+version_added: 1.4.0
+notes:
+- Tested against NX-OS 9.3.6.
+- This module works with connection C(network_cli) and C(httpapi).
+author: Nilashish Chakraborty (@NilashishC)
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section '^router bgp').
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A list of BGP process configuration.
+ type: dict
+ suboptions:
+ as_number:
+ description: Autonomous System Number of the router.
+ type: str
+ affinity_group:
+ description: Configure an affinity group.
+ type: dict
+ suboptions:
+ group_id:
+ description: Affinity Group ID.
+ type: int
+ bestpath: &bestpath
+ description: Define the default bestpath selection algorithm.
+ type: dict
+ suboptions:
+ always_compare_med:
+ description: Compare MED on paths from different AS.
+ type: bool
+ as_path:
+ description: AS-Path.
+ type: dict
+ suboptions:
+ ignore:
+ description: Ignore AS-Path during bestpath selection.
+ type: bool
+ multipath_relax:
+ description: Relax AS-Path restriction when choosing multipaths.
+ type: bool
+ compare_neighborid:
+ description: When more paths are available than max path config, use neighborid as tie-breaker.
+ type: bool
+ compare_routerid:
+ description: Compare router-id for identical EBGP paths.
+ type: bool
+ cost_community_ignore:
+ description: Ignore cost communities in bestpath selection.
+ type: bool
+ igp_metric_ignore:
+ description: Ignore IGP metric for next-hop during bestpath selection.
+ type: bool
+ med:
+ description: MED
+ type: dict
+ suboptions:
+ confed:
+ description: Compare MED only from paths originated from within a confederation.
+ type: bool
+ missing_as_worst:
+ description: Treat missing MED as highest MED.
+ type: bool
+ non_deterministic:
+ description: Not always pick the best-MED path among paths from same AS.
+ type: bool
+ cluster_id: &cluster_id
+ description: Configure Route Reflector Cluster-ID.
+ type: str
+ confederation: &confederation
+ description: AS confederation parameters.
+ type: dict
+ suboptions:
+ identifier:
+ description: Set routing domain confederation AS.
+ type: str
+ peers:
+ description: Peer ASs in BGP confederation.
+ type: list
+ elements: str
+ disable_policy_batching:
+ description: Disable batching evaluation of outbound policy for a peer.
+ type: dict
+ suboptions:
+ set:
+ description: Set policy batching.
+ type: bool
+ ipv4:
+ description: IPv4 address-family settings.
+ type: dict
+ suboptions:
+ prefix_list:
+ description: Name of prefix-list to apply.
+ type: str
+ ipv6:
+ description: IPv6 address-family settings.
+ type: dict
+ suboptions:
+ prefix_list:
+ description: Name of prefix-list to apply.
+ type: str
+ nexthop:
+ description: Batching based on nexthop.
+ type: bool
+ dynamic_med_interval:
+ description: Sets the interval for dampening of med changes.
+ type: int
+ enforce_first_as:
+ description: Enforce neighbor AS is the first AS in AS-PATH attribute (EBGP).
+ type: bool
+ enhanced_error:
+ description: Enable BGP Enhanced error handling.
+ type: bool
+ fabric_soo:
+ description: Fabric site of origin.
+ type: str
+ fast_external_fallover:
+ description: Immediately reset the session if the link to a directly connected BGP peer goes down.
+ type: bool
+ flush_routes:
+ description: Flush routes in RIB upon controlled restart.
+ type: bool
+ graceful_restart: &graceful_restart
+ description: Configure Graceful Restart functionality.
+ type: dict
+ suboptions:
+ set:
+ description: Enable graceful-restart.
+ type: bool
+ restart_time:
+ description: Maximum time for restart advertised to peers.
+ type: int
+ stalepath_time:
+ description: Maximum time to keep a restarting peer's stale routes.
+ type: int
+ helper:
+ description: Configure Graceful Restart Helper mode functionality.
+ type: bool
+ graceful_shutdown:
+ description: Graceful-shutdown for BGP protocol.
+ type: dict
+ suboptions:
+ activate:
+ description: Send graceful-shutdown community on all routes.
+ type: dict
+ suboptions:
+ set:
+ description: Activiate graceful-shutdown.
+ type: bool
+ route_map:
+ description: Apply route-map to modify attributes for outbound.
+ type: str
+ aware:
+ description: Lower preference of routes carrying graceful-shutdown community.
+ type: bool
+ isolate:
+ description: Isolate this router from BGP perspective.
+ type: dict
+ suboptions:
+ set:
+ description: Withdraw remote BGP routes to isolate this router.
+ type: bool
+ include_local:
+ description: Withdraw both local and remote BGP routes.
+ type: bool
+ log_neighbor_changes: &log_nbr
+ description: Log a message for neighbor up/down event.
+ type: bool
+ maxas_limit: &maxas_limit
+ description: Allow AS-PATH attribute from EBGP neighbor imposing a limit on number of ASes.
+ type: int
+ neighbors: &nbr
+ description: Configure BGP neighbors.
+ type: list
+ elements: dict
+ suboptions:
+ neighbor_address:
+ description: IP address/Prefix of the neighbor or interface.
+ type: str
+ required: True
+ neighbor_affinity_group:
+ description: Configure an affinity group.
+ type: dict
+ suboptions:
+ group_id:
+ description: Affinity Group ID.
+ type: int
+ bmp_activate_server:
+ description: Specify server ID for activating BMP monitoring for the peer.
+ type: int
+ capability:
+ description: Capability.
+ type: dict
+ suboptions:
+ suppress_4_byte_as:
+ description: Suppress 4-byte AS Capability.
+ type: bool
+ description:
+ description: Neighbor specific descripion.
+ type: str
+ disable_connected_check:
+ description: Disable check for directly connected peer.
+ type: bool
+ dont_capability_negotiate:
+ description: Don't negotiate capability with this neighbor.
+ type: bool
+ dscp:
+ description: Set dscp value for tcp transport.
+ type: str
+ dynamic_capability:
+ description: Dynamic Capability
+ type: bool
+ ebgp_multihop:
+ description: Specify multihop TTL for remote peer.
+ type: int
+ graceful_shutdown:
+ description: Graceful-shutdown for this neighbor.
+ type: dict
+ suboptions:
+ activate:
+ description: Send graceful-shutdown community.
+ type: dict
+ suboptions:
+ set:
+ description: Set activate.
+ type: bool
+ route_map:
+ description: Apply route-map to modify attributes for outbound.
+ type: str
+ inherit:
+ description: Inherit a template.
+ type: dict
+ suboptions:
+ peer:
+ description: Peer template to inherit.
+ type: str
+ peer_session:
+ description: Peer-session template to inherit.
+ type: str
+ local_as:
+ description: Specify the local-as number for the eBGP neighbor.
+ type: str
+ log_neighbor_changes:
+ description: Log message for neighbor up/down event.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set log-neighbor-changes.
+ type: bool
+ disable:
+ description:
+ - Disable logging of neighbor up/down event.
+ type: bool
+ low_memory:
+ description: Behaviour in low memory situations.
+ type: dict
+ suboptions:
+ exempt:
+ description: Do not shutdown this peer when under memory pressure.
+ type: bool
+ password:
+ description: Configure a password for neighbor.
+ type: dict
+ suboptions:
+ encryption:
+ description:
+ - 0 specifies an UNENCRYPTED neighbor password.
+ - 3 specifies an 3DES ENCRYPTED neighbor password will follow.
+ - 7 specifies a Cisco type 7 ENCRYPTED neighbor password will follow.
+ type: int
+ key:
+ description: Authentication password.
+ type: str
+ path_attribute:
+ description: BGP path attribute optional filtering.
+ type: list
+ elements: dict
+ suboptions:
+ action:
+ description: Action.
+ type: str
+ choices: ["discard", "treat-as-withdraw"]
+ type:
+ description: Path attribute type
+ type: int
+ range:
+ description: Path attribute range.
+ type: dict
+ suboptions:
+ start:
+ description: Path attribute range start value.
+ type: int
+ end:
+ description: Path attribute range end value.
+ type: int
+ peer_type:
+ description: Neighbor facing
+ type: str
+ choices: ["fabric-border-leaf", "fabric-external"]
+ remote_as:
+ description: Specify Autonomous System Number of the neighbor.
+ type: str
+ remove_private_as:
+ description: Remove private AS number from outbound updates.
+ type: dict
+ suboptions:
+ set:
+ description: Remove private AS.
+ type: bool
+ replace_as:
+ description: Replace.
+ type: bool
+ all:
+ description: All.
+ type: bool
+ shutdown:
+ description: Administratively shutdown this neighbor.
+ type: bool
+ timers:
+ description: Configure keepalive and hold timers.
+ type: dict
+ suboptions:
+ keepalive:
+ description: Keepalive interval (seconds).
+ type: int
+ holdtime:
+ description: Holdtime (seconds).
+ type: int
+ transport:
+ description: BGP transport connection.
+ type: dict
+ suboptions:
+ connection_mode:
+ description: Specify type of connection.
+ type: dict
+ suboptions:
+ passive:
+ description: Allow passive connection setup only.
+ type: bool
+ ttl_security:
+ description: Enable TTL Security Mechanism.
+ type: dict
+ suboptions:
+ hops:
+ description: Specify hop count for remote peer.
+ type: int
+ update_source:
+ description: Specify source of BGP session and updates.
+ type: str
+ neighbor_down: &nbr_down
+ description: Handle BGP neighbor down event, due to various reasons.
+ type: dict
+ suboptions:
+ fib_accelerate:
+ description: Accelerate the hardware updates for IP/IPv6 adjacencies for neighbor.
+ type: bool
+ nexthop:
+ description: Nexthop resolution options.
+ type: dict
+ suboptions:
+ suppress_default_resolution:
+ description: Prohibit use of default route for nexthop address resolution.
+ type: bool
+ rd:
+ description: Secondary Route Distinguisher for vxlan multisite border gateway.
+ type: dict
+ suboptions:
+ dual:
+ description: Generate Secondary RD for all VRFs and L2VNIs.
+ type: bool
+ id:
+ description: Specify 2 byte value for ID.
+ type: int
+ reconnect_interval: &reconn_intv
+ description: Configure connection reconnect interval.
+ type: int
+ router_id: &rtr_id
+ description: Specify the IP address to use as router-id.
+ type: str
+ shutdown: &shtdwn
+ description: Administratively shutdown BGP protocol.
+ type: bool
+ suppress_fib_pending: &suppr
+ description: Advertise only routes that are programmed in hardware to peers.
+ type: bool
+ timers: &timers
+ description: Configure bgp related timers.
+ type: dict
+ suboptions:
+ bestpath_limit:
+ description: Configure timeout for first bestpath after restart.
+ type: dict
+ suboptions:
+ timeout:
+ description: Bestpath timeout (seconds).
+ type: int
+ always:
+ description: Configure update-delay-always option.
+ type: bool
+ bgp:
+ description: Configure different bgp keepalive and holdtimes.
+ type: dict
+ suboptions:
+ keepalive:
+ description: Keepalive interval (seconds).
+ type: int
+ holdtime:
+ description: Holdtime (seconds).
+ type: int
+ prefix_peer_timeout:
+ description: Prefix Peer timeout (seconds).
+ type: int
+ prefix_peer_wait:
+ description: Configure wait timer for a prefix peer.
+ type: int
+ vrfs:
+ description: Virtual Router Context configurations.
+ type: list
+ elements: dict
+ suboptions:
+ vrf:
+ description: VRF name.
+ type: str
+ allocate_index:
+ description: Configure allocate-index.
+ type: int
+ bestpath: *bestpath
+ cluster_id: *cluster_id
+ confederation: *confederation
+ graceful_restart: *graceful_restart
+ local_as:
+ description: Specify the local-as for this vrf.
+ type: str
+ log_neighbor_changes: *log_nbr
+ maxas_limit: *maxas_limit
+ neighbors: *nbr
+ neighbor_down: *nbr_down
+ reconnect_interval: *reconn_intv
+ router_id: *rtr_id
+ timers: *timers
+ state:
+ description:
+ - The state the configuration should be left in.
+ - State I(purged) removes all the BGP configurations from the
+ target device. Use caution with this state.
+ - State I(deleted) only removes BGP attributes that this modules
+ manages and does not negate the BGP process completely. Thereby, preserving
+ address-family related configurations under BGP context.
+ - Running states I(deleted) and I(replaced) will result in an error if there
+ are address-family configuration lines present under a neighbor,
+ or a vrf context that is to be removed. Please use the
+ M(cisco.nxos.nxos_bgp_af) or M(cisco.nxos.nxos_bgp_neighbor_af)
+ modules for prior cleanup.
+ - Refer to examples for more details.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - deleted
+ - purged
+ - parsed
+ - gathered
+ - rendered
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# Nexus9000v#
+
+- name: Merge the provided configuration with the exisiting running configuration
+ cisco.nxos.nxos_bgp_global:
+ config:
+ as_number: 65563
+ router_id: 192.168.1.1
+ bestpath:
+ as_path:
+ multipath_relax: True
+ compare_neighborid: True
+ cost_community_ignore: True
+ confederation:
+ identifier: 42
+ peers:
+ - 65020
+ - 65030
+ - 65040
+ log_neighbor_changes: True
+ maxas_limit: 20
+ neighbors:
+ - neighbor_address: 192.168.1.100
+ neighbor_affinity_group:
+ group_id: 160
+ bmp_activate_server: 1
+ remote_as: 65563
+ description: NBR-1
+ low_memory:
+ exempt: True
+ - neighbor_address: 192.168.1.101
+ remote_as: 65563
+ password:
+ encryption: 7
+ key: 12090404011C03162E
+ neighbor_down:
+ fib_accelerate: True
+ vrfs:
+ - vrf: site-1
+ allocate_index: 5000
+ local_as: 200
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 198.51.100.1
+ description: site-1-nbr-1
+ password:
+ encryption: 3
+ key: 13D4D3549493D2877B1DC116EE27A6BE
+ remote_as: 65562
+ - neighbor_address: 198.51.100.2
+ remote_as: 65562
+ description: site-1-nbr-2
+ - vrf: site-2
+ local_as: 300
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 203.0.113.2
+ description: site-2-nbr-1
+ password:
+ encryption: 3
+ key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+ remote_as: 65568
+ neighbor_down:
+ fib_accelerate: True
+
+# Task output
+# -------------
+# before: {}
+#
+# commands:
+# - router bgp 65563
+# - bestpath as-path multipath-relax
+# - bestpath compare-neighborid
+# - bestpath cost-community ignore
+# - confederation identifier 42
+# - log-neighbor-changes
+# - maxas-limit 20
+# - neighbor-down fib-accelerate
+# - router-id 192.168.1.1
+# - confederation peers 65020 65030 65040
+# - neighbor 192.168.1.100
+# - remote-as 65563
+# - affinity-group 160
+# - bmp-activate-server 1
+# - description NBR-1
+# - low-memory exempt
+# - neighbor 192.168.1.101
+# - remote-as 65563
+# - password 7 12090404011C03162E
+# - vrf site-1
+# - allocate-index 5000
+# - local-as 200
+# - log-neighbor-changes
+# - neighbor 198.51.100.1
+# - remote-as 65562
+# - description site-1-nbr-1
+# - password 3 13D4D3549493D2877B1DC116EE27A6BE
+# - neighbor 198.51.100.2
+# - remote-as 65562
+# - description site-1-nbr-2
+# - vrf site-2
+# - local-as 300
+# - log-neighbor-changes
+# - neighbor-down fib-accelerate
+# - neighbor 203.0.113.2
+# - remote-as 65568
+# - description site-2-nbr-1
+# - password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+#
+# after:
+# as_number: '65563'
+# bestpath:
+# as_path:
+# multipath_relax: true
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65040'
+# log_neighbor_changes: true
+# maxas_limit: 20
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# - neighbor_address: 192.168.1.101
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - allocate_index: 5000
+# local_as: '200'
+# log_neighbor_changes: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 198.51.100.1
+# password:
+# encryption: 3
+# key: 13D4D3549493D2877B1DC116EE27A6BE
+# remote_as: '65562'
+# - description: site-1-nbr-2
+# neighbor_address: 198.51.100.2
+# remote_as: '65562'
+# vrf: site-1
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - description: site-2-nbr-1
+# neighbor_address: 203.0.113.2
+# password:
+# encryption: 3
+# key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+# remote_as: '65568'
+# vrf: site-2
+
+
+# After state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65040
+# bestpath as-path multipath-relax
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 20
+# log-neighbor-changes
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# neighbor 192.168.1.101
+# remote-as 65563
+# password 7 12090404011C03162E
+# vrf site-1
+# local-as 200
+# log-neighbor-changes
+# allocate-index 5000
+# neighbor 198.51.100.1
+# remote-as 65562
+# description site-1-nbr-1
+# password 3 13D4D3549493D2877B1DC116EE27A6BE
+# neighbor 198.51.100.2
+# remote-as 65562
+# description site-1-nbr-2
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# remote-as 65568
+# description site-2-nbr-1
+# password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+
+# Using replaced
+
+# Before state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65040
+# bestpath as-path multipath-relax
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 20
+# log-neighbor-changes
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# neighbor 192.168.1.101
+# remote-as 65563
+# password 7 12090404011C03162E
+# vrf site-1
+# local-as 200
+# log-neighbor-changes
+# allocate-index 5000
+# neighbor 198.51.100.1
+# remote-as 65562
+# description site-1-nbr-1
+# password 3 13D4D3549493D2877B1DC116EE27A6BE
+# neighbor 198.51.100.2
+# remote-as 65562
+# description site-1-nbr-2
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# remote-as 65568
+# description site-2-nbr-1
+# password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+
+- name: Replace BGP configuration with provided configuration
+ cisco.nxos.nxos_bgp_global:
+ config:
+ as_number: 65563
+ router_id: 192.168.1.1
+ bestpath:
+ compare_neighborid: True
+ cost_community_ignore: True
+ confederation:
+ identifier: 42
+ peers:
+ - 65020
+ - 65030
+ - 65050
+ maxas_limit: 40
+ neighbors:
+ - neighbor_address: 192.168.1.100
+ neighbor_affinity_group:
+ group_id: 160
+ bmp_activate_server: 1
+ remote_as: 65563
+ description: NBR-1
+ low_memory:
+ exempt: True
+ neighbor_down:
+ fib_accelerate: True
+ vrfs:
+ - vrf: site-2
+ local_as: 300
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 203.0.113.2
+ password:
+ encryption: 7
+ key: 12090404011C03162E
+ neighbor_down:
+ fib_accelerate: True
+ state: replaced
+
+# Task output
+# -------------
+# before:
+# as_number: '65563'
+# bestpath:
+# as_path:
+# multipath_relax: true
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65040'
+# log_neighbor_changes: true
+# maxas_limit: 20
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# - neighbor_address: 192.168.1.101
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - allocate_index: 5000
+# local_as: '200'
+# log_neighbor_changes: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 198.51.100.1
+# password:
+# encryption: 3
+# key: 13D4D3549493D2877B1DC116EE27A6BE
+# remote_as: '65562'
+# - description: site-1-nbr-2
+# neighbor_address: 198.51.100.2
+# remote_as: '65562'
+# vrf: site-1
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - description: site-2-nbr-1
+# neighbor_address: 203.0.113.2
+# password:
+# encryption: 3
+# key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+# remote_as: '65568'
+# vrf: site-2
+#
+# commands:
+# - router bgp 65563
+# - no bestpath as-path multipath-relax
+# - no log-neighbor-changes
+# - maxas-limit 40
+# - no confederation peers 65020 65030 65040
+# - confederation peers 65020 65030 65050
+# - no neighbor 192.168.1.101
+# - vrf site-2
+# - neighbor 203.0.113.2
+# - no remote-as 65568
+# - no description site-2-nbr-1
+# - password 7 12090404011C03162E
+# - no vrf site-1
+
+# after:
+# as_number: '65563'
+# bestpath:
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65050'
+# maxas_limit: 40
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - neighbor_address: 203.0.113.2
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# vrf: site-2
+#
+# After state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65050
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 40
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# password 7 12090404011C03162E
+
+# Using deleted
+
+# Before state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65040
+# bestpath as-path multipath-relax
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 20
+# log-neighbor-changes
+# address-family ipv4 unicast
+# default-metric 400
+# suppress-inactive
+# default-information originate
+# address-family ipv6 multicast
+# wait-igp-convergence
+# redistribute eigrp eigrp-1 route-map site-1-rmap
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# neighbor 192.168.1.101
+# remote-as 65563
+# password 7 12090404011C03162E
+# vrf site-1
+# local-as 200
+# log-neighbor-changes
+# allocate-index 5000
+# address-family ipv4 multicast
+# maximum-paths 40
+# dampen-igp-metric 1200
+# neighbor 198.51.100.1
+# remote-as 65562
+# description site-1-nbr-1
+# password 3 13D4D3549493D2877B1DC116EE27A6BE
+# neighbor 198.51.100.2
+# remote-as 65562
+# description site-1-nbr-2
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# remote-as 65568
+# description site-1-nbr-1
+# password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+
+- name: Delete BGP configurations handled by this module
+ cisco.nxos.nxos_bgp_global:
+ state: deleted
+
+# Task output
+# -------------
+
+# before:
+# as_number: '65563'
+# bestpath:
+# as_path:
+# multipath_relax: true
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65040'
+# log_neighbor_changes: true
+# maxas_limit: 20
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# - neighbor_address: 192.168.1.101
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - allocate_index: 5000
+# local_as: '200'
+# log_neighbor_changes: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 198.51.100.1
+# password:
+# encryption: 3
+# key: 13D4D3549493D2877B1DC116EE27A6BE
+# remote_as: '65562'
+# - description: site-1-nbr-2
+# neighbor_address: 198.51.100.2
+# remote_as: '65562'
+# vrf: site-1
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 203.0.113.2
+# password:
+# encryption: 3
+# key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+# remote_as: '65568'
+# vrf: site-2
+#
+# commands:
+# - router bgp 65563
+# - no bestpath as-path multipath-relax
+# - no bestpath compare-neighborid
+# - no bestpath cost-community ignore
+# - no confederation identifier 42
+# - no log-neighbor-changes
+# - no maxas-limit 20
+# - no neighbor-down fib-accelerate
+# - no router-id 192.168.1.1
+# - no confederation peers 65020 65030 65040
+# - no neighbor 192.168.1.100
+# - no neighbor 192.168.1.101
+# - no vrf site-1
+# - no vrf site-2
+#
+# after:
+# as_number: '65563'
+#
+# After state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# address-family ipv4 unicast
+# default-metric 400
+# suppress-inactive
+# default-information originate
+# address-family ipv6 multicast
+# wait-igp-convergence
+# redistribute eigrp eigrp-1 route-map site-1-rmap
+#
+
+# Using purged
+
+# Before state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65040
+# bestpath as-path multipath-relax
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 20
+# log-neighbor-changes
+# address-family ipv4 unicast
+# default-metric 400
+# suppress-inactive
+# default-information originate
+# address-family ipv6 multicast
+# wait-igp-convergence
+# redistribute eigrp eigrp-1 route-map site-1-rmap
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# neighbor 192.168.1.101
+# remote-as 65563
+# password 7 12090404011C03162E
+# vrf site-1
+# local-as 200
+# log-neighbor-changes
+# allocate-index 5000
+# address-family ipv4 multicast
+# maximum-paths 40
+# dampen-igp-metric 1200
+# neighbor 198.51.100.1
+# remote-as 65562
+# description site-1-nbr-1
+# password 3 13D4D3549493D2877B1DC116EE27A6BE
+# neighbor 198.51.100.2
+# remote-as 65562
+# description site-1-nbr-2
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# remote-as 65568
+# description site-1-nbr-1
+# password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+
+- name: Purge all BGP configurations from the device
+ cisco.nxos.nxos_bgp_global:
+ state: purged
+
+# Task output
+# -------------
+
+# before:
+# as_number: '65563'
+# bestpath:
+# as_path:
+# multipath_relax: true
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65040'
+# log_neighbor_changes: true
+# maxas_limit: 20
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# - neighbor_address: 192.168.1.101
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - allocate_index: 5000
+# local_as: '200'
+# log_neighbor_changes: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 198.51.100.1
+# password:
+# encryption: 3
+# key: 13D4D3549493D2877B1DC116EE27A6BE
+# remote_as: '65562'
+# - description: site-1-nbr-2
+# neighbor_address: 198.51.100.2
+# remote_as: '65562'
+# vrf: site-1
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 203.0.113.2
+# password:
+# encryption: 3
+# key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+# remote_as: '65568'
+# vrf: site-2
+#
+# commands:
+# - no router bgp 65563
+#
+# after: {}
+#
+# After state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# Nexus9000v#
+
+# Using rendered
+
+- name: Render platform specific configuration lines (without connecting to the device)
+ cisco.nxos.nxos_bgp_global:
+ config:
+ as_number: 65563
+ router_id: 192.168.1.1
+ bestpath:
+ as_path:
+ multipath_relax: True
+ compare_neighborid: True
+ cost_community_ignore: True
+ confederation:
+ identifier: 42
+ peers:
+ - 65020
+ - 65030
+ - 65040
+ log_neighbor_changes: True
+ maxas_limit: 20
+ neighbors:
+ - neighbor_address: 192.168.1.100
+ neighbor_affinity_group:
+ group_id: 160
+ bmp_activate_server: 1
+ remote_as: 65563
+ description: NBR-1
+ low_memory:
+ exempt: True
+ - neighbor_address: 192.168.1.101
+ remote_as: 65563
+ password:
+ encryption: 7
+ key: 12090404011C03162E
+ neighbor_down:
+ fib_accelerate: True
+ vrfs:
+ - vrf: site-1
+ allocate_index: 5000
+ local_as: 200
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 198.51.100.1
+ description: site-1-nbr-1
+ password:
+ encryption: 3
+ key: 13D4D3549493D2877B1DC116EE27A6BE
+ remote_as: 65562
+ - neighbor_address: 198.51.100.2
+ remote_as: 65562
+ description: site-1-nbr-2
+ - vrf: site-2
+ local_as: 300
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 203.0.113.2
+ description: site-1-nbr-1
+ password:
+ encryption: 3
+ key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+ remote_as: 65568
+ neighbor_down:
+ fib_accelerate: True
+
+# Task Output (redacted)
+# -----------------------
+# rendered:
+# - router bgp 65563
+# - bestpath as-path multipath-relax
+# - bestpath compare-neighborid
+# - bestpath cost-community ignore
+# - confederation identifier 42
+# - log-neighbor-changes
+# - maxas-limit 20
+# - neighbor-down fib-accelerate
+# - router-id 192.168.1.1
+# - confederation peers 65020 65030 65040
+# - neighbor 192.168.1.100
+# - remote-as 65563
+# - affinity-group 160
+# - bmp-activate-server 1
+# - description NBR-1
+# - low-memory exempt
+# - neighbor 192.168.1.101
+# - remote-as 65563
+# - password 7 12090404011C03162E
+# - vrf site-1
+# - allocate-index 5000
+# - local-as 200
+# - log-neighbor-changes
+# - neighbor 198.51.100.1
+# - remote-as 65562
+# - description site-1-nbr-1
+# - password 3 13D4D3549493D2877B1DC116EE27A6BE
+# - neighbor 198.51.100.2
+# - remote-as 65562
+# - description site-1-nbr-2
+# - vrf site-2
+# - local-as 300
+# - log-neighbor-changes
+# - neighbor-down fib-accelerate
+# - neighbor 203.0.113.2
+# - remote-as 65568
+# - description site-1-nbr-1
+# - password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65040
+# bestpath as-path multipath-relax
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 20
+# log-neighbor-changes
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# neighbor 192.168.1.101
+# remote-as 65563
+# password 7 12090404011C03162E
+# vrf site-1
+# local-as 200
+# log-neighbor-changes
+# allocate-index 5000
+# neighbor 198.51.100.1
+# remote-as 65562
+# description site-1-nbr-1
+# password 3 13D4D3549493D2877B1DC116EE27A6BE
+# neighbor 198.51.100.2
+# remote-as 65562
+# description site-1-nbr-2
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# remote-as 65568
+# description site-1-nbr-1
+# password 3 AF92F4C16A0A0EC5BDF56CF58BC030F6
+
+- name: Parse externally provided BGP config
+ cisco.nxos.nxos_bgp_global:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# parsed:
+# as_number: '65563'
+# bestpath:
+# as_path:
+# multipath_relax: true
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65040'
+# log_neighbor_changes: true
+# maxas_limit: 20
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# - neighbor_address: 192.168.1.101
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - allocate_index: 5000
+# local_as: '200'
+# log_neighbor_changes: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 198.51.100.1
+# password:
+# encryption: 3
+# key: 13D4D3549493D2877B1DC116EE27A6BE
+# remote_as: '65562'
+# - description: site-1-nbr-2
+# neighbor_address: 198.51.100.2
+# remote_as: '65562'
+# vrf: site-1
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - description: site-1-nbr-1
+# neighbor_address: 203.0.113.2
+# password:
+# encryption: 3
+# key: AF92F4C16A0A0EC5BDF56CF58BC030F6
+# remote_as: '65568'
+# vrf: site-2
+
+# Using gathered
+
+# existing config
+#
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65563
+# router-id 192.168.1.1
+# confederation identifier 42
+# confederation peers 65020 65030 65050
+# bestpath cost-community ignore
+# bestpath compare-neighborid
+# neighbor-down fib-accelerate
+# maxas-limit 40
+# neighbor 192.168.1.100
+# low-memory exempt
+# bmp-activate-server 1
+# remote-as 65563
+# description NBR-1
+# affinity-group 160
+# vrf site-1
+# vrf site-2
+# local-as 300
+# neighbor-down fib-accelerate
+# log-neighbor-changes
+# neighbor 203.0.113.2
+# password 7 12090404011C03162E
+
+- name: Gather BGP facts using gathered
+ cisco.nxos.nxos_bgp_global:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# as_number: '65563'
+# bestpath:
+# compare_neighborid: true
+# cost_community_ignore: true
+# confederation:
+# identifier: '42'
+# peers:
+# - '65020'
+# - '65030'
+# - '65050'
+# maxas_limit: 40
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - bmp_activate_server: 1
+# description: NBR-1
+# low_memory:
+# exempt: true
+# neighbor_address: 192.168.1.100
+# neighbor_affinity_group:
+# group_id: 160
+# remote_as: '65563'
+# router_id: 192.168.1.1
+# vrfs:
+# - vrf: site-1
+# - local_as: '300'
+# log_neighbor_changes: true
+# neighbor_down:
+# fib_accelerate: true
+# neighbors:
+# - neighbor_address: 203.0.113.2
+# password:
+# encryption: 7
+# key: 12090404011C03162E
+# vrf: site-2
+
+# Remove a neighbor having AF configurations with state replaced (will fail)
+
+# Before state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65536
+# log-neighbor-changes
+# maxas-limit 20
+# router-id 198.51.100.2
+# neighbor 203.0.113.2
+# address-family ipv4 unicast
+# next-hop-self
+# remote-as 65538
+# affinity-group 160
+# description NBR-1
+# low-memory exempt
+# neighbor 192.0.2.1
+# remote-as 65537
+# password 7 12090404011C03162E
+
+- name: Remove a neighbor having AF configurations (should fail)
+ cisco.nxos.nxos_bgp_global:
+ config:
+ as_number: 65536
+ router_id: 198.51.100.2
+ maxas_limit: 20
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 192.0.2.1
+ remote_as: 65537
+ password:
+ encryption: 7
+ key: 12090404011C03162E
+ state: replaced
+
+# Task output (redacted)
+# -----------------------
+# fatal: [Nexus9000v]: FAILED! => changed=false
+# msg: Neighbor 203.0.113.2 has address-family configurations.
+# Please use the nxos_bgp_neighbor_af module to remove those first.
+
+# Remove a VRF having AF configurations with state replaced (will fail)
+
+# Before state:
+# -------------
+# Nexus9000v# show running-config | section "^router bgp"
+# router bgp 65536
+# log-neighbor-changes
+# maxas-limit 20
+# router-id 198.51.100.2
+# neighbor 192.0.2.1
+# remote-as 65537
+# password 7 12090404011C03162E
+# vrf site-1
+# address-family ipv4 unicast
+# default-information originate
+# neighbor 203.0.113.2
+# remote-as 65538
+# affinity-group 160
+# description NBR-1
+# low-memory exempt
+# vrf site-2
+# neighbor-down fib-accelerate
+
+- name: Remove a VRF having AF configurations (should fail)
+ cisco.nxos.nxos_bgp_global:
+ config:
+ as_number: 65536
+ router_id: 198.51.100.2
+ maxas_limit: 20
+ log_neighbor_changes: True
+ neighbors:
+ - neighbor_address: 192.0.2.1
+ remote_as: 65537
+ password:
+ encryption: 7
+ key: 12090404011C03162E
+ vrfs:
+ - vrf: site-2
+ neighbor_down:
+ fib_accelerate: True
+ state: replaced
+
+# Task output (redacted)
+# -----------------------
+# fatal: [Nexus9000v]: FAILED! => changed=false
+# msg: VRF site-1 has address-family configurations.
+# Please use the nxos_bgp_af module to remove those first.
+"""
+
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - router bgp 65563
+ - maxas-limit 20
+ - router-id 192.168.1.1
+ - confederation peers 65020 65030 65040
+ - neighbor 192.168.1.100
+ - remote-as 65563
+ - affinity-group 160
+ - bmp-activate-server 1
+ - description NBR-1
+ - low-memory exempt
+ - vrf site-1
+ - log-neighbor-changes
+ - neighbor 198.51.100.1
+ - remote-as 65562
+ - description site-1-nbr-1
+ - password 3 13D4D3549493D2877B1DC116EE27A6BE
+"""
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.bgp_global.bgp_global import (
+ Bgp_globalArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.bgp_global.bgp_global import (
+ Bgp_global,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=Bgp_globalArgs.argument_spec,
+ mutually_exclusive=[["config", "running_config"]],
+ required_if=[
+ ["state", "merged", ["config"]],
+ ["state", "replaced", ["config"]],
+ ["state", "rendered", ["config"]],
+ ["state", "parsed", ["running_config"]],
+ ],
+ supports_check_mode=True,
+ )
+
+ result = Bgp_global(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor.py
new file mode 100644
index 00000000..0b22c916
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor.py
@@ -0,0 +1,588 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_bgp_neighbor
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2023-01-27) Manages BGP neighbors configurations.
+description:
+- Manages BGP neighbors configurations on NX-OS switches.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+deprecated:
+ alternative: nxos_bgp_global
+ why: Updated module released with more functionality.
+ removed_at_date: '2023-01-27'
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the whole BGP neighbor configuration.
+- Default, where supported, restores params default value.
+options:
+ asn:
+ description:
+ - BGP autonomous system number. Valid values are string, Integer in ASPLAIN or
+ ASDOT notation.
+ required: true
+ type: str
+ vrf:
+ description:
+ - Name of the VRF. The name 'default' is a valid VRF representing the global bgp.
+ default: default
+ type: str
+ neighbor:
+ description:
+ - Neighbor Identifier. Valid values are string. Neighbors may use IPv4 or IPv6
+ notation, with or without prefix length.
+ required: true
+ type: str
+ description:
+ description:
+ - Description of the neighbor.
+ type: str
+ bfd:
+ description:
+ - Enables/Disables BFD for a given neighbor.
+ - "Dependency: ''feature bfd''"
+ type: str
+ choices:
+ - enable
+ - disable
+ connected_check:
+ description:
+ - Configure whether or not to check for directly connected peer.
+ type: bool
+ capability_negotiation:
+ description:
+ - Configure whether or not to negotiate capability with this neighbor.
+ type: bool
+ dynamic_capability:
+ description:
+ - Configure whether or not to enable dynamic capability.
+ type: bool
+ ebgp_multihop:
+ description:
+ - Specify multihop TTL for a remote peer. Valid values are integers between 2
+ and 255, or keyword 'default' to disable this property.
+ type: str
+ local_as:
+ description:
+ - Specify the local-as number for the eBGP neighbor. Valid values are String or
+ Integer in ASPLAIN or ASDOT notation, or 'default', which means not to configure
+ it.
+ type: str
+ log_neighbor_changes:
+ description:
+ - Specify whether or not to enable log messages for neighbor up/down event.
+ choices:
+ - enable
+ - disable
+ - inherit
+ type: str
+ low_memory_exempt:
+ description:
+ - Specify whether or not to shut down this neighbor under memory pressure.
+ type: bool
+ maximum_peers:
+ description:
+ - Specify Maximum number of peers for this neighbor prefix Valid values are between
+ 1 and 1000, or 'default', which does not impose the limit. Note that this parameter
+ is accepted only on neighbors with address/prefix.
+ type: str
+ pwd:
+ description:
+ - Specify the password for neighbor. Valid value is string.
+ type: str
+ pwd_type:
+ description:
+ - Specify the encryption type the password will use. Valid values are '3des' or
+ 'cisco_type_7' encryption or keyword 'default'.
+ choices:
+ - 3des
+ - cisco_type_7
+ - default
+ type: str
+ remote_as:
+ description:
+ - Specify Autonomous System Number of the neighbor. Valid values are String or
+ Integer in ASPLAIN or ASDOT notation, or 'default', which means not to configure
+ it.
+ type: str
+ remove_private_as:
+ description:
+ - Specify the config to remove private AS number from outbound updates. Valid
+ values are 'enable' to enable this config, 'disable' to disable this config,
+ 'all' to remove all private AS number, or 'replace-as', to replace the private
+ AS number.
+ choices:
+ - enable
+ - disable
+ - all
+ - replace-as
+ type: str
+ shutdown:
+ description:
+ - Configure to administratively shutdown this neighbor.
+ type: bool
+ suppress_4_byte_as:
+ description:
+ - Configure to suppress 4-byte AS Capability.
+ type: bool
+ timers_keepalive:
+ description:
+ - Specify keepalive timer value. Valid values are integers between 0 and 3600
+ in terms of seconds, or 'default', which is 60.
+ type: str
+ timers_holdtime:
+ description:
+ - Specify holdtime timer value. Valid values are integers between 0 and 3600 in
+ terms of seconds, or 'default', which is 180.
+ type: str
+ transport_passive_only:
+ description:
+ - Specify whether or not to only allow passive connection setup. Valid values
+ are 'true', 'false', and 'default', which defaults to 'false'. This property
+ can only be configured when the neighbor is in 'ip' address format without prefix
+ length.
+ type: bool
+ update_source:
+ description:
+ - Specify source interface of BGP session and updates.
+ type: str
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ peer_type:
+ description:
+ - Specify the peer type for BGP session.
+ choices:
+ - fabric_border_leaf
+ - fabric_external
+ - disable
+ type: str
+ version_added: 1.1.0
+"""
+EXAMPLES = """
+# create a new neighbor
+- cisco.nxos.nxos_bgp_neighbor:
+ asn: 65535
+ neighbor: 192.0.2.3
+ local_as: 20
+ remote_as: 30
+ bfd: enable
+ description: just a description
+ update_source: Ethernet1/3
+ state: present
+ peer_type: fabric_external
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["router bgp 65535", "neighbor 192.0.2.3",
+ "remote-as 30", "update-source Ethernet1/3",
+ "description just a description", "local-as 20", "peer-type fabric-external"]
+"""
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+BOOL_PARAMS = [
+ "capability_negotiation",
+ "shutdown",
+ "connected_check",
+ "dynamic_capability",
+ "low_memory_exempt",
+ "suppress_4_byte_as",
+ "transport_passive_only",
+]
+PARAM_TO_COMMAND_KEYMAP = {
+ "asn": "router bgp",
+ "bfd": "bfd",
+ "capability_negotiation": "dont-capability-negotiate",
+ "connected_check": "disable-connected-check",
+ "description": "description",
+ "dynamic_capability": "dynamic-capability",
+ "ebgp_multihop": "ebgp-multihop",
+ "local_as": "local-as",
+ "log_neighbor_changes": "log-neighbor-changes",
+ "low_memory_exempt": "low-memory exempt",
+ "maximum_peers": "maximum-peers",
+ "neighbor": "neighbor",
+ "pwd": "password",
+ "pwd_type": "password",
+ "remote_as": "remote-as",
+ "remove_private_as": "remove-private-as",
+ "shutdown": "shutdown",
+ "suppress_4_byte_as": "capability suppress 4-byte-as",
+ "timers_keepalive": "timers",
+ "timers_holdtime": "timers",
+ "transport_passive_only": "transport connection-mode passive",
+ "update_source": "update-source",
+ "vrf": "vrf",
+ "peer_type": "peer-type",
+}
+PARAM_TO_DEFAULT_KEYMAP = {
+ "bfd": "disable",
+ "shutdown": False,
+ "dynamic_capability": True,
+ "timers_keepalive": 60,
+ "timers_holdtime": 180,
+ "peer_type": "disable",
+}
+
+
+def get_value(arg, config):
+ command = PARAM_TO_COMMAND_KEYMAP[arg]
+ has_command = re.search(r"^\s+{0}$".format(command), config, re.M)
+ has_command_val = re.search(
+ r"(?:\s+{0}\s*)(?P<value>.*)$".format(command), config, re.M
+ )
+
+ if arg == "dynamic_capability":
+ has_no_command = re.search(
+ r"\s+no\s{0}\s*$".format(command), config, re.M
+ )
+ value = True
+ if has_no_command:
+ value = False
+ elif arg in BOOL_PARAMS:
+ value = False
+ if has_command:
+ value = True
+ elif arg == "log_neighbor_changes":
+ value = ""
+ if has_command:
+ value = "enable"
+ elif has_command_val:
+ value = "disable"
+
+ elif arg == "remove_private_as":
+ value = "disable"
+ if has_command:
+ value = "enable"
+ elif has_command_val:
+ value = has_command_val.group("value")
+ elif arg == "bfd":
+ value = "enable" if has_command else "disable"
+
+ elif arg == "peer_type":
+ value = "disable"
+ if has_command_val:
+ value = has_command_val.group("value").replace("-", "_")
+ else:
+ value = ""
+
+ if has_command_val:
+ value = has_command_val.group("value")
+
+ if command in ["timers", "password"]:
+ split_value = value.split()
+ value = ""
+
+ if arg in ["timers_keepalive", "pwd_type"]:
+ value = split_value[0]
+ elif (
+ arg in ["timers_holdtime", "pwd"] and len(split_value) == 2
+ ):
+ value = split_value[1]
+
+ return value
+
+
+def get_existing(module, args, warnings):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+
+ asn_regex = re.compile(
+ r".*router\sbgp\s(?P<existing_asn>\d+(\.\d+)?).*", re.S
+ )
+ match_asn = asn_regex.match(str(netcfg))
+
+ if match_asn:
+ existing_asn = match_asn.group("existing_asn")
+ parents = ["router bgp {0}".format(existing_asn)]
+
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ parents.append("neighbor {0}".format(module.params["neighbor"]))
+ config = netcfg.get_section(parents)
+ if config:
+ for arg in args:
+ if arg not in ["asn", "vrf", "neighbor"]:
+ existing[arg] = get_value(arg, config)
+
+ existing["asn"] = existing_asn
+ existing["neighbor"] = module.params["neighbor"]
+ existing["vrf"] = module.params["vrf"]
+ else:
+ warnings.append(
+ "The BGP process didn't exist but the task" " just created it."
+ )
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key in table:
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = table.get(key)
+
+ return new_dict
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, value in proposed_commands.items():
+ if value is True:
+ commands.append(key)
+ elif value is False:
+ commands.append("no {0}".format(key))
+ elif value == "default":
+ if existing_commands.get(key):
+ if key == "password":
+ commands.append("no password")
+ else:
+ existing_value = existing_commands.get(key)
+ commands.append("no {0} {1}".format(key, existing_value))
+ else:
+ if key == "log-neighbor-changes":
+ if value == "enable":
+ commands.append("{0}".format(key))
+ elif value == "disable":
+ commands.append("{0} {1}".format(key, value))
+ elif value == "inherit":
+ if existing_commands.get(key):
+ commands.append("no {0}".format(key))
+ elif key == "password":
+ pwd_type = module.params["pwd_type"]
+ if pwd_type == "3des":
+ pwd_type = 3
+ else:
+ pwd_type = 7
+ command = "{0} {1} {2}".format(key, pwd_type, value)
+ if command not in commands:
+ commands.append(command)
+ elif key == "remove-private-as":
+ if value == "enable":
+ command = "{0}".format(key)
+ commands.append(command)
+ elif value == "disable":
+ if existing_commands.get(key) != "disable":
+ command = "no {0}".format(key)
+ commands.append(command)
+ else:
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+ elif key == "timers":
+ if proposed["timers_keepalive"] != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timers_keepalive"
+ ) or proposed[
+ "timers_holdtime"
+ ] != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timers_holdtime"
+ ):
+ command = "timers {0} {1}".format(
+ proposed["timers_keepalive"],
+ proposed["timers_holdtime"],
+ )
+ if command not in commands:
+ commands.append(command)
+ elif key == "bfd":
+ no_cmd = "no " if value == "disable" else ""
+ commands.append(no_cmd + key)
+ elif key == "peer-type":
+ if value == "disable":
+ if existing_commands.get(key) != "disable":
+ command = "no {0}".format(key)
+ commands.append(command)
+ elif value == "fabric_external":
+ ptype = "fabric-external"
+ command = "{0} {1}".format(key, ptype)
+ commands.append(command)
+ elif value == "fabric_border_leaf":
+ ptype = "fabric-border-leaf"
+ command = "{0} {1}".format(key, ptype)
+ commands.append(command)
+ else:
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+
+ if commands:
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ parents.append("neighbor {0}".format(module.params["neighbor"]))
+
+ # make sure that local-as is the last command in the list.
+ local_as_command = "local-as {0}".format(module.params["local_as"])
+ if local_as_command in commands:
+ commands.remove(local_as_command)
+ commands.append(local_as_command)
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, existing, proposed, candidate):
+ commands = []
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ commands.append("no neighbor {0}".format(module.params["neighbor"]))
+ candidate.add(commands, parents=parents)
+
+
+def main():
+ argument_spec = dict(
+ asn=dict(required=True, type="str"),
+ vrf=dict(required=False, type="str", default="default"),
+ neighbor=dict(required=True, type="str"),
+ description=dict(required=False, type="str"),
+ bfd=dict(required=False, type="str", choices=["enable", "disable"]),
+ capability_negotiation=dict(required=False, type="bool"),
+ connected_check=dict(required=False, type="bool"),
+ dynamic_capability=dict(required=False, type="bool"),
+ ebgp_multihop=dict(required=False, type="str"),
+ local_as=dict(required=False, type="str"),
+ log_neighbor_changes=dict(
+ required=False,
+ type="str",
+ choices=["enable", "disable", "inherit"],
+ ),
+ low_memory_exempt=dict(required=False, type="bool"),
+ maximum_peers=dict(required=False, type="str"),
+ pwd=dict(required=False, type="str"),
+ pwd_type=dict(
+ required=False,
+ type="str",
+ choices=["3des", "cisco_type_7", "default"],
+ ),
+ remote_as=dict(required=False, type="str"),
+ remove_private_as=dict(
+ required=False,
+ type="str",
+ choices=["enable", "disable", "all", "replace-as"],
+ ),
+ shutdown=dict(required=False, type="bool"),
+ suppress_4_byte_as=dict(required=False, type="bool"),
+ timers_keepalive=dict(required=False, type="str"),
+ timers_holdtime=dict(required=False, type="str"),
+ transport_passive_only=dict(required=False, type="bool"),
+ update_source=dict(required=False, type="str"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ peer_type=dict(
+ required=False,
+ type="str",
+ choices=["disable", "fabric_border_leaf", "fabric_external"],
+ ),
+ )
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_together=[
+ ["timers_holdtime", "timers_keepalive"],
+ ["pwd", "pwd_type"],
+ ],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+
+ if module.params["pwd_type"] == "default":
+ module.params["pwd_type"] = "0"
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing = get_existing(module, args, warnings)
+
+ if existing.get("asn") and state == "present":
+ if existing["asn"] != module.params["asn"]:
+ module.fail_json(
+ msg="Another BGP ASN already exists.",
+ proposed_asn=module.params["asn"],
+ existing_asn=existing.get("asn"),
+ )
+
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key not in ["asn", "vrf", "neighbor", "pwd_type"]:
+ if str(value).lower() == "default":
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key, "default")
+ if key == "bfd":
+ if existing.get("bfd", "disable") != value:
+ proposed[key] = value
+ elif existing.get(key) != value:
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ elif state == "absent" and existing:
+ state_absent(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ if not module.check_mode:
+ load_config(module, candidate)
+ result["changed"] = True
+ result["commands"] = candidate
+ else:
+ result["commands"] = []
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor_af.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor_af.py
new file mode 100644
index 00000000..b5e633b5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_bgp_neighbor_af.py
@@ -0,0 +1,815 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_bgp_neighbor_af
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages BGP address-family's neighbors configuration.
+description:
+- Manages BGP address-family's neighbors configurations on NX-OS switches.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the whole BGP address-family's neighbor configuration.
+- Default, when supported, removes properties
+- In order to default maximum-prefix configuration, only C(max_prefix_limit=default)
+ is needed.
+options:
+ asn:
+ description:
+ - BGP autonomous system number. Valid values are String, Integer in ASPLAIN or
+ ASDOT notation.
+ required: true
+ type: str
+ vrf:
+ description:
+ - Name of the VRF. The name 'default' is a valid VRF representing the global bgp.
+ default: default
+ type: str
+ neighbor:
+ description:
+ - Neighbor Identifier. Valid values are string. Neighbors may use IPv4 or IPv6
+ notation, with or without prefix length.
+ required: true
+ type: str
+ afi:
+ description:
+ - Address Family Identifier.
+ required: true
+ choices:
+ - ipv4
+ - ipv6
+ - vpnv4
+ - vpnv6
+ - l2vpn
+ type: str
+ safi:
+ description:
+ - Sub Address Family Identifier.
+ required: true
+ choices:
+ - unicast
+ - multicast
+ - evpn
+ type: str
+ additional_paths_receive:
+ description:
+ - Valid values are enable for basic command enablement; disable for disabling
+ the command at the neighbor af level (it adds the disable keyword to the basic
+ command); and inherit to remove the command at this level (the command value
+ is inherited from a higher BGP layer).
+ choices:
+ - enable
+ - disable
+ - inherit
+ type: str
+ additional_paths_send:
+ description:
+ - Valid values are enable for basic command enablement; disable for disabling
+ the command at the neighbor af level (it adds the disable keyword to the basic
+ command); and inherit to remove the command at this level (the command value
+ is inherited from a higher BGP layer).
+ choices:
+ - enable
+ - disable
+ - inherit
+ type: str
+ advertise_map_exist:
+ description:
+ - Conditional route advertisement. This property requires two route maps, an advertise-map
+ and an exist-map. Valid values are an array specifying both the advertise-map
+ name and the exist-map name, or simply 'default' e.g. ['my_advertise_map', 'my_exist_map'].
+ This command is mutually exclusive with the advertise_map_non_exist property.
+ type: list
+ elements: str
+ advertise_map_non_exist:
+ description:
+ - Conditional route advertisement. This property requires two route maps, an advertise-map
+ and an exist-map. Valid values are an array specifying both the advertise-map
+ name and the non-exist-map name, or simply 'default' e.g. ['my_advertise_map',
+ 'my_non_exist_map']. This command is mutually exclusive with the advertise_map_exist
+ property.
+ type: list
+ elements: str
+ allowas_in:
+ description:
+ - Activate allowas-in property
+ type: bool
+ allowas_in_max:
+ description:
+ - Max-occurrences value for allowas_in. Valid values are an integer value or 'default'.
+ This is mutually exclusive with allowas_in.
+ type: str
+ as_override:
+ description:
+ - Activate the as-override feature.
+ type: bool
+ default_originate:
+ description:
+ - Activate the default-originate feature.
+ type: bool
+ default_originate_route_map:
+ description:
+ - Route-map for the default_originate property. Valid values are a string defining
+ a route-map name, or 'default'. This is mutually exclusive with default_originate.
+ type: str
+ disable_peer_as_check:
+ description:
+ - Disable checking of peer AS-number while advertising
+ type: bool
+ filter_list_in:
+ description:
+ - Valid values are a string defining a filter-list name, or 'default'.
+ type: str
+ filter_list_out:
+ description:
+ - Valid values are a string defining a filter-list name, or 'default'.
+ type: str
+ max_prefix_limit:
+ description:
+ - maximum-prefix limit value. Valid values are an integer value or 'default'.
+ type: str
+ max_prefix_interval:
+ description:
+ - Optional restart interval. Valid values are an integer. Requires max_prefix_limit.
+ May not be combined with max_prefix_warning.
+ type: str
+ max_prefix_threshold:
+ description:
+ - Optional threshold percentage at which to generate a warning. Valid values are
+ an integer value. Requires max_prefix_limit.
+ type: str
+ max_prefix_warning:
+ description:
+ - Optional warning-only keyword. Requires max_prefix_limit. May not be combined
+ with max_prefix_interval.
+ type: bool
+ next_hop_self:
+ description:
+ - Activate the next-hop-self feature.
+ type: bool
+ next_hop_third_party:
+ description:
+ - Activate the next-hop-third-party feature.
+ type: bool
+ prefix_list_in:
+ description:
+ - Valid values are a string defining a prefix-list name, or 'default'.
+ type: str
+ prefix_list_out:
+ description:
+ - Valid values are a string defining a prefix-list name, or 'default'.
+ type: str
+ route_map_in:
+ description:
+ - Valid values are a string defining a route-map name, or 'default'.
+ type: str
+ route_map_out:
+ description:
+ - Valid values are a string defining a route-map name, or 'default'.
+ type: str
+ route_reflector_client:
+ description:
+ - Router reflector client.
+ type: bool
+ send_community:
+ description:
+ - send-community attribute.
+ choices:
+ - none
+ - both
+ - extended
+ - standard
+ - default
+ type: str
+ soft_reconfiguration_in:
+ description:
+ - Valid values are 'enable' for basic command enablement; 'always' to add the
+ always keyword to the basic command; and 'inherit' to remove the command at
+ this level (the command value is inherited from a higher BGP layer).
+ choices:
+ - enable
+ - always
+ - inherit
+ type: str
+ soo:
+ description:
+ - Site-of-origin. Valid values are a string defining a VPN extcommunity or 'default'.
+ type: str
+ suppress_inactive:
+ description:
+ - suppress-inactive feature.
+ type: bool
+ unsuppress_map:
+ description:
+ - unsuppress-map. Valid values are a string defining a route-map name or 'default'.
+ type: str
+ weight:
+ description:
+ - Weight value. Valid values are an integer value or 'default'.
+ type: str
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ rewrite_evpn_rt_asn:
+ description:
+ - Auto generate route targets for EBGP neighbor.
+ type: bool
+ version_added: 1.1.0
+"""
+EXAMPLES = """
+- name: configure RR client
+ cisco.nxos.nxos_bgp_neighbor_af:
+ asn: 65535
+ neighbor: 192.0.2.3
+ afi: ipv4
+ safi: unicast
+ route_reflector_client: true
+ state: present
+ rewrite_evpn_rt_asn: true
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["router bgp 65535", "neighbor 192.0.2.3",
+ "address-family ipv4 unicast", "route-reflector-client",
+ "rewrite-evpn-rt-asn"]
+"""
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+BOOL_PARAMS = [
+ "allowas_in",
+ "as_override",
+ "default_originate",
+ "disable_peer_as_check",
+ "next_hop_self",
+ "next_hop_third_party",
+ "route_reflector_client",
+ "suppress_inactive",
+ "rewrite_evpn_rt_asn",
+]
+PARAM_TO_COMMAND_KEYMAP = {
+ "afi": "address-family",
+ "asn": "router bgp",
+ "neighbor": "neighbor",
+ "additional_paths_receive": "capability additional-paths receive",
+ "additional_paths_send": "capability additional-paths send",
+ "advertise_map_exist": "advertise-map exist-map",
+ "advertise_map_non_exist": "advertise-map non-exist-map",
+ "allowas_in": "allowas-in",
+ "allowas_in_max": "allowas-in",
+ "as_override": "as-override",
+ "default_originate": "default-originate",
+ "default_originate_route_map": "default-originate route-map",
+ "disable_peer_as_check": "disable-peer-as-check",
+ "filter_list_in": "filter-list in",
+ "filter_list_out": "filter-list out",
+ "max_prefix_limit": "maximum-prefix",
+ "max_prefix_interval": "maximum-prefix interval",
+ "max_prefix_threshold": "maximum-prefix threshold",
+ "max_prefix_warning": "maximum-prefix warning",
+ "next_hop_self": "next-hop-self",
+ "next_hop_third_party": "next-hop-third-party",
+ "prefix_list_in": "prefix-list in",
+ "prefix_list_out": "prefix-list out",
+ "route_map_in": "route-map in",
+ "route_map_out": "route-map out",
+ "route_reflector_client": "route-reflector-client",
+ "safi": "address-family",
+ "send_community": "send-community",
+ "soft_reconfiguration_in": "soft-reconfiguration inbound",
+ "soo": "soo",
+ "suppress_inactive": "suppress-inactive",
+ "unsuppress_map": "unsuppress-map",
+ "weight": "weight",
+ "vrf": "vrf",
+ "rewrite_evpn_rt_asn": "rewrite-evpn-rt-asn",
+}
+
+
+def get_value(arg, config, module):
+ custom = [
+ "additional_paths_send",
+ "additional_paths_receive",
+ "max_prefix_limit",
+ "max_prefix_interval",
+ "max_prefix_threshold",
+ "max_prefix_warning",
+ "send_community",
+ "soft_reconfiguration_in",
+ ]
+ command = PARAM_TO_COMMAND_KEYMAP[arg]
+ has_command = re.search(r"^\s+{0}\s*".format(command), config, re.M)
+ has_command_val = re.search(
+ r"(?:{0}\s)(?P<value>.*)$".format(command), config, re.M
+ )
+ value = ""
+
+ if arg in custom:
+ value = get_custom_value(arg, config, module)
+
+ elif arg == "next_hop_third_party":
+ has_no_command = re.search(
+ r"^\s+no\s+{0}\s*$".format(command), config, re.M
+ )
+ value = False
+ if not has_no_command:
+ value = True
+
+ elif arg in BOOL_PARAMS:
+ value = False
+ if has_command:
+ value = True
+
+ elif command.startswith("advertise-map"):
+ value = []
+ has_adv_map = re.search(
+ r"{0}\s(?P<value1>.*)\s{1}\s(?P<value2>.*)$".format(
+ *command.split()
+ ),
+ config,
+ re.M,
+ )
+ if has_adv_map:
+ value = list(has_adv_map.groups())
+
+ elif command.split()[0] in ["filter-list", "prefix-list", "route-map"]:
+ has_cmd_direction_val = re.search(
+ r"{0}\s(?P<value>.*)\s{1}$".format(*command.split()), config, re.M
+ )
+ if has_cmd_direction_val:
+ value = has_cmd_direction_val.group("value")
+
+ elif has_command_val:
+ value = has_command_val.group("value")
+
+ return value
+
+
+def get_custom_value(arg, config, module):
+ command = PARAM_TO_COMMAND_KEYMAP.get(arg)
+ splitted_config = config.splitlines()
+ value = ""
+
+ if arg.startswith("additional_paths"):
+ value = "inherit"
+ for line in splitted_config:
+ if command in line:
+ if "disable" in line:
+ value = "disable"
+ else:
+ value = "enable"
+ elif arg.startswith("max_prefix"):
+ for line in splitted_config:
+ if "maximum-prefix" in line:
+ splitted_line = line.split()
+ if arg == "max_prefix_limit":
+ value = splitted_line[1]
+ elif arg == "max_prefix_interval" and "restart" in line:
+ value = splitted_line[-1]
+ elif arg == "max_prefix_threshold" and len(splitted_line) > 2:
+ try:
+ int(splitted_line[2])
+ value = splitted_line[2]
+ except ValueError:
+ value = ""
+ elif arg == "max_prefix_warning":
+ value = "warning-only" in line
+ elif arg == "soft_reconfiguration_in":
+ value = "inherit"
+ for line in splitted_config:
+ if command in line:
+ if "always" in line:
+ value = "always"
+ else:
+ value = "enable"
+
+ elif arg == "send_community":
+ value = "none"
+ for line in splitted_config:
+ if command in line:
+ if "extended" in line:
+ if value == "standard":
+ value = "both"
+ else:
+ value = "extended"
+ elif "both" in line:
+ value = "both"
+ else:
+ value = "standard"
+
+ return value
+
+
+def get_existing(module, args, warnings):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+
+ asn_regex = re.compile(
+ r".*router\sbgp\s(?P<existing_asn>\d+(\.\d+)?).*", re.S
+ )
+ match_asn = asn_regex.match(str(netcfg))
+
+ if match_asn:
+ existing_asn = match_asn.group("existing_asn")
+ parents = ["router bgp {0}".format(existing_asn)]
+
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ parents.append("neighbor {0}".format(module.params["neighbor"]))
+ parents.append(
+ "address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ )
+ config = netcfg.get_section(parents)
+
+ if config:
+ for arg in args:
+ if arg not in ["asn", "vrf", "neighbor", "afi", "safi"]:
+ existing[arg] = get_value(arg, config, module)
+
+ existing["asn"] = existing_asn
+ existing["neighbor"] = module.params["neighbor"]
+ existing["vrf"] = module.params["vrf"]
+ existing["afi"] = module.params["afi"]
+ existing["safi"] = module.params["safi"]
+ else:
+ warnings.append(
+ "The BGP process didn't exist but the task just created it."
+ )
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key in table:
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = table.get(key)
+
+ return new_dict
+
+
+def get_default_command(key, value, existing_commands):
+ command = ""
+ if existing_commands.get(key):
+ existing_value = existing_commands.get(key)
+ if value == "inherit":
+ if existing_value != "inherit":
+ command = "no {0}".format(key)
+ else:
+ if key == "advertise-map exist-map":
+ command = "no advertise-map {0} exist-map {1}".format(
+ existing_value[0], existing_value[1]
+ )
+ elif key == "advertise-map non-exist-map":
+ command = "no advertise-map {0} non-exist-map {1}".format(
+ existing_value[0], existing_value[1]
+ )
+ elif key == "filter-list in":
+ command = "no filter-list {0} in".format(existing_value)
+ elif key == "filter-list out":
+ command = "no filter-list {0} out".format(existing_value)
+ elif key == "prefix-list in":
+ command = "no prefix-list {0} in".format(existing_value)
+ elif key == "prefix-list out":
+ command = "no prefix-list {0} out".format(existing_value)
+ elif key == "route-map in":
+ command = "no route-map {0} in".format(existing_value)
+ elif key == "route-map out":
+ command = "no route-map {0} out".format(existing_value)
+ elif key.startswith("maximum-prefix"):
+ command = "no maximum-prefix"
+ elif key == "allowas-in max":
+ command = ["no allowas-in {0}".format(existing_value)]
+ command.append("allowas-in")
+ else:
+ command = "no {0} {1}".format(key, existing_value)
+ else:
+ if key.replace(" ", "_").replace("-", "_") in BOOL_PARAMS:
+ command = "no {0}".format(key)
+ return command
+
+
+def fix_proposed(module, existing, proposed):
+ allowas_in = proposed.get("allowas_in")
+ allowas_in_max = proposed.get("allowas_in_max")
+
+ if allowas_in_max and not allowas_in:
+ proposed.pop("allowas_in_max")
+ elif allowas_in and allowas_in_max:
+ proposed.pop("allowas_in")
+
+ if (
+ existing.get("send_community") == "none"
+ and proposed.get("send_community") == "default"
+ ):
+ proposed.pop("send_community")
+ return proposed
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed = fix_proposed(module, existing, proposed)
+
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+ for key, value in proposed_commands.items():
+ if value in ["inherit", "default"]:
+ command = get_default_command(key, value, existing_commands)
+
+ if isinstance(command, str):
+ if command and command not in commands:
+ commands.append(command)
+ elif isinstance(command, list):
+ for cmd in command:
+ if cmd not in commands:
+ commands.append(cmd)
+
+ elif key.startswith("maximum-prefix"):
+ if module.params["max_prefix_limit"] != "default":
+ command = "maximum-prefix {0}".format(
+ module.params["max_prefix_limit"]
+ )
+ if module.params["max_prefix_threshold"]:
+ command += " {0}".format(
+ module.params["max_prefix_threshold"]
+ )
+ if module.params["max_prefix_interval"]:
+ command += " restart {0}".format(
+ module.params["max_prefix_interval"]
+ )
+ elif module.params["max_prefix_warning"]:
+ command += " warning-only"
+ commands.append(command)
+
+ elif value is True:
+ commands.append(key)
+ elif value is False:
+ commands.append("no {0}".format(key))
+ elif key == "address-family":
+ commands.append(
+ "address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ )
+ elif key.startswith("capability additional-paths"):
+ command = key
+ if value == "disable":
+ command += " disable"
+ commands.append(command)
+ elif key.startswith("advertise-map"):
+ direction = key.split()[1]
+ commands.append(
+ "advertise-map {1} {0} {2}".format(direction, *value)
+ )
+ elif key.split()[0] in ["filter-list", "prefix-list", "route-map"]:
+ commands.append("{1} {0} {2}".format(value, *key.split()))
+
+ elif key == "soft-reconfiguration inbound":
+ command = ""
+ if value == "enable":
+ command = key
+ elif value == "always":
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+ elif key == "send-community":
+ command = key
+ if value in ["standard", "extended"]:
+ commands.append("no " + key + " both")
+ command += " {0}".format(value)
+ commands.append(command)
+ else:
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+
+ if commands:
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ parents.append("neighbor {0}".format(module.params["neighbor"]))
+
+ af_command = "address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ parents.append(af_command)
+ if af_command in commands:
+ commands.remove(af_command)
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, existing, candidate):
+ commands = []
+ parents = ["router bgp {0}".format(module.params["asn"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ parents.append("neighbor {0}".format(module.params["neighbor"]))
+ commands.append(
+ "no address-family {0} {1}".format(
+ module.params["afi"], module.params["safi"]
+ )
+ )
+ candidate.add(commands, parents=parents)
+
+
+def main():
+ argument_spec = dict(
+ asn=dict(required=True, type="str"),
+ vrf=dict(required=False, type="str", default="default"),
+ neighbor=dict(required=True, type="str"),
+ afi=dict(
+ required=True,
+ type="str",
+ choices=["ipv4", "ipv6", "vpnv4", "vpnv6", "l2vpn"],
+ ),
+ safi=dict(
+ required=True, type="str", choices=["unicast", "multicast", "evpn"]
+ ),
+ additional_paths_receive=dict(
+ required=False,
+ type="str",
+ choices=["enable", "disable", "inherit"],
+ ),
+ additional_paths_send=dict(
+ required=False,
+ type="str",
+ choices=["enable", "disable", "inherit"],
+ ),
+ advertise_map_exist=dict(required=False, type="list", elements="str"),
+ advertise_map_non_exist=dict(
+ required=False, type="list", elements="str"
+ ),
+ allowas_in=dict(required=False, type="bool"),
+ allowas_in_max=dict(required=False, type="str"),
+ as_override=dict(required=False, type="bool"),
+ default_originate=dict(required=False, type="bool"),
+ default_originate_route_map=dict(required=False, type="str"),
+ disable_peer_as_check=dict(required=False, type="bool"),
+ filter_list_in=dict(required=False, type="str"),
+ filter_list_out=dict(required=False, type="str"),
+ max_prefix_limit=dict(required=False, type="str"),
+ max_prefix_interval=dict(required=False, type="str"),
+ max_prefix_threshold=dict(required=False, type="str"),
+ max_prefix_warning=dict(required=False, type="bool"),
+ next_hop_self=dict(required=False, type="bool"),
+ next_hop_third_party=dict(required=False, type="bool"),
+ prefix_list_in=dict(required=False, type="str"),
+ prefix_list_out=dict(required=False, type="str"),
+ route_map_in=dict(required=False, type="str"),
+ route_map_out=dict(required=False, type="str"),
+ route_reflector_client=dict(required=False, type="bool"),
+ send_community=dict(
+ required=False,
+ choices=["none", "both", "extended", "standard", "default"],
+ ),
+ soft_reconfiguration_in=dict(
+ required=False, type="str", choices=["enable", "always", "inherit"]
+ ),
+ soo=dict(required=False, type="str"),
+ suppress_inactive=dict(required=False, type="bool"),
+ unsuppress_map=dict(required=False, type="str"),
+ weight=dict(required=False, type="str"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ rewrite_evpn_rt_asn=dict(required=False, type="bool"),
+ )
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[
+ ["advertise_map_exist", "advertise_map_non_exist"],
+ ["max_prefix_interval", "max_prefix_warning"],
+ ["default_originate", "default_originate_route_map"],
+ ["allowas_in", "allowas_in_max"],
+ ],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+ for key in [
+ "max_prefix_interval",
+ "max_prefix_warning",
+ "max_prefix_threshold",
+ ]:
+ if module.params[key] and not module.params["max_prefix_limit"]:
+ module.fail_json(
+ msg="max_prefix_limit is required when using %s" % key
+ )
+ if module.params["vrf"] == "default" and module.params["soo"]:
+ module.fail_json(msg="SOO is only allowed in non-default VRF")
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing = get_existing(module, args, warnings)
+
+ if existing.get("asn") and state == "present":
+ if existing.get("asn") != module.params["asn"]:
+ module.fail_json(
+ msg="Another BGP ASN already exists.",
+ proposed_asn=module.params["asn"],
+ existing_asn=existing.get("asn"),
+ )
+
+ for param in ["advertise_map_exist", "advertise_map_non_exist"]:
+ if module.params[param] == ["default"]:
+ module.params[param] = "default"
+
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key not in ["asn", "vrf", "neighbor"]:
+ if not isinstance(value, list):
+ if str(value).lower() == "true":
+ value = True
+ elif str(value).lower() == "false":
+ value = False
+ elif str(value).lower() == "default":
+ if key in BOOL_PARAMS:
+ value = False
+ else:
+ value = "default"
+ elif key == "send_community" and str(value).lower() == "none":
+ value = "default"
+ if existing.get(key) != value:
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ elif state == "absent" and existing:
+ state_absent(module, existing, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ if not module.check_mode:
+ responses = load_config(module, candidate)
+ if responses:
+ for resp in responses:
+ if resp:
+ if resp.endswith("is valid only for EBGP peers"):
+ module.fail_json(msg=resp)
+ result["changed"] = True
+ result["commands"] = candidate
+ else:
+ result["commands"] = []
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_command.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_command.py
new file mode 100644
index 00000000..c90133f5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_command.py
@@ -0,0 +1,235 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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 = """
+module: nxos_command
+extends_documentation_fragment:
+- cisco.nxos.nxos
+author: Peter Sprygada (@privateip)
+short_description: Run arbitrary command on Cisco NXOS devices
+description:
+- Sends an arbitrary command to an NXOS node and returns the results read from the
+ device. This module includes an argument that will cause the module to wait for
+ a specific condition before returning or timing out if the condition is not met.
+version_added: 1.0.0
+options:
+ commands:
+ description:
+ - The commands to send to the remote NXOS device. The resulting output from the
+ command is returned. If the I(wait_for) argument is provided, the module is
+ not returned until the condition is satisfied or the number of retires as expired.
+ - The I(commands) argument also accepts an alternative form that allows for complex
+ values that specify the command to run and the output format to return. This
+ can be done on a command by command basis. The complex argument supports the
+ keywords C(command) and C(output) where C(command) is the command to run and
+ C(output) is one of 'text' or 'json'.
+ - If a command sent to the device requires answering a prompt, it is possible to pass
+ a dict containing command, answer and prompt. Common answers are 'y' or "\\r"
+ (carriage return, must be double quotes). See examples.
+ required: true
+ type: list
+ elements: raw
+ wait_for:
+ description:
+ - Specifies what to evaluate from the output of the command and what conditionals
+ to apply. This argument will cause the task to wait for a particular conditional
+ to be true before moving forward. If the conditional is not true by the configured
+ retries, the task fails. See examples.
+ aliases:
+ - waitfor
+ type: list
+ elements: str
+ match:
+ description:
+ - The I(match) argument is used in conjunction with the I(wait_for) argument to
+ specify the match policy. Valid values are C(all) or C(any). If the value
+ is set to C(all) then all conditionals in the I(wait_for) must be satisfied. If
+ the value is set to C(any) then only one of the values must be satisfied.
+ default: all
+ choices: ['any', 'all']
+ type: str
+ retries:
+ description:
+ - Specifies the number of retries a command should by tried before it is considered
+ failed. The command is run on the target device every retry and evaluated against
+ the I(wait_for) conditionals.
+ default: 10
+ type: int
+ interval:
+ description:
+ - Configures the interval in seconds to wait between retries of the command. If
+ the command does not pass the specified conditional, the interval indicates
+ how to long to wait before trying the command again.
+ default: 1
+ type: int
+"""
+
+EXAMPLES = """
+- name: run show version on remote devices
+ cisco.nxos.nxos_command:
+ commands: show version
+
+- name: run show version and check to see if output contains Cisco
+ cisco.nxos.nxos_command:
+ commands: show version
+ wait_for: result[0] contains Cisco
+
+- name: run multiple commands on remote nodes
+ cisco.nxos.nxos_command:
+ commands:
+ - show version
+ - show interfaces
+
+- name: run multiple commands and evaluate the output
+ cisco.nxos.nxos_command:
+ commands:
+ - show version
+ - show interfaces
+ wait_for:
+ - result[0] contains Cisco
+ - result[1] contains loopback0
+
+- name: run commands and specify the output format
+ cisco.nxos.nxos_command:
+ commands:
+ - command: show version
+ output: json
+
+- name: run commands that require answering a prompt
+ cisco.nxos.nxos_command:
+ commands:
+ - configure terminal
+ - command: no feature npv
+ prompt: Do you want to continue
+ answer: y
+
+"""
+
+RETURN = """
+stdout:
+ description: The set of responses from the commands
+ returned: always apart from low level errors (such as action plugin)
+ type: list
+ sample: ['...', '...']
+stdout_lines:
+ description: The value of stdout split into a list
+ returned: always apart from low level errors (such as action plugin)
+ type: list
+ sample: [['...', '...'], ['...'], ['...']]
+failed_conditions:
+ description: The list of conditionals that have failed
+ returned: failed
+ type: list
+ sample: ['...', '...']
+"""
+import time
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import (
+ Conditional,
+ FailedConditionalError,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ transform_commands,
+ to_lines,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ run_commands,
+)
+
+
+def parse_commands(module, warnings):
+ commands = transform_commands(module)
+
+ if module.check_mode:
+ for item in list(commands):
+ if not item["command"].startswith("show"):
+ warnings.append(
+ "Only show commands are supported when using check mode, not "
+ "executing %s" % item["command"]
+ )
+ commands.remove(item)
+
+ return commands
+
+
+def to_cli(obj):
+ cmd = obj["command"]
+ if obj.get("output") == "json":
+ cmd += " | json"
+ return cmd
+
+
+def main():
+ """entry point for module execution
+ """
+ argument_spec = dict(
+ # { command: <str>, output: <str>, prompt: <str>, response: <str> }
+ commands=dict(type="list", required=True, elements="raw"),
+ wait_for=dict(type="list", aliases=["waitfor"], elements="str"),
+ match=dict(default="all", choices=["any", "all"]),
+ retries=dict(default=10, type="int"),
+ interval=dict(default=1, type="int"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ result = {"changed": False, "warnings": warnings}
+ commands = parse_commands(module, warnings)
+ wait_for = module.params["wait_for"] or list()
+
+ try:
+ conditionals = [Conditional(c) for c in wait_for]
+ except AttributeError as exc:
+ module.fail_json(msg=to_text(exc))
+
+ retries = module.params["retries"]
+ interval = module.params["interval"]
+ match = module.params["match"]
+
+ while retries > 0:
+ responses = run_commands(module, commands)
+
+ for item in list(conditionals):
+ try:
+ if item(responses):
+ if match == "any":
+ conditionals = list()
+ break
+ conditionals.remove(item)
+ except FailedConditionalError as exc:
+ module.fail_json(msg=to_text(exc))
+
+ if not conditionals:
+ break
+
+ time.sleep(interval)
+ retries -= 1
+
+ if conditionals:
+ failed_conditions = [item.raw for item in conditionals]
+ msg = "One or more conditional statements have not been satisfied"
+ module.fail_json(msg=msg, failed_conditions=failed_conditions)
+
+ result.update(
+ {"stdout": responses, "stdout_lines": list(to_lines(responses))}
+ )
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_config.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_config.py
new file mode 100644
index 00000000..586ebeec
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_config.py
@@ -0,0 +1,598 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_config
+extends_documentation_fragment:
+- cisco.nxos.nxos
+author: Peter Sprygada (@privateip)
+short_description: Manage Cisco NXOS configuration sections
+description:
+- Cisco NXOS configurations use a simple block indent file syntax for segmenting configuration
+ into sections. This module provides an implementation for working with NXOS configuration
+ sections in a deterministic way. This module works with either CLI or NXAPI transports.
+version_added: 1.0.0
+options:
+ lines:
+ description:
+ - The ordered set of commands that should be configured in the section. The commands
+ must be the exact same commands as found in the device running-config to ensure idempotency
+ and correct diff. Be sure to note the configuration command syntax as some commands are
+ automatically modified by the device config parser.
+ type: list
+ aliases:
+ - commands
+ elements: str
+ parents:
+ description:
+ - The ordered set of parents that uniquely identify the section or hierarchy the
+ commands should be checked against. If the parents argument is omitted, the
+ commands are checked against the set of top level or global commands.
+ type: list
+ elements: str
+ src:
+ description:
+ - The I(src) argument provides a path to the configuration file to load into the
+ remote system. The path can either be a full system path to the configuration
+ file if the value starts with / or relative to the root of the implemented role
+ or playbook. This argument is mutually exclusive with the I(lines) and I(parents)
+ arguments. The configuration lines in the source file should be similar to how it
+ will appear if present in the running-configuration of the device including indentation
+ to ensure idempotency and correct diff.
+ type: path
+ replace_src:
+ description:
+ - The I(replace_src) argument provides path to the configuration file to load
+ into the remote system. This argument is used to replace the entire config with
+ a flat-file. This is used with argument I(replace) with value I(config). This
+ is mutually exclusive with the I(lines) and I(src) arguments. This argument
+ will only work for NX-OS versions that support `config replace`. Use I(nxos_file_copy)
+ module to copy the flat file to remote device and then use the path with this argument.
+ The configuration lines in the file should be similar to how it
+ will appear if present in the running-configuration of the device including the indentation
+ to ensure idempotency and correct diff.
+ type: str
+ before:
+ description:
+ - The ordered set of commands to push on to the command stack if a change needs
+ to be made. This allows the playbook designer the opportunity to perform configuration
+ commands prior to pushing any changes without affecting how the set of commands
+ are matched against the system.
+ type: list
+ elements: str
+ after:
+ description:
+ - The ordered set of commands to append to the end of the command stack if a change
+ needs to be made. Just like with I(before) this allows the playbook designer
+ to append a set of commands to be executed after the command set.
+ type: list
+ elements: str
+ match:
+ description:
+ - Instructs the module on the way to perform the matching of the set of commands
+ against the current device config. If match is set to I(line), commands are
+ matched line by line. If match is set to I(strict), command lines are matched
+ with respect to position. If match is set to I(exact), command lines must be
+ an equal match. Finally, if match is set to I(none), the module will not attempt
+ to compare the source configuration with the running configuration on the remote
+ device.
+ default: line
+ choices:
+ - line
+ - strict
+ - exact
+ - none
+ type: str
+ replace:
+ description:
+ - Instructs the module on the way to perform the configuration on the device. If
+ the replace argument is set to I(line) then the modified lines are pushed to
+ the device in configuration mode. If the replace argument is set to I(block)
+ then the entire command block is pushed to the device in configuration mode
+ if any line is not correct. replace I(config) will only work for NX-OS versions
+ that support `config replace`.
+ default: line
+ choices:
+ - line
+ - block
+ - config
+ type: str
+ backup:
+ description:
+ - This argument will cause the module to create a full backup of the current C(running-config)
+ from the remote device before any changes are made. If the C(backup_options)
+ value is not given, the backup file is written to the C(backup) folder in the
+ playbook root directory or role root directory, if playbook is part of an ansible
+ role. If the directory does not exist, it is created.
+ type: bool
+ default: no
+ running_config:
+ description:
+ - The module, by default, will connect to the remote device and retrieve the current
+ running-config to use as a base for comparing against the contents of source. There
+ are times when it is not desirable to have the task get the current running-config
+ for every task in a playbook. The I(running_config) argument allows the implementer
+ to pass in the configuration to use as the base config for comparison.
+ The configuration lines for this option should be similar to how it will appear if present
+ in the running-configuration of the device including the indentation to ensure idempotency
+ and correct diff.
+ aliases:
+ - config
+ type: str
+ defaults:
+ description:
+ - The I(defaults) argument will influence how the running-config is collected
+ from the device. When the value is set to true, the command used to collect
+ the running-config is append with the all keyword. When the value is set to
+ false, the command is issued without the all keyword
+ type: bool
+ default: no
+ save_when:
+ description:
+ - When changes are made to the device running-configuration, the changes are not
+ copied to non-volatile storage by default. Using this argument will change
+ that before. If the argument is set to I(always), then the running-config will
+ always be copied to the startup-config and the I(modified) flag will always
+ be set to True. If the argument is set to I(modified), then the running-config
+ will only be copied to the startup-config if it has changed since the last save
+ to startup-config. If the argument is set to I(never), the running-config will
+ never be copied to the startup-config. If the argument is set to I(changed),
+ then the running-config will only be copied to the startup-config if the task
+ has made a change. I(changed) was added in Ansible 2.6.
+ default: never
+ choices:
+ - always
+ - never
+ - modified
+ - changed
+ type: str
+ diff_against:
+ description:
+ - When using the C(ansible-playbook --diff) command line argument the module can
+ generate diffs against different sources.
+ - When this option is configure as I(startup), the module will return the diff
+ of the running-config against the startup-config.
+ - When this option is configured as I(intended), the module will return the diff
+ of the running-config against the configuration provided in the C(intended_config)
+ argument.
+ - When this option is configured as I(running), the module will return the before
+ and after diff of the running-config with respect to any changes made to the
+ device configuration.
+ choices:
+ - startup
+ - intended
+ - running
+ type: str
+ diff_ignore_lines:
+ description:
+ - Use this argument to specify one or more lines that should be ignored during
+ the diff. This is used for lines in the configuration that are automatically
+ updated by the system. This argument takes a list of regular expressions or
+ exact line matches.
+ type: list
+ elements: str
+ intended_config:
+ description:
+ - The C(intended_config) provides the master configuration that the node should
+ conform to and is used to check the final running-config against. This argument
+ will not modify any settings on the remote device and is strictly used to check
+ the compliance of the current device's configuration against. When specifying
+ this argument, the task should also modify the C(diff_against) value and set
+ it to I(intended). The configuration lines for this value should be similar to how it
+ will appear if present in the running-configuration of the device including the indentation
+ to ensure correct diff.
+ type: str
+ backup_options:
+ description:
+ - This is a dict object containing configurable options related to backup file
+ path. The value of this option is read only when C(backup) is set to I(True),
+ if C(backup) is set to I(false) this option will be silently ignored.
+ suboptions:
+ filename:
+ description:
+ - The filename to be used to store the backup configuration. If the filename
+ is not given it will be generated based on the hostname, current time and
+ date in format defined by <hostname>_config.<current-date>@<current-time>
+ type: str
+ dir_path:
+ description:
+ - This option provides the path ending with directory name in which the backup
+ configuration file will be stored. If the directory does not exist it will
+ be created and the filename is either the value of C(filename) or default
+ filename as described in C(filename) options description. If the path value
+ is not given in that case a I(backup) directory will be created in the current
+ working directory and backup configuration will be copied in C(filename)
+ within I(backup) directory.
+ type: path
+ type: dict
+notes:
+- Abbreviated commands are NOT idempotent, see
+ L(Network FAQ,../network/user_guide/faq.html#why-do-the-config-modules-always-return-changed-true-with-abbreviated-commands).
+- To ensure idempotency and correct diff the configuration lines in the relevant module options should be similar to how they
+ appear if present in the running configuration on device including the indentation.
+"""
+
+EXAMPLES = """
+- name: configure top level configuration and save it
+ cisco.nxos.nxos_config:
+ lines: hostname {{ inventory_hostname }}
+ save_when: modified
+
+- name: diff the running-config against a provided config
+ cisco.nxos.nxos_config:
+ diff_against: intended
+ intended_config: "{{ lookup('file', 'master.cfg') }}"
+
+- cisco.nxos.nxos_config:
+ lines:
+ - 10 permit ip 192.0.2.1/32 any log
+ - 20 permit ip 192.0.2.2/32 any log
+ - 30 permit ip 192.0.2.3/32 any log
+ - 40 permit ip 192.0.2.4/32 any log
+ - 50 permit ip 192.0.2.5/32 any log
+ parents: ip access-list test
+ before: no ip access-list test
+ match: exact
+
+- cisco.nxos.nxos_config:
+ lines:
+ - 10 permit ip 192.0.2.1/32 any log
+ - 20 permit ip 192.0.2.2/32 any log
+ - 30 permit ip 192.0.2.3/32 any log
+ - 40 permit ip 192.0.2.4/32 any log
+ parents: ip access-list test
+ before: no ip access-list test
+ replace: block
+
+- name: replace config with flat file
+ cisco.nxos.nxos_config:
+ replace_src: config.txt
+ replace: config
+
+- name: for idempotency, use full-form commands
+ cisco.nxos.nxos_config:
+ lines:
+ # - shut
+ - shutdown
+ # parents: int eth1/1
+ parents: interface Ethernet1/1
+
+- name: configurable backup path
+ cisco.nxos.nxos_config:
+ backup: yes
+ backup_options:
+ filename: backup.cfg
+ dir_path: /home/user
+"""
+
+RETURN = """
+commands:
+ description: The set of commands that will be pushed to the remote device
+ returned: always
+ type: list
+ sample: ['hostname foo', 'vlan 1', 'name default']
+updates:
+ description: The set of commands that will be pushed to the remote device
+ returned: always
+ type: list
+ sample: ['hostname foo', 'vlan 1', 'name default']
+backup_path:
+ description: The full path to the backup file
+ returned: when backup is yes
+ type: str
+ sample: /playbooks/ansible/backup/nxos_config.2016-07-16@22:28:34
+filename:
+ description: The name of the backup file
+ returned: when backup is yes and filename is not specified in backup options
+ type: str
+ sample: nxos_config.2016-07-16@22:28:34
+shortname:
+ description: The full path to the backup file excluding the timestamp
+ returned: when backup is yes and filename is not specified in backup options
+ type: str
+ sample: /playbooks/ansible/backup/nxos_config
+date:
+ description: The date extracted from the backup file name
+ returned: when backup is yes
+ type: str
+ sample: "2016-07-16"
+time:
+ description: The time extracted from the backup file name
+ returned: when backup is yes
+ type: str
+ sample: "22:28:34"
+"""
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.connection import ConnectionError
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ NetworkConfig,
+ dumps,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+ get_connection,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list,
+)
+
+
+def get_running_config(module, config=None, flags=None):
+ contents = module.params["running_config"]
+ if not contents:
+ if config:
+ contents = config
+ else:
+ contents = get_config(module, flags=flags)
+ return contents
+
+
+def get_candidate(module):
+ candidate = ""
+ if module.params["src"]:
+ if module.params["replace"] != "config":
+ candidate = module.params["src"]
+ if module.params["replace"] == "config":
+ candidate = "config replace {0}".format(module.params["replace_src"])
+ elif module.params["lines"]:
+ candidate_obj = NetworkConfig(indent=2)
+ parents = module.params["parents"] or list()
+ candidate_obj.add(module.params["lines"], parents=parents)
+ candidate = dumps(candidate_obj, "raw")
+ return candidate
+
+
+def execute_show_commands(module, commands, output="text"):
+ cmds = []
+ for command in to_list(commands):
+ cmd = {"command": command, "output": output}
+ cmds.append(cmd)
+ body = run_commands(module, cmds)
+ return body
+
+
+def save_config(module, result):
+ result["changed"] = True
+ if not module.check_mode:
+ cmd = {
+ "command": "copy running-config startup-config",
+ "output": "text",
+ }
+ run_commands(module, [cmd])
+ else:
+ module.warn(
+ "Skipping command `copy running-config startup-config` "
+ "due to check_mode. Configuration not copied to "
+ "non-volatile storage"
+ )
+
+
+def main():
+ """ main entry point for module execution
+ """
+ backup_spec = dict(filename=dict(), dir_path=dict(type="path"))
+ argument_spec = dict(
+ src=dict(type="path"),
+ replace_src=dict(),
+ lines=dict(aliases=["commands"], type="list", elements="str"),
+ parents=dict(type="list", elements="str"),
+ before=dict(type="list", elements="str"),
+ after=dict(type="list", elements="str"),
+ match=dict(
+ default="line", choices=["line", "strict", "exact", "none"]
+ ),
+ replace=dict(default="line", choices=["line", "block", "config"]),
+ running_config=dict(aliases=["config"]),
+ intended_config=dict(),
+ defaults=dict(type="bool", default=False),
+ backup=dict(type="bool", default=False),
+ backup_options=dict(type="dict", options=backup_spec),
+ save_when=dict(
+ choices=["always", "never", "modified", "changed"], default="never"
+ ),
+ diff_against=dict(choices=["running", "startup", "intended"]),
+ diff_ignore_lines=dict(type="list", elements="str"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ mutually_exclusive = [("lines", "src", "replace_src"), ("parents", "src")]
+
+ required_if = [
+ ("match", "strict", ["lines"]),
+ ("match", "exact", ["lines"]),
+ ("replace", "block", ["lines"]),
+ ("replace", "config", ["replace_src"]),
+ ("diff_against", "intended", ["intended_config"]),
+ ]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ required_if=required_if,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+
+ result = {"changed": False, "warnings": warnings}
+
+ config = None
+
+ diff_ignore_lines = module.params["diff_ignore_lines"]
+ path = module.params["parents"]
+ connection = get_connection(module)
+ contents = None
+ flags = ["all"] if module.params["defaults"] else []
+ replace_src = module.params["replace_src"]
+ if replace_src:
+ if module.params["replace"] != "config":
+ module.fail_json(
+ msg="replace: config is required with replace_src"
+ )
+
+ if module.params["backup"] or (
+ module._diff and module.params["diff_against"] == "running"
+ ):
+ contents = get_config(module, flags=flags)
+ config = NetworkConfig(indent=2, contents=contents)
+ if module.params["backup"]:
+ result["__backup__"] = contents
+
+ if any((module.params["src"], module.params["lines"], replace_src)):
+ msg = (
+ "To ensure idempotency and correct diff the input configuration lines should be"
+ " similar to how they appear if present in the running configuration on device"
+ )
+ if module.params["src"] or replace_src:
+ msg += " including the indentation"
+ warnings.append(msg)
+ match = module.params["match"]
+ replace = module.params["replace"]
+
+ commit = not module.check_mode
+ candidate = get_candidate(module)
+ running = get_running_config(module, contents, flags=flags)
+ if replace_src:
+ commands = candidate.split("\n")
+ result["commands"] = result["updates"] = commands
+ if commit:
+ load_config(module, commands, replace=replace_src)
+
+ result["changed"] = True
+ else:
+ try:
+ response = connection.get_diff(
+ candidate=candidate,
+ running=running,
+ diff_match=match,
+ diff_ignore_lines=diff_ignore_lines,
+ path=path,
+ diff_replace=replace,
+ )
+ except ConnectionError as exc:
+ module.fail_json(
+ msg=to_text(exc, errors="surrogate_then_replace")
+ )
+
+ config_diff = response["config_diff"]
+ if config_diff:
+ commands = config_diff.split("\n")
+
+ if module.params["before"]:
+ commands[:0] = module.params["before"]
+
+ if module.params["after"]:
+ commands.extend(module.params["after"])
+
+ result["commands"] = commands
+ result["updates"] = commands
+
+ if commit:
+ load_config(module, commands, replace=replace_src)
+
+ result["changed"] = True
+
+ running_config = module.params["running_config"]
+ startup_config = None
+
+ if module.params["save_when"] == "always":
+ save_config(module, result)
+ elif module.params["save_when"] == "modified":
+ output = execute_show_commands(
+ module, ["show running-config", "show startup-config"]
+ )
+
+ running_config = NetworkConfig(
+ indent=2, contents=output[0], ignore_lines=diff_ignore_lines
+ )
+ startup_config = NetworkConfig(
+ indent=2, contents=output[1], ignore_lines=diff_ignore_lines
+ )
+
+ if running_config.sha1 != startup_config.sha1:
+ save_config(module, result)
+ elif module.params["save_when"] == "changed" and result["changed"]:
+ save_config(module, result)
+
+ if module._diff:
+ if not running_config:
+ output = execute_show_commands(module, "show running-config")
+ contents = output[0]
+ else:
+ contents = running_config
+
+ # recreate the object in order to process diff_ignore_lines
+ running_config = NetworkConfig(
+ indent=2, contents=contents, ignore_lines=diff_ignore_lines
+ )
+
+ if module.params["diff_against"] == "running":
+ if module.check_mode:
+ module.warn(
+ "unable to perform diff against running-config due to check mode"
+ )
+ contents = None
+ else:
+ contents = config.config_text
+
+ elif module.params["diff_against"] == "startup":
+ if not startup_config:
+ output = execute_show_commands(module, "show startup-config")
+ contents = output[0]
+ else:
+ contents = startup_config.config_text
+
+ elif module.params["diff_against"] == "intended":
+ contents = module.params["intended_config"]
+
+ if contents is not None:
+ base_config = NetworkConfig(
+ indent=2, contents=contents, ignore_lines=diff_ignore_lines
+ )
+
+ if running_config.sha1 != base_config.sha1:
+ if module.params["diff_against"] == "intended":
+ before = running_config
+ after = base_config
+ elif module.params["diff_against"] in ("startup", "running"):
+ before = base_config
+ after = running_config
+
+ result.update(
+ {
+ "changed": True,
+ "diff": {"before": str(before), "after": str(after)},
+ }
+ )
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_devicealias.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_devicealias.py
new file mode 100644
index 00000000..30edfc75
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_devicealias.py
@@ -0,0 +1,593 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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
+
+
+DOCUMENTATION = """
+module: nxos_devicealias
+short_description: Configuration of device alias for Cisco NXOS MDS Switches.
+description:
+- Configuration of device alias for Cisco MDS NXOS.
+version_added: 1.0.0
+author:
+- Suhas Bharadwaj (@srbharadwaj) (subharad@cisco.com)
+notes:
+- Tested against NX-OS 8.4(1)
+options:
+ distribute:
+ description:
+ - Enable/Disable device-alias distribution
+ type: bool
+ mode:
+ description:
+ - Mode of devices-alias, basic or enhanced
+ choices:
+ - basic
+ - enhanced
+ type: str
+ da:
+ description:
+ - List of device-alias to be added or removed
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the device-alias to be added or removed
+ required: true
+ type: str
+ pwwn:
+ description:
+ - pwwn to which the name needs to be associated with
+ type: str
+ remove:
+ description:
+ - Removes the device-alias if set to True
+ type: bool
+ default: false
+ rename:
+ description:
+ - List of device-alias to be renamed
+ type: list
+ elements: dict
+ suboptions:
+ old_name:
+ description:
+ - Old name of the device-alias that needs to be renamed
+ required: true
+ type: str
+ new_name:
+ description:
+ - New name of the device-alias
+ required: true
+ type: str
+"""
+
+EXAMPLES = """
+- name: Test that device alias module works
+ cisco.nxos.nxos_devicealias:
+ da:
+ - name: test1_add
+ pwwn: 56:2:22:11:22:88:11:67
+ - name: test2_add
+ pwwn: 65:22:22:11:22:22:11:d
+ - name: dev1
+ remove: true
+ - name: dev2
+ remove: true
+ distribute: true
+ mode: enhanced
+ rename:
+ - new_name: bcd
+ old_name: abc
+ - new_name: bcd1
+ old_name: abc1
+
+
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - terminal dont-ask
+ - device-alias database
+ - device-alias name somename pwwn 10:00:00:00:89:a1:01:03
+ - device-alias name somename1 pwwn 10:00:00:00:89:a1:02:03
+ - device-alias commit
+ - no terminal dont-ask
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+import string
+
+__metaclass__ = type
+
+VALID_DA_CHARS = ("-", "_", "$", "^")
+
+
+class showDeviceAliasStatus(object):
+ """docstring for showDeviceAliasStatus"""
+
+ def __init__(self, module):
+ self.module = module
+ self.distribute = ""
+ self.mode = ""
+ self.locked = False
+ self.update()
+
+ def execute_show_cmd(self, cmd):
+ output = execute_show_command(cmd, self.module)[0]
+ return output
+
+ def update(self):
+ command = "show device-alias status"
+ output = self.execute_show_cmd(command).split("\n")
+ for o in output:
+ if "Fabric Distribution" in o:
+ self.distribute = o.split(":")[1].strip().lower()
+ if "Mode" in o:
+ self.mode = o.split("Mode:")[1].strip().lower()
+ if "Locked" in o:
+ self.locked = True
+
+ def isLocked(self):
+ return self.locked
+
+ def getDistribute(self):
+ return self.distribute
+
+ def getMode(self):
+ return self.mode
+
+
+class showDeviceAliasDatabase(object):
+ """docstring for showDeviceAliasDatabase"""
+
+ def __init__(self, module):
+ self.module = module
+ self.da_dict = {}
+ self.update()
+
+ def execute_show_cmd(self, cmd):
+ output = execute_show_command(cmd, self.module)[0]
+ return output
+
+ def update(self):
+ command = "show device-alias database"
+ # output = execute_show_command(command, self.module)[0].split("\n")
+ output = self.execute_show_cmd(command)
+ self.da_list = output.split("\n")
+ for eachline in self.da_list:
+ if "device-alias" in eachline:
+ sv = eachline.strip().split()
+ self.da_dict[sv[2]] = sv[4]
+
+ def isNameInDaDatabase(self, name):
+ return name in self.da_dict.keys()
+
+ def isPwwnInDaDatabase(self, pwwn):
+ newpwwn = ":".join(
+ ["0" + str(ep) if len(ep) == 1 else ep for ep in pwwn.split(":")]
+ )
+ return newpwwn in self.da_dict.values()
+
+ def isNamePwwnPresentInDatabase(self, name, pwwn):
+ newpwwn = ":".join(
+ ["0" + str(ep) if len(ep) == 1 else ep for ep in pwwn.split(":")]
+ )
+ if name in self.da_dict.keys():
+ if newpwwn == self.da_dict[name]:
+ return True
+ return False
+
+ def getPwwnByName(self, name):
+ if name in self.da_dict.keys():
+ return self.da_dict[name]
+ else:
+ return None
+
+ def getNameByPwwn(self, pwwn):
+ newpwwn = ":".join(
+ ["0" + str(ep) if len(ep) == 1 else ep for ep in pwwn.split(":")]
+ )
+ for n, p in self.da_dict.items():
+ if p == newpwwn:
+ return n
+ return None
+
+
+def isPwwnValid(pwwn):
+ pwwnsplit = pwwn.split(":")
+ if len(pwwnsplit) != 8:
+ return False
+ for eachpwwnsplit in pwwnsplit:
+ if len(eachpwwnsplit) > 2 or len(eachpwwnsplit) < 1:
+ return False
+ if not all(c in string.hexdigits for c in eachpwwnsplit):
+ return False
+ return True
+
+
+def isNameValid(name):
+ if not name[0].isalpha():
+ # Illegal first character. Name must start with a letter
+ return False
+ if len(name) > 64:
+ return False
+ for character in name:
+ if not character.isalnum() and character not in VALID_DA_CHARS:
+ return False
+ return True
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ output = "text"
+ commands = [{"command": command, "output": output}]
+ out = run_commands(module, commands)
+ return out
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def main():
+ element_spec = dict(
+ name=dict(required=True, type="str"),
+ pwwn=dict(type="str"),
+ remove=dict(type="bool", default=False),
+ )
+
+ element_spec_rename = dict(
+ old_name=dict(required=True, type="str"),
+ new_name=dict(required=True, type="str"),
+ )
+
+ argument_spec = dict(
+ distribute=dict(type="bool"),
+ mode=dict(type="str", choices=["enhanced", "basic"]),
+ da=dict(type="list", elements="dict", options=element_spec),
+ rename=dict(type="list", elements="dict", options=element_spec_rename),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ messages = list()
+ commands_to_execute = list()
+ result = {"changed": False}
+
+ distribute = module.params["distribute"]
+ mode = module.params["mode"]
+ da = module.params["da"]
+ rename = module.params["rename"]
+
+ # Step 0.0: Validate syntax of name and pwwn
+ # Also validate syntax of rename arguments
+ if da is not None:
+ for eachdict in da:
+ name = eachdict["name"]
+ pwwn = eachdict["pwwn"]
+ remove = eachdict["remove"]
+ if pwwn is not None:
+ pwwn = pwwn.lower()
+ if not remove:
+ if pwwn is None:
+ module.fail_json(
+ msg="This device alias name "
+ + str(name)
+ + " which needs to be added, does not have pwwn specified. Please specify a valid pwwn"
+ )
+ if not isNameValid(name):
+ module.fail_json(
+ msg="This pwwn name is invalid : "
+ + str(name)
+ + ". Note that name cannot be more than 64 alphanumeric chars, "
+ + "it must start with a letter, and can only contain these characters: "
+ + ", ".join(
+ ["'{0}'".format(c) for c in VALID_DA_CHARS]
+ )
+ )
+ if not isPwwnValid(pwwn):
+ module.fail_json(
+ msg="This pwwn is invalid : "
+ + str(pwwn)
+ + ". Please check that its a valid pwwn"
+ )
+ if rename is not None:
+ for eachdict in rename:
+ oldname = eachdict["old_name"]
+ newname = eachdict["new_name"]
+ if not isNameValid(oldname):
+ module.fail_json(
+ msg="This pwwn name is invalid : "
+ + str(oldname)
+ + ". Note that name cannot be more than 64 alphanumeric chars, "
+ + "it must start with a letter, and can only contain these characters: "
+ + ", ".join(["'{0}'".format(c) for c in VALID_DA_CHARS])
+ )
+ if not isNameValid(newname):
+ module.fail_json(
+ msg="This pwwn name is invalid : "
+ + str(newname)
+ + ". Note that name cannot be more than 64 alphanumeric chars, "
+ + "it must start with a letter, and can only contain these characters: "
+ + ", ".join(["'{0}'".format(c) for c in VALID_DA_CHARS])
+ )
+
+ # Step 0.1: Check DA status
+ shDAStausObj = showDeviceAliasStatus(module)
+ d = shDAStausObj.getDistribute()
+ m = shDAStausObj.getMode()
+ if shDAStausObj.isLocked():
+ module.fail_json(
+ msg="device-alias has acquired lock on the switch. Hence cannot procced."
+ )
+
+ # Step 1: Process distribute
+ commands = []
+ if distribute is not None:
+ if distribute:
+ # playbook has distribute as True(enabled)
+ if d == "disabled":
+ # but switch distribute is disabled(false), so set it to
+ # true(enabled)
+ commands.append("device-alias distribute")
+ messages.append(
+ "device-alias distribute changed from disabled to enabled"
+ )
+ else:
+ messages.append(
+ "device-alias distribute remains unchanged. current distribution mode is enabled"
+ )
+ else:
+ # playbook has distribute as False(disabled)
+ if d == "enabled":
+ # but switch distribute is enabled(true), so set it to
+ # false(disabled)
+ commands.append("no device-alias distribute")
+ messages.append(
+ "device-alias distribute changed from enabled to disabled"
+ )
+ else:
+ messages.append(
+ "device-alias distribute remains unchanged. current distribution mode is disabled"
+ )
+
+ cmds = flatten_list(commands)
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the da_add/da_remove stage
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+
+ # Step 2: Process mode
+ commands = []
+ if mode is not None:
+ if mode == "basic":
+ # playbook has mode as basic
+ if m == "enhanced":
+ # but switch mode is enhanced, so set it to basic
+ commands.append("no device-alias mode enhanced")
+ messages.append(
+ "device-alias mode changed from enhanced to basic"
+ )
+ else:
+ messages.append(
+ "device-alias mode remains unchanged. current mode is basic"
+ )
+
+ else:
+ # playbook has mode as enhanced
+ if m == "basic":
+ # but switch mode is basic, so set it to enhanced
+ commands.append("device-alias mode enhanced")
+ messages.append(
+ "device-alias mode changed from basic to enhanced"
+ )
+ else:
+ messages.append(
+ "device-alias mode remains unchanged. current mode is enhanced"
+ )
+
+ if commands:
+ if distribute:
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+ else:
+ if distribute is None and d == "enabled":
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands)
+
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the end
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+
+ # Step 3: Process da
+ commands = []
+ shDADatabaseObj = showDeviceAliasDatabase(module)
+ if da is not None:
+ da_remove_list = []
+ da_add_list = []
+ for eachdict in da:
+ name = eachdict["name"]
+ pwwn = eachdict["pwwn"]
+ remove = eachdict["remove"]
+ if pwwn is not None:
+ pwwn = pwwn.lower()
+ if remove:
+ if shDADatabaseObj.isNameInDaDatabase(name):
+ commands.append("no device-alias name " + name)
+ da_remove_list.append(name)
+ else:
+ messages.append(
+ name
+ + " - This device alias name is not in switch device-alias database, hence cannot be removed."
+ )
+ else:
+ if shDADatabaseObj.isNamePwwnPresentInDatabase(name, pwwn):
+ messages.append(
+ name
+ + " : "
+ + pwwn
+ + " - This device alias name,pwwn is already in switch device-alias database, hence nothing to configure"
+ )
+ else:
+ if shDADatabaseObj.isNameInDaDatabase(name):
+ module.fail_json(
+ msg=name
+ + " - This device alias name is already present in switch device-alias database but assigned to another pwwn ("
+ + shDADatabaseObj.getPwwnByName(name)
+ + ") hence cannot be added"
+ )
+
+ elif shDADatabaseObj.isPwwnInDaDatabase(pwwn):
+ module.fail_json(
+ msg=pwwn
+ + " - This device alias pwwn is already present in switch device-alias database but assigned to another name ("
+ + shDADatabaseObj.getNameByPwwn(pwwn)
+ + ") hence cannot be added"
+ )
+
+ else:
+ commands.append(
+ "device-alias name " + name + " pwwn " + pwwn
+ )
+ da_add_list.append(name)
+
+ if len(da_add_list) != 0 or len(da_remove_list) != 0:
+ commands = ["device-alias database"] + commands
+ if distribute:
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+ else:
+ if distribute is None and d == "enabled":
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"]
+ + commands
+ + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands)
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the end
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+ if len(da_remove_list) != 0:
+ messages.append(
+ "the required device-alias were removed. "
+ + ",".join(da_remove_list)
+ )
+ if len(da_add_list) != 0:
+ messages.append(
+ "the required device-alias were added. "
+ + ",".join(da_add_list)
+ )
+
+ # Step 5: Process rename
+ commands = []
+ if rename is not None:
+ for eachdict in rename:
+ oldname = eachdict["old_name"]
+ newname = eachdict["new_name"]
+ if shDADatabaseObj.isNameInDaDatabase(newname):
+ module.fail_json(
+ changed=False,
+ commands=cmds,
+ msg=newname
+ + " - this name is already present in the device-alias database, hence we cannot rename "
+ + oldname
+ + " with this one",
+ )
+ if shDADatabaseObj.isNameInDaDatabase(oldname):
+ commands.append(
+ "device-alias rename " + oldname + " " + newname
+ )
+ else:
+ module.fail_json(
+ changed=False,
+ commands=cmds,
+ msg=oldname
+ + " - this name is not present in the device-alias database, hence we cannot rename.",
+ )
+
+ if len(commands) != 0:
+ commands = ["device-alias database"] + commands
+ if distribute:
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+ else:
+ if distribute is None and d == "enabled":
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"]
+ + commands
+ + ["no terminal dont-ask"]
+ )
+ cmds = flatten_list(commands)
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the end
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+
+ # Step END: check for 'check' mode
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ commands=commands_to_execute,
+ msg="Check Mode: No cmds issued to the hosts",
+ )
+
+ result["messages"] = messages
+ result["commands"] = commands_to_execute
+ result["warnings"] = warnings
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_global.py
new file mode 100644
index 00000000..29aa1bf4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_global.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_evpn_global
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Handles the EVPN control plane for VXLAN.
+description:
+- Handles the EVPN control plane for VXLAN.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- This module is not supported on Nexus 3000 series of switches.
+options:
+ nv_overlay_evpn:
+ description:
+ - EVPN control plane.
+ required: true
+ type: bool
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_evpn_global:
+ nv_overlay_evpn: true
+"""
+
+RETURN = """
+commands:
+ description: The set of commands to be sent to the remote device
+ returned: always
+ type: list
+ sample: ['nv overlay evpn']
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+ nxos_argument_spec,
+)
+
+
+def main():
+ argument_spec = dict(nv_overlay_evpn=dict(required=True, type="bool"))
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ result = {"changed": False}
+
+ warnings = list()
+ if warnings:
+ result["warnings"] = warnings
+
+ config = get_config(module)
+ commands = list()
+
+ info = get_capabilities(module).get("device_info", {})
+ os_platform = info.get("network_os_platform", "")
+
+ if "3K" in os_platform:
+ module.fail_json(
+ msg="This module is not supported on Nexus 3000 series"
+ )
+
+ if module.params["nv_overlay_evpn"] is True:
+ if "nv overlay evpn" not in config:
+ commands.append("nv overlay evpn")
+ elif "nv overlay evpn" in config:
+ commands.append("no nv overlay evpn")
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ result["commands"] = commands
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_vni.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_vni.py
new file mode 100644
index 00000000..bd3dcb10
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_evpn_vni.py
@@ -0,0 +1,310 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_evpn_vni
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages Cisco EVPN VXLAN Network Identifier (VNI).
+description:
+- Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network Identifier (VNI)
+ configurations of a Nexus device.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- default, where supported, restores params default value.
+- RD override is not permitted. You should set it to the default values first and
+ then reconfigure it.
+- C(route_target_both), C(route_target_import) and C(route_target_export valid) values
+ are a list of extended communities, (i.e. ['1.2.3.4:5', '33:55']) or the keywords
+ 'auto' or 'default'.
+- The C(route_target_both) property is discouraged due to the inconsistent behavior
+ of the property across Nexus platforms and image versions. For this reason it is
+ recommended to use explicit C(route_target_export) and C(route_target_import) properties
+ instead of C(route_target_both).
+- RD valid values are a string in one of the route-distinguisher formats, the keyword
+ 'auto', or the keyword 'default'.
+options:
+ vni:
+ description:
+ - The EVPN VXLAN Network Identifier.
+ required: true
+ type: str
+ route_distinguisher:
+ description:
+ - The VPN Route Distinguisher (RD). The RD is combined with the IPv4 or IPv6 prefix
+ learned by the PE router to create a globally unique address.
+ type: str
+ route_target_both:
+ description:
+ - Enables/Disables route-target settings for both import and export target communities
+ using a single property.
+ type: list
+ elements: str
+ route_target_import:
+ description:
+ - Sets the route-target 'import' extended communities.
+ type: list
+ elements: str
+ route_target_export:
+ description:
+ - Sets the route-target 'export' extended communities.
+ type: list
+ elements: str
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- name: vni configuration
+ cisco.nxos.nxos_evpn_vni:
+ vni: 6000
+ route_distinguisher: 60:10
+ route_target_import:
+ - 5000:10
+ - 4100:100
+ route_target_export: auto
+ route_target_both: default
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["evpn", "vni 6000 l2", "route-target import 5001:10"]
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+PARAM_TO_COMMAND_KEYMAP = {
+ "vni": "vni",
+ "route_distinguisher": "rd",
+ "route_target_both": "route-target both",
+ "route_target_import": "route-target import",
+ "route_target_export": "route-target export",
+}
+
+
+def get_value(arg, config, module):
+ command = PARAM_TO_COMMAND_KEYMAP.get(arg)
+ command_re = re.compile(r"(?:{0}\s)(?P<value>.*)$".format(command), re.M)
+ value = ""
+ if command in config:
+ value = command_re.search(config).group("value")
+ return value
+
+
+def get_route_target_value(arg, config, module):
+ splitted_config = config.splitlines()
+ value_list = []
+ command = PARAM_TO_COMMAND_KEYMAP.get(arg)
+ command_re = re.compile(r"(?:{0}\s)(?P<value>.*)$".format(command), re.M)
+
+ for line in splitted_config:
+ value = ""
+ if command in line.strip():
+ value = command_re.search(line).group("value")
+ value_list.append(value)
+ return value_list
+
+
+def get_existing(module, args):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+ parents = ["evpn", "vni {0} l2".format(module.params["vni"])]
+ config = netcfg.get_section(parents)
+
+ if config:
+ for arg in args:
+ if arg != "vni":
+ if arg == "route_distinguisher":
+ existing[arg] = get_value(arg, config, module)
+ else:
+ existing[arg] = get_route_target_value(arg, config, module)
+
+ existing_fix = dict((k, v) for k, v in existing.items() if v)
+ if not existing_fix:
+ existing = existing_fix
+
+ existing["vni"] = module.params["vni"]
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key in table:
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = table.get(key)
+ return new_dict
+
+
+def fix_proposed(proposed_commands):
+ new_proposed = {}
+ for key, value in proposed_commands.items():
+ if key == "route-target both":
+ new_proposed["route-target export"] = value
+ new_proposed["route-target import"] = value
+ else:
+ new_proposed[key] = value
+ return new_proposed
+
+
+def state_present(module, existing, proposed):
+ commands = list()
+ parents = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ if proposed_commands.get("route-target both"):
+ proposed_commands = fix_proposed(proposed_commands)
+
+ for key, value in proposed_commands.items():
+ if key.startswith("route-target"):
+ if value == ["default"]:
+ existing_value = existing_commands.get(key)
+
+ if existing_value:
+ for target in existing_value:
+ commands.append("no {0} {1}".format(key, target))
+ elif not isinstance(value, list):
+ value = [value]
+
+ for target in value:
+ if target == "default":
+ continue
+ if existing:
+ if target not in existing.get(
+ key.replace("-", "_").replace(" ", "_")
+ ):
+ commands.append("{0} {1}".format(key, target))
+ else:
+ commands.append("{0} {1}".format(key, target))
+
+ if existing.get(key.replace("-", "_").replace(" ", "_")):
+ for exi in existing.get(
+ key.replace("-", "_").replace(" ", "_")
+ ):
+ if exi not in value:
+ commands.append("no {0} {1}".format(key, exi))
+
+ elif value == "default":
+ existing_value = existing_commands.get(key)
+ if existing_value:
+ commands.append("no {0} {1}".format(key, existing_value))
+ else:
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+
+ if commands:
+ parents = ["evpn", "vni {0} l2".format(module.params["vni"])]
+
+ return commands, parents
+
+
+def state_absent(module, existing, proposed):
+ commands = ["no vni {0} l2".format(module.params["vni"])]
+ parents = ["evpn"]
+ return commands, parents
+
+
+def main():
+ argument_spec = dict(
+ vni=dict(required=True, type="str"),
+ route_distinguisher=dict(required=False, type="str"),
+ route_target_both=dict(required=False, type="list", elements="str"),
+ route_target_import=dict(required=False, type="list", elements="str"),
+ route_target_export=dict(required=False, type="list", elements="str"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing = get_existing(module, args)
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+ commands = []
+ parents = []
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key != "vni":
+ if value == "true":
+ value = True
+ elif value == "false":
+ value = False
+ if existing.get(key) != value:
+ proposed[key] = value
+
+ if state == "present":
+ commands, parents = state_present(module, existing, proposed)
+ elif state == "absent" and existing:
+ commands, parents = state_absent(module, existing, proposed)
+
+ if commands:
+ candidate = CustomNetworkConfig(indent=3)
+ candidate.add(commands, parents=parents)
+ candidate = candidate.items_text()
+ if not module.check_mode:
+ load_config(module, candidate)
+ results["changed"] = True
+ results["commands"] = candidate
+ else:
+ results["commands"] = []
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_facts.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_facts.py
new file mode 100644
index 00000000..5b23d9ac
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_facts.py
@@ -0,0 +1,248 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_facts
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Gets facts about NX-OS switches
+description:
+- Collects facts from Cisco Nexus devices running the NX-OS operating system. Fact
+ collection is supported over both Cli and Nxapi transports. This module prepends
+ all of the base network fact keys with C(ansible_net_<fact>). The facts module
+ will always collect a base set of facts from the device and can enable or disable
+ collection of additional facts.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+options:
+ gather_subset:
+ description:
+ - When supplied, this argument will restrict the facts collected to a given subset. Possible
+ values for this argument include all, hardware, config, legacy, and interfaces. Can
+ specify a list of values to include a larger subset. Values can also be used
+ with an initial C(!) to specify that a specific subset should not be collected.
+ required: false
+ default: '!config'
+ type: list
+ elements: str
+ gather_network_resources:
+ description:
+ - When supplied, this argument will restrict the facts collected to a given subset.
+ Possible values for this argument include all and the resources like interfaces,
+ vlans etc. Can specify a list of values to include a larger subset. Values can
+ also be used with an initial C(!) to specify that a specific subset should
+ not be collected. Valid subsets are 'all', 'bfd_interfaces', 'lag_interfaces',
+ 'telemetry', 'vlans', 'lacp', 'lacp_interfaces', 'interfaces', 'l3_interfaces',
+ 'l2_interfaces', 'lldp_global', 'acls', 'acl_interfaces', 'ospfv2'.
+ required: false
+ type: list
+ elements: str
+"""
+
+EXAMPLES = """
+- name: Gather all legacy facts
+ cisco.nxos.nxos_facts:
+ gather_subset: all
+- name: Gather only the config and default facts
+ cisco.nxos.nxos_facts:
+ gather_subset:
+ - config
+- name: Do not gather hardware facts
+ cisco.nxos.nxos_facts:
+ gather_subset:
+ - '!hardware'
+- name: Gather legacy and resource facts
+ cisco.nxos.nxos_facts:
+ gather_subset: all
+ gather_network_resources: all
+- name: Gather only the interfaces resource facts and no legacy facts
+ cisco.nxos.nxos_facts:
+ gather_subset:
+ - '!all'
+ - '!min'
+ gather_network_resources:
+ - interfaces
+- name: Gather interfaces resource and minimal legacy facts
+ cisco.nxos.nxos_facts:
+ gather_subset: min
+ gather_network_resources: interfaces
+"""
+
+RETURN = """
+ansible_net_gather_subset:
+ description: The list of fact subsets collected from the device
+ returned: always
+ type: list
+ansible_net_gather_network_resources:
+ description: The list of fact for network resource subsets collected from the device
+ returned: when the resource is configured
+ type: list
+# default
+ansible_net_model:
+ description: The model name returned from the device
+ returned: always
+ type: str
+ansible_net_serialnum:
+ description: The serial number of the remote device
+ returned: always
+ type: str
+ansible_net_version:
+ description: The operating system version running on the remote device
+ returned: always
+ type: str
+ansible_net_hostname:
+ description: The configured hostname of the device
+ returned: always
+ type: str
+ansible_net_image:
+ description: The image file the device is running
+ returned: always
+ type: str
+ansible_net_api:
+ description: The name of the transport
+ returned: always
+ type: str
+ansible_net_license_hostid:
+ description: The License host id of the device
+ returned: always
+ type: str
+ansible_net_python_version:
+ description: The Python version Ansible controller is using
+ returned: always
+ type: str
+# hardware
+ansible_net_filesystems:
+ description: All file system names available on the device
+ returned: when hardware is configured
+ type: list
+ansible_net_memfree_mb:
+ description: The available free memory on the remote device in Mb
+ returned: when hardware is configured
+ type: int
+ansible_net_memtotal_mb:
+ description: The total memory on the remote device in Mb
+ returned: when hardware is configured
+ type: int
+# config
+ansible_net_config:
+ description: The current active config from the device
+ returned: when config is configured
+ type: str
+# interfaces
+ansible_net_all_ipv4_addresses:
+ description: All IPv4 addresses configured on the device
+ returned: when interfaces is configured
+ type: list
+ansible_net_all_ipv6_addresses:
+ description: All IPv6 addresses configured on the device
+ returned: when interfaces is configured
+ type: list
+ansible_net_interfaces:
+ description: A hash of all interfaces running on the system
+ returned: when interfaces is configured
+ type: dict
+ansible_net_neighbors:
+ description:
+ - The list of LLDP and CDP neighbors from the device. If both,
+ CDP and LLDP neighbor data is present on one port, CDP is preferred.
+ returned: when interfaces is configured
+ type: dict
+# legacy (pre Ansible 2.2)
+fan_info:
+ description: A hash of facts about fans in the remote device
+ returned: when legacy is configured
+ type: dict
+hostname:
+ description: The configured hostname of the remote device
+ returned: when legacy is configured
+ type: dict
+interfaces_list:
+ description: The list of interface names on the remote device
+ returned: when legacy is configured
+ type: dict
+kickstart:
+ description: The software version used to boot the system
+ returned: when legacy is configured
+ type: str
+module:
+ description: A hash of facts about the modules in a remote device
+ returned: when legacy is configured
+ type: dict
+platform:
+ description: The hardware platform reported by the remote device
+ returned: when legacy is configured
+ type: str
+power_supply_info:
+ description: A hash of facts about the power supplies in the remote device
+ returned: when legacy is configured
+ type: str
+vlan_list:
+ description: The list of VLAN IDs configured on the remote device
+ returned: when legacy is configured
+ type: list
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.facts.facts import (
+ FactsArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.facts.facts import (
+ Facts,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: ansible_facts
+ """
+ argument_spec = FactsArgs.argument_spec
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = []
+ if module.params["gather_subset"] == "!config":
+ warnings.append(
+ "default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards"
+ )
+
+ result = Facts(module).get_facts()
+
+ ansible_facts, additional_warnings = result
+ warnings.extend(additional_warnings)
+
+ module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_feature.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_feature.py
new file mode 100644
index 00000000..77a7e3c7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_feature.py
@@ -0,0 +1,285 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_feature
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manage features in NX-OS switches.
+description:
+- Offers ability to enable and disable features in NX-OS.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+options:
+ feature:
+ description:
+ - Name of feature.
+ required: true
+ type: str
+ state:
+ description:
+ - Desired state of the feature.
+ required: false
+ default: enabled
+ choices:
+ - enabled
+ - disabled
+ type: str
+"""
+
+EXAMPLES = """
+- name: Ensure lacp is enabled
+ cisco.nxos.nxos_feature:
+ feature: lacp
+ state: enabled
+
+- name: Ensure ospf is disabled
+ cisco.nxos.nxos_feature:
+ feature: ospf
+ state: disabled
+
+- name: Ensure vpc is enabled
+ cisco.nxos.nxos_feature:
+ feature: vpc
+ state: enabled
+"""
+
+RETURN = """
+commands:
+ description: The set of commands to be sent to the remote device
+ returned: always
+ type: list
+ sample: ['nv overlay evpn']
+"""
+
+import re
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.connection import ConnectionError
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+ get_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+ nxos_argument_spec,
+)
+
+
+def get_available_features(feature, module):
+ available_features = {}
+ feature_regex = r"(?P<feature>\S+)\s+\d+\s+(?P<state>.*)"
+ command = {"command": "show feature", "output": "text"}
+
+ try:
+ body = run_commands(module, [command])[0]
+ split_body = body.splitlines()
+ except (KeyError, IndexError):
+ return {}
+
+ for line in split_body:
+ try:
+ match_feature = re.match(feature_regex, line, re.DOTALL)
+ feature_group = match_feature.groupdict()
+ feature = feature_group["feature"]
+ state = feature_group["state"]
+ except AttributeError:
+ feature = ""
+ state = ""
+
+ if feature and state:
+ if "enabled" in state:
+ state = "enabled"
+
+ if feature not in available_features:
+ available_features[feature] = state
+ else:
+ if (
+ available_features[feature] == "disabled"
+ and state == "enabled"
+ ):
+ available_features[feature] = state
+
+ # certain configurable features do not
+ # show up in the output of "show feature"
+ # but appear in running-config when set
+ run_cfg = get_config(module, flags=["| include ^feature"])
+ for item in re.findall(r"feature\s(.*)", run_cfg):
+ if item not in available_features:
+ available_features[item] = "enabled"
+
+ if "fabric forwarding" not in available_features:
+ available_features["fabric forwarding"] = "disabled"
+
+ return available_features
+
+
+def get_commands(proposed, existing, state, module):
+ feature = validate_feature(module, mode="config")
+ commands = []
+ feature_check = proposed == existing
+ if not feature_check:
+ if state == "enabled":
+ command = "feature {0}".format(feature)
+ commands.append(command)
+ elif state == "disabled":
+ command = "no feature {0}".format(feature)
+ commands.append(command)
+ return commands
+
+
+def validate_feature(module, mode="show"):
+ """Some features may need to be mapped due to inconsistency
+ between how they appear from "show feature" output and
+ how they are configured"""
+
+ feature = module.params["feature"]
+
+ try:
+ info = get_capabilities(module)
+ device_info = info.get("device_info", {})
+ os_version = device_info.get("network_os_version", "")
+ except ConnectionError:
+ os_version = ""
+
+ if "8.1" in os_version:
+ feature_to_be_mapped = {
+ "show": {
+ "nv overlay": "nve",
+ "vn-segment-vlan-based": "vnseg_vlan",
+ "hsrp": "hsrp_engine",
+ "fabric multicast": "fabric_mcast",
+ "scp-server": "scpServer",
+ "sftp-server": "sftpServer",
+ "sla responder": "sla_responder",
+ "sla sender": "sla_sender",
+ "ssh": "sshServer",
+ "tacacs+": "tacacs",
+ "telnet": "telnetServer",
+ "ethernet-link-oam": "elo",
+ },
+ "config": {
+ "nve": "nv overlay",
+ "vnseg_vlan": "vn-segment-vlan-based",
+ "hsrp_engine": "hsrp",
+ "fabric_mcast": "fabric multicast",
+ "scpServer": "scp-server",
+ "sftpServer": "sftp-server",
+ "sla_sender": "sla sender",
+ "sla_responder": "sla responder",
+ "sshServer": "ssh",
+ "tacacs": "tacacs+",
+ "telnetServer": "telnet",
+ "elo": "ethernet-link-oam",
+ },
+ }
+ else:
+ feature_to_be_mapped = {
+ "show": {
+ "nv overlay": "nve",
+ "vn-segment-vlan-based": "vnseg_vlan",
+ "hsrp": "hsrp_engine",
+ "fabric multicast": "fabric_mcast",
+ "scp-server": "scpServer",
+ "sftp-server": "sftpServer",
+ "sla responder": "sla_responder",
+ "sla sender": "sla_sender",
+ "ssh": "sshServer",
+ "tacacs+": "tacacs",
+ "telnet": "telnetServer",
+ "ethernet-link-oam": "elo",
+ "port-security": "eth_port_sec",
+ },
+ "config": {
+ "nve": "nv overlay",
+ "vnseg_vlan": "vn-segment-vlan-based",
+ "hsrp_engine": "hsrp",
+ "fabric_mcast": "fabric multicast",
+ "scpServer": "scp-server",
+ "sftpServer": "sftp-server",
+ "sla_sender": "sla sender",
+ "sla_responder": "sla responder",
+ "sshServer": "ssh",
+ "tacacs": "tacacs+",
+ "telnetServer": "telnet",
+ "elo": "ethernet-link-oam",
+ "eth_port_sec": "port-security",
+ },
+ }
+
+ if feature in feature_to_be_mapped[mode]:
+ feature = feature_to_be_mapped[mode][feature]
+
+ return feature
+
+
+def main():
+ argument_spec = dict(
+ feature=dict(type="str", required=True),
+ state=dict(choices=["enabled", "disabled"], default="enabled"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = dict(changed=False, warnings=warnings)
+
+ feature = validate_feature(module)
+ state = module.params["state"].lower()
+
+ available_features = get_available_features(feature, module)
+ if feature not in available_features:
+ module.fail_json(
+ msg="Invalid feature name.",
+ features_currently_supported=available_features,
+ invalid_feature=feature,
+ )
+ else:
+ existstate = available_features[feature]
+
+ existing = dict(state=existstate)
+ proposed = dict(state=state)
+ results["changed"] = False
+
+ cmds = get_commands(proposed, existing, state, module)
+
+ if cmds:
+ # On N35 A8 images, some features return a yes/no prompt
+ # on enablement or disablement. Bypass using terminal dont-ask
+ cmds.insert(0, "terminal dont-ask")
+ if not module.check_mode:
+ load_config(module, cmds)
+ results["changed"] = True
+
+ results["commands"] = cmds
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_file_copy.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_file_copy.py
new file mode 100644
index 00000000..d338a201
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_file_copy.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+DOCUMENTATION = """
+module: nxos_file_copy
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Copy a file to a remote NXOS device.
+description:
+- This module supports two different workflows for copying a file to flash (or bootflash)
+ on NXOS devices. Files can either be (1) pushed from the Ansible controller to
+ the device or (2) pulled from a remote SCP file server to the device. File copies
+ are initiated from the NXOS device to the remote SCP server. This module only supports
+ the use of connection C(network_cli) or C(Cli) transport with connection C(local).
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+- Rewritten as a plugin by (@mikewiebe)
+notes:
+- Tested against NXOS 7.0(3)I2(5), 7.0(3)I4(6), 7.0(3)I5(3), 7.0(3)I6(1), 7.0(3)I7(3),
+ 6.0(2)A8(8), 7.0(3)F3(4), 7.3(0)D1(1), 8.3(0), 9.2, 9.3
+- When pushing files (file_pull is False) to the NXOS device, feature scp-server must
+ be enabled.
+- When pulling files (file_pull is True) to the NXOS device, feature scp-server is
+ not required.
+- When pulling files (file_pull is True) to the NXOS device, no transfer will take
+ place if the file is already present.
+- Check mode will tell you if the file would be copied.
+requirements:
+- paramiko (required when file_pull is False)
+- SCPClient (required when file_pull is False)
+- pexpect (required when file_pull is True)
+options:
+ local_file:
+ description:
+ - When (file_pull is False) this is the path to the local file on the Ansible
+ controller. The local directory must exist.
+ - When (file_pull is True) this is the target file name on the NXOS device.
+ remote_file:
+ description:
+ - When (file_pull is False) this is the remote file path on the NXOS device. If
+ omitted, the name of the local file will be used. The remote directory must
+ exist.
+ - When (file_pull is True) this is the full path to the file on the remote SCP
+ server to be copied to the NXOS device.
+ file_system:
+ description:
+ - The remote file system on the nxos device. If omitted, devices that support
+ a I(file_system) parameter will use their default values.
+ default: 'bootflash:'
+ connect_ssh_port:
+ description:
+ - SSH server port used for file transfer.
+ default: 22
+ file_pull:
+ description:
+ - When (False) file is copied from the Ansible controller to the NXOS device.
+ - When (True) file is copied from a remote SCP server to the NXOS device. In this
+ mode, the file copy is initiated from the NXOS device.
+ - If the file is already present on the device it will be overwritten and therefore
+ the operation is NOT idempotent.
+ type: bool
+ default: false
+ file_pull_protocol:
+ description:
+ - When file_pull is True, this can be used to define the transfer protocol for
+ copying file from remote to the NXOS device.
+ - When (file_pull is False), this is not used.
+ default: 'scp'
+ choices:
+ - scp
+ - sftp
+ - ftp
+ - http
+ - https
+ - tftp
+ type: str
+ file_pull_compact:
+ description:
+ - When file_pull is True, this is used to compact nxos image files. This option
+ can only be used with nxos image files.
+ - When (file_pull is False), this is not used.
+ type: bool
+ default: false
+ file_pull_kstack:
+ description:
+ - When file_pull is True, this can be used to speed up file copies when the nxos
+ running image supports the use-kstack option.
+ - When (file_pull is False), this is not used.
+ type: bool
+ default: false
+ local_file_directory:
+ description:
+ - When (file_pull is True) file is copied from a remote SCP server to the NXOS
+ device, and written to this directory on the NXOS device. If the directory does
+ not exist, it will be created under the file_system. This is an optional parameter.
+ - When (file_pull is False), this is not used.
+ file_pull_timeout:
+ description:
+ - Use this parameter to set timeout in seconds, when transferring large files
+ or when the network is slow.
+ - When (file_pull is False), this is not used.
+ default: 300
+ remote_scp_server:
+ description:
+ - The remote scp server address when file_pull is True. This is required if file_pull
+ is True.
+ - When (file_pull is False), this is not used.
+ remote_scp_server_user:
+ description:
+ - The remote scp server username when file_pull is True. This is required if file_pull
+ is True.
+ - When (file_pull is False), this is not used.
+ remote_scp_server_password:
+ description:
+ - The remote scp server password when file_pull is True. This is required if file_pull
+ is True.
+ - When (file_pull is False), this is not used.
+ vrf:
+ description:
+ - The VRF used to pull the file. Useful when no vrf management is defined
+ default: management
+"""
+
+EXAMPLES = """
+# File copy from ansible controller to nxos device
+- name: copy from server to device
+ cisco.nxos.nxos_file_copy:
+ local_file: ./test_file.txt
+ remote_file: test_file.txt
+
+# Initiate file copy from the nxos device to transfer file from an SCP server back to the nxos device
+- name: initiate file copy from device
+ cisco.nxos.nxos_file_copy:
+ file_pull: true
+ local_file: xyz
+ local_file_directory: dir1/dir2/dir3
+ remote_file: /mydir/abc
+ remote_scp_server: 192.168.0.1
+ remote_scp_server_user: myUser
+ remote_scp_server_password: myPassword
+ vrf: management
+
+# Initiate file copy from the nxos device to transfer file from a ftp server back to the nxos device.
+# remote_scp_server_user and remote_scp_server_password are used to login to the FTP server.
+- name: initiate file copy from device
+ cisco.nxos.nxos_file_copy:
+ file_pull: true
+ file_pull_protocol: ftp
+ local_file: xyz
+ remote_file: /mydir/abc
+ remote_scp_server: 192.168.0.1
+ remote_scp_server_user: myUser
+ remote_scp_server_password: myPassword
+ vrf: management
+"""
+
+RETURN = """
+transfer_status:
+ description: Whether a file was transferred to the nxos device.
+ returned: success
+ type: str
+ sample: 'Sent'
+local_file:
+ description: The path of the local file.
+ returned: success
+ type: str
+ sample: '/path/to/local/file'
+remote_file:
+ description: The path of the remote file.
+ returned: success
+ type: str
+ sample: '/path/to/remote/file'
+remote_scp_server:
+ description: The name of the scp server when file_pull is True.
+ returned: success
+ type: str
+ sample: 'fileserver.example.com'
+changed:
+ description: Indicates whether or not the file was copied.
+ returned: success
+ type: bool
+ sample: true
+"""
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir.py
new file mode 100644
index 00000000..88912af4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir.py
@@ -0,0 +1,371 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_gir
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Trigger a graceful removal or insertion (GIR) of the switch.
+description:
+- Trigger a graceful removal or insertion (GIR) of the switch.
+- GIR processing may take more than 2 minutes. Timeout settings are automatically
+ extended to 200s when user timeout settings are insufficient.
+version_added: 1.0.0
+author:
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state) has effect only in combination with C(system_mode_maintenance_timeout)
+ or C(system_mode_maintenance_on_reload_reset_reason).
+- Using C(system_mode_maintenance) and C(system_mode_maintenance_dont_generate_profile)
+ would make the module fail, but the system mode will be triggered anyway.
+options:
+ system_mode_maintenance:
+ description:
+ - When C(system_mode_maintenance=true) it puts all enabled protocols in maintenance
+ mode (using the isolate command). When C(system_mode_maintenance=false) it puts
+ all enabled protocols in normal mode (using the no isolate command).
+ type: bool
+ system_mode_maintenance_dont_generate_profile:
+ description:
+ - When C(system_mode_maintenance_dont_generate_profile=true) it prevents the dynamic
+ searching of enabled protocols and executes commands configured in a maintenance-mode
+ profile. Use this option if you want the system to use a maintenance-mode profile
+ that you have created. When C(system_mode_maintenance_dont_generate_profile=false)
+ it prevents the dynamic searching of enabled protocols and executes commands
+ configured in a normal-mode profile. Use this option if you want the system
+ to use a normal-mode profile that you have created.
+ type: bool
+ system_mode_maintenance_timeout:
+ description:
+ - Keeps the switch in maintenance mode for a specified number of minutes. Range
+ is 5-65535.
+ type: str
+ system_mode_maintenance_shutdown:
+ description:
+ - Shuts down all protocols, vPC domains, and interfaces except the management
+ interface (using the shutdown command). This option is disruptive while C(system_mode_maintenance)
+ (which uses the isolate command) is not.
+ type: bool
+ system_mode_maintenance_on_reload_reset_reason:
+ description:
+ - Boots the switch into maintenance mode automatically in the event of a specified
+ system crash. Note that not all reset reasons are applicable for all platforms.
+ Also if reset reason is set to match_any, it is not idempotent as it turns on
+ all reset reasons. If reset reason is match_any and state is absent, it turns
+ off all the reset reasons.
+ choices:
+ - hw_error
+ - svc_failure
+ - kern_failure
+ - wdog_timeout
+ - fatal_error
+ - lc_failure
+ - match_any
+ - manual_reload
+ - any_other
+ - maintenance
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# Trigger system maintenance mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance: true
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+# Trigger system normal mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance: false
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+# Configure on-reload reset-reason for maintenance mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance_on_reload_reset_reason: manual_reload
+ state: present
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+# Add on-reload reset-reason for maintenance mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance_on_reload_reset_reason: hw_error
+ state: present
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+# Remove on-reload reset-reason for maintenance mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance_on_reload_reset_reason: manual_reload
+ state: absent
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+# Set timeout for maintenance mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance_timeout: 30
+ state: present
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+# Remove timeout for maintenance mode
+- cisco.nxos.nxos_gir:
+ system_mode_maintenance_timeout: 30
+ state: absent
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+final_system_mode:
+ description: describe the last system mode
+ returned: verbose mode
+ type: str
+ sample: normal
+updates:
+ description: commands sent to the device
+ returned: verbose mode
+ type: list
+ sample: ["terminal dont-ask", "system mode maintenance timeout 10"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_system_mode(module):
+ command = {"command": "show system mode", "output": "text"}
+ body = run_commands(module, [command])[0]
+ if body and "normal" in body.lower():
+ mode = "normal"
+ else:
+ mode = "maintenance"
+ return mode
+
+
+def get_maintenance_timeout(module):
+ command = {"command": "show maintenance timeout", "output": "text"}
+ body = run_commands(module, [command])[0]
+ timeout = body.split()[4]
+ return timeout
+
+
+def get_reset_reasons(module):
+ command = {
+ "command": "show maintenance on-reload reset-reasons",
+ "output": "text",
+ }
+ body = run_commands(module, [command])[0]
+ return body
+
+
+def get_commands(module, state, mode):
+ commands = list()
+ if module.params["system_mode_maintenance"] is True and mode == "normal":
+ commands.append("system mode maintenance")
+ elif (
+ module.params["system_mode_maintenance"] is False
+ and mode == "maintenance"
+ ):
+ commands.append("no system mode maintenance")
+
+ elif (
+ module.params["system_mode_maintenance_dont_generate_profile"] is True
+ and mode == "normal"
+ ):
+ commands.append("system mode maintenance dont-generate-profile")
+ elif (
+ module.params["system_mode_maintenance_dont_generate_profile"] is False
+ and mode == "maintenance"
+ ):
+ commands.append("no system mode maintenance dont-generate-profile")
+
+ elif module.params["system_mode_maintenance_timeout"]:
+ timeout = get_maintenance_timeout(module)
+ if (
+ state == "present"
+ and timeout != module.params["system_mode_maintenance_timeout"]
+ ):
+ commands.append(
+ "system mode maintenance timeout {0}".format(
+ module.params["system_mode_maintenance_timeout"]
+ )
+ )
+ elif (
+ state == "absent"
+ and timeout == module.params["system_mode_maintenance_timeout"]
+ ):
+ commands.append(
+ "no system mode maintenance timeout {0}".format(
+ module.params["system_mode_maintenance_timeout"]
+ )
+ )
+
+ elif (
+ module.params["system_mode_maintenance_shutdown"] and mode == "normal"
+ ):
+ commands.append("system mode maintenance shutdown")
+ elif (
+ module.params["system_mode_maintenance_shutdown"] is False
+ and mode == "maintenance"
+ ):
+ commands.append("no system mode maintenance")
+
+ elif module.params["system_mode_maintenance_on_reload_reset_reason"]:
+ reset_reasons = get_reset_reasons(module)
+ if (
+ state == "present"
+ and module.params[
+ "system_mode_maintenance_on_reload_reset_reason"
+ ].lower()
+ not in reset_reasons.lower()
+ ):
+ commands.append(
+ "system mode maintenance on-reload "
+ "reset-reason {0}".format(
+ module.params[
+ "system_mode_maintenance_on_reload_reset_reason"
+ ]
+ )
+ )
+ elif (
+ state == "absent"
+ and module.params[
+ "system_mode_maintenance_on_reload_reset_reason"
+ ].lower()
+ in reset_reasons.lower()
+ ):
+ commands.append(
+ "no system mode maintenance on-reload "
+ "reset-reason {0}".format(
+ module.params[
+ "system_mode_maintenance_on_reload_reset_reason"
+ ]
+ )
+ )
+
+ if commands:
+ commands.insert(0, "terminal dont-ask")
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ system_mode_maintenance=dict(required=False, type="bool"),
+ system_mode_maintenance_dont_generate_profile=dict(
+ required=False, type="bool"
+ ),
+ system_mode_maintenance_timeout=dict(required=False, type="str"),
+ system_mode_maintenance_shutdown=dict(required=False, type="bool"),
+ system_mode_maintenance_on_reload_reset_reason=dict(
+ required=False,
+ choices=[
+ "hw_error",
+ "svc_failure",
+ "kern_failure",
+ "wdog_timeout",
+ "fatal_error",
+ "lc_failure",
+ "match_any",
+ "manual_reload",
+ "any_other",
+ "maintenance",
+ ],
+ ),
+ state=dict(
+ choices=["absent", "present"], default="present", required=False
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[
+ [
+ "system_mode_maintenance",
+ "system_mode_maintenance_dont_generate_profile",
+ "system_mode_maintenance_timeout",
+ "system_mode_maintenance_shutdown",
+ "system_mode_maintenance_on_reload_reset_reason",
+ ]
+ ],
+ required_one_of=[
+ [
+ "system_mode_maintenance",
+ "system_mode_maintenance_dont_generate_profile",
+ "system_mode_maintenance_timeout",
+ "system_mode_maintenance_shutdown",
+ "system_mode_maintenance_on_reload_reset_reason",
+ ]
+ ],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+
+ state = module.params["state"]
+ mode = get_system_mode(module)
+ commands = get_commands(module, state, mode)
+ changed = False
+ if commands:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=commands)
+ else:
+ load_config(module, commands)
+ changed = True
+
+ result = {}
+ result["changed"] = changed
+ if module._verbosity > 0:
+ final_system_mode = get_system_mode(module)
+ result["final_system_mode"] = final_system_mode
+ result["updates"] = commands
+
+ result["warnings"] = warnings
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir_profile_management.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir_profile_management.py
new file mode 100644
index 00000000..298c45da
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_gir_profile_management.py
@@ -0,0 +1,218 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_gir_profile_management
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Create a maintenance-mode or normal-mode profile for GIR.
+description:
+- Manage a maintenance-mode or normal-mode profile with configuration commands that
+ can be applied during graceful removal or graceful insertion.
+version_added: 1.0.0
+author:
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the whole profile.
+options:
+ commands:
+ description:
+ - List of commands to be included into the profile.
+ type: list
+ elements: str
+ mode:
+ description:
+ - Configure the profile as Maintenance or Normal mode.
+ required: true
+ choices:
+ - maintenance
+ - normal
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# Create a maintenance-mode profile
+- cisco.nxos.nxos_gir_profile_management:
+ mode: maintenance
+ commands:
+ - router eigrp 11
+ - isolate
+
+# Remove the maintenance-mode profile
+- cisco.nxos.nxos_gir_profile_management:
+ mode: maintenance
+ state: absent
+"""
+
+RETURN = """
+proposed:
+ description: list of commands passed into module.
+ returned: verbose mode
+ type: list
+ sample: ["router eigrp 11", "isolate"]
+existing:
+ description: list of existing profile commands.
+ returned: verbose mode
+ type: list
+ sample: ["router bgp 65535","isolate","router eigrp 10","isolate",
+ "diagnostic bootup level complete"]
+end_state:
+ description: list of profile entries after module execution.
+ returned: verbose mode
+ type: list
+ sample: ["router bgp 65535","isolate","router eigrp 10","isolate",
+ "diagnostic bootup level complete","router eigrp 11", "isolate"]
+updates:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["configure maintenance profile maintenance-mode",
+ "router eigrp 11","isolate"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+def get_existing(module):
+ existing = []
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+
+ if module.params["mode"] == "maintenance":
+ parents = ["configure maintenance profile maintenance-mode"]
+ else:
+ parents = ["configure maintenance profile normal-mode"]
+
+ config = netcfg.get_section(parents)
+ if config:
+ existing = config.splitlines()
+ existing = [cmd.strip() for cmd in existing]
+ existing.pop(0)
+
+ return existing
+
+
+def state_present(module, existing, commands):
+ cmds = list()
+ if existing == commands:
+ # Idempotent case
+ return cmds
+ cmds.extend(commands)
+ if module.params["mode"] == "maintenance":
+ cmds.insert(0, "configure maintenance profile maintenance-mode")
+ else:
+ cmds.insert(0, "configure maintenance profile normal-mode")
+
+ return cmds
+
+
+def state_absent(module, existing, commands):
+ if module.params["mode"] == "maintenance":
+ cmds = ["no configure maintenance profile maintenance-mode"]
+ else:
+ cmds = ["no configure maintenance profile normal-mode"]
+ return cmds
+
+
+def invoke(name, *args, **kwargs):
+ func = globals().get(name)
+ if func:
+ return func(*args, **kwargs)
+
+
+def main():
+ argument_spec = dict(
+ commands=dict(required=False, type="list", elements="str"),
+ mode=dict(required=True, choices=["maintenance", "normal"]),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ state = module.params["state"]
+ commands = module.params["commands"] or []
+
+ if state == "absent" and commands:
+ module.fail_json(msg="when state is absent, no command can be used.")
+
+ existing = invoke("get_existing", module)
+ end_state = existing
+ changed = False
+
+ result = {}
+ cmds = []
+ if state == "present" or (state == "absent" and existing):
+ cmds = invoke("state_%s" % state, module, existing, commands)
+
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ if cmds:
+ load_config(module, cmds)
+ changed = True
+ end_state = invoke("get_existing", module)
+
+ result["changed"] = changed
+ if module._verbosity > 0:
+ end_state = invoke("get_existing", module)
+ result["end_state"] = end_state
+ result["existing"] = existing
+ result["proposed"] = commands
+ result["updates"] = cmds
+
+ result["warnings"] = warnings
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp.py
new file mode 100644
index 00000000..9d051e12
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp.py
@@ -0,0 +1,537 @@
+#!/usr/bin/python
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+DOCUMENTATION = """
+module: nxos_hsrp
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages HSRP configuration on NX-OS switches.
+description:
+- Manages HSRP configuration on NX-OS switches.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- HSRP feature needs to be enabled first on the system.
+- SVIs must exist before using this module.
+- Interface must be a L3 port before using this module.
+- HSRP cannot be configured on loopback interfaces.
+- MD5 authentication is only possible with HSRPv2 while it is ignored if HSRPv1 is
+ used instead, while it will not raise any error. Here we allow MD5 authentication
+ only with HSRPv2 in order to enforce better practice.
+options:
+ group:
+ description:
+ - HSRP group number.
+ required: true
+ type: str
+ interface:
+ description:
+ - Full name of interface that is being managed for HSRP.
+ required: true
+ type: str
+ version:
+ description:
+ - HSRP version.
+ default: 1
+ choices:
+ - '1'
+ - '2'
+ type: str
+ priority:
+ description:
+ - HSRP priority or keyword 'default'.
+ type: str
+ preempt:
+ description:
+ - Enable/Disable preempt.
+ choices:
+ - enabled
+ - disabled
+ type: str
+ vip:
+ description:
+ - HSRP virtual IP address or keyword 'default'
+ type: str
+ auth_string:
+ description:
+ - Authentication string. If this needs to be hidden(for md5 type), the string
+ should be 7 followed by the key string. Otherwise, it can be 0 followed by key
+ string or just key string (for backward compatibility). For text type, this
+ should be just be a key string. if this is 'default', authentication is removed.
+ type: str
+ auth_type:
+ description:
+ - Authentication type.
+ choices:
+ - text
+ - md5
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ choices:
+ - present
+ - absent
+ default: present
+ type: str
+"""
+
+EXAMPLES = """
+- name: Ensure HSRP is configured with following params on a SVI
+ cisco.nxos.nxos_hsrp:
+ group: 10
+ vip: 10.1.1.1
+ priority: 150
+ interface: vlan10
+ preempt: enabled
+
+- name: Ensure HSRP is configured with following params on a SVI with clear text authentication
+ cisco.nxos.nxos_hsrp:
+ group: 10
+ vip: 10.1.1.1
+ priority: 150
+ interface: vlan10
+ preempt: enabled
+ auth_type: text
+ auth_string: CISCO
+
+- name: Ensure HSRP is configured with md5 authentication and clear authentication
+ string
+ cisco.nxos.nxos_hsrp:
+ group: 10
+ vip: 10.1.1.1
+ priority: 150
+ interface: vlan10
+ preempt: enabled
+ auth_type: md5
+ auth_string: 0 1234
+
+- name: Ensure HSRP is configured with md5 authentication and hidden authentication
+ string
+ cisco.nxos.nxos_hsrp:
+ group: 10
+ vip: 10.1.1.1
+ priority: 150
+ interface: vlan10
+ preempt: enabled
+ auth_type: md5
+ auth_string: 7 1234
+
+- name: Remove HSRP config for given interface, group, and VIP
+ cisco.nxos.nxos_hsrp:
+ group: 10
+ interface: vlan10
+ vip: 10.1.1.1
+ state: absent
+"""
+
+RETURN = r"""
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface vlan10", "hsrp version 2", "hsrp 30", "ip 10.30.1.1"]
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_interface_type,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+PARAM_TO_DEFAULT_KEYMAP = {
+ "vip": None,
+ "priority": "100",
+ "auth_type": "text",
+ "auth_string": "cisco",
+}
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key in table:
+ new_key = key_map.get(key)
+ if new_key:
+ value = table.get(key)
+ if value:
+ new_dict[new_key] = str(value)
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def get_interface_mode(interface, intf_type, module):
+ command = "show interface {0} | json".format(interface)
+ interface = {}
+ mode = "unknown"
+ try:
+ body = run_commands(module, [command])[0]
+ except IndexError:
+ return None
+
+ if intf_type in ["ethernet", "portchannel"]:
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ mode = str(interface_table.get("eth_mode", "layer3"))
+ if mode == "access" or mode == "trunk":
+ mode = "layer2"
+ elif intf_type == "svi":
+ mode = "layer3"
+ return mode
+
+
+def get_hsrp_group(group, interface, module):
+ command = "show hsrp group {0} all | json".format(group)
+ hsrp = {}
+
+ hsrp_key = {
+ "sh_if_index": "interface",
+ "sh_group_num": "group",
+ "sh_group_version": "version",
+ "sh_cfg_prio": "priority",
+ "sh_preempt": "preempt",
+ "sh_vip": "vip",
+ "sh_authentication_type": "auth_type",
+ "sh_keystring_attr": "auth_enc",
+ "sh_authentication_data": "auth_string",
+ }
+
+ try:
+ body = run_commands(module, [command])[0]
+ hsrp_table = body["TABLE_grp_detail"]["ROW_grp_detail"]
+ if "sh_keystring_attr" not in hsrp_table:
+ del hsrp_key["sh_keystring_attr"]
+ if "unknown enum:" in str(hsrp_table):
+ hsrp_table = get_hsrp_group_unknown_enum(
+ module, command, hsrp_table
+ )
+ except (AttributeError, IndexError, TypeError, KeyError):
+ return {}
+
+ if isinstance(hsrp_table, dict):
+ hsrp_table = [hsrp_table]
+
+ for hsrp_group in hsrp_table:
+ parsed_hsrp = apply_key_map(hsrp_key, hsrp_group)
+
+ parsed_hsrp["interface"] = parsed_hsrp["interface"].lower()
+
+ if parsed_hsrp["version"] == "v1":
+ parsed_hsrp["version"] = "1"
+ elif parsed_hsrp["version"] == "v2":
+ parsed_hsrp["version"] = "2"
+
+ if parsed_hsrp["auth_type"] == "md5":
+ if parsed_hsrp["auth_enc"] == "hidden":
+ parsed_hsrp["auth_enc"] = "7"
+ else:
+ parsed_hsrp["auth_enc"] = "0"
+
+ if parsed_hsrp["interface"] == interface:
+ return parsed_hsrp
+
+ return hsrp
+
+
+def get_hsrp_group_unknown_enum(module, command, hsrp_table):
+ """Some older NXOS images fail to set the attr values when using structured output and
+ instead set the values to <unknown enum>. This fallback method is a workaround that
+ uses an unstructured (text) request to query the device a second time.
+ 'sh_preempt' is currently the only attr affected. Add checks for other attrs as needed.
+ """
+ if "unknown enum:" in hsrp_table["sh_preempt"]:
+ cmd = {"output": "text", "command": command.split("|")[0]}
+ out = run_commands(module, cmd)[0]
+ hsrp_table["sh_preempt"] = (
+ "enabled" if ("may preempt" in out) else "disabled"
+ )
+ return hsrp_table
+
+
+def get_commands_remove_hsrp(group, interface):
+ commands = ["interface {0}".format(interface), "no hsrp {0}".format(group)]
+ return commands
+
+
+def get_commands_config_hsrp(delta, interface, args, existing):
+ commands = []
+
+ config_args = {
+ "group": "hsrp {group}",
+ "priority": "{priority}",
+ "preempt": "{preempt}",
+ "vip": "{vip}",
+ }
+
+ preempt = delta.get("preempt", None)
+ group = delta.get("group", None)
+ vip = delta.get("vip", None)
+ priority = delta.get("priority", None)
+
+ if preempt:
+ if preempt == "enabled":
+ delta["preempt"] = "preempt"
+ elif preempt == "disabled":
+ delta["preempt"] = "no preempt"
+
+ if priority:
+ if priority == "default":
+ if existing and existing.get(
+ "priority"
+ ) != PARAM_TO_DEFAULT_KEYMAP.get("priority"):
+ delta["priority"] = "no priority"
+ else:
+ del delta["priority"]
+ else:
+ delta["priority"] = "priority {0}".format(delta["priority"])
+
+ if vip:
+ if vip == "default":
+ if existing and existing.get("vip") != PARAM_TO_DEFAULT_KEYMAP.get(
+ "vip"
+ ):
+ delta["vip"] = "no ip"
+ else:
+ del delta["vip"]
+ else:
+ delta["vip"] = "ip {0}".format(delta["vip"])
+
+ for key in delta:
+ command = config_args.get(key, "DNE").format(**delta)
+ if command and command != "DNE":
+ if key == "group":
+ commands.insert(0, command)
+ else:
+ commands.append(command)
+ command = None
+
+ auth_type = delta.get("auth_type", None)
+ auth_string = delta.get("auth_string", None)
+ auth_enc = delta.get("auth_enc", None)
+ if auth_type or auth_string:
+ if not auth_type:
+ auth_type = args["auth_type"]
+ elif not auth_string:
+ auth_string = args["auth_string"]
+ if auth_string != "default":
+ if auth_type == "md5":
+ command = "authentication md5 key-string {0} {1}".format(
+ auth_enc, auth_string
+ )
+ commands.append(command)
+ elif auth_type == "text":
+ command = "authentication text {0}".format(auth_string)
+ commands.append(command)
+ else:
+ if existing and existing.get(
+ "auth_string"
+ ) != PARAM_TO_DEFAULT_KEYMAP.get("auth_string"):
+ commands.append("no authentication")
+
+ if commands and not group:
+ commands.insert(0, "hsrp {0}".format(args["group"]))
+
+ version = delta.get("version", None)
+ if version:
+ if version == "2":
+ command = "hsrp version 2"
+ elif version == "1":
+ command = "hsrp version 1"
+ commands.insert(0, command)
+ commands.insert(0, "interface {0}".format(interface))
+
+ if commands:
+ if not commands[0].startswith("interface"):
+ commands.insert(0, "interface {0}".format(interface))
+
+ return commands
+
+
+def is_default(interface, module):
+ command = "show run interface {0}".format(interface)
+
+ try:
+ body = run_commands(module, [command], check_rc=False)[0]
+ if "invalid" in body.lower():
+ return "DNE"
+ else:
+ raw_list = body.split("\n")
+ if raw_list[-1].startswith("interface"):
+ return True
+ else:
+ return False
+ except (KeyError):
+ return "DNE"
+
+
+def validate_config(body, vip, module):
+ new_body = "".join(body)
+ if "invalid ip address" in new_body.lower():
+ module.fail_json(
+ msg="Invalid VIP. Possible duplicate IP address.", vip=vip
+ )
+
+
+def main():
+ argument_spec = dict(
+ group=dict(required=True, type="str"),
+ interface=dict(required=True),
+ version=dict(choices=["1", "2"], default="1", required=False),
+ priority=dict(type="str", required=False),
+ preempt=dict(
+ type="str", choices=["disabled", "enabled"], required=False
+ ),
+ vip=dict(type="str", required=False),
+ auth_type=dict(choices=["text", "md5"], required=False),
+ auth_string=dict(type="str", required=False),
+ state=dict(
+ choices=["absent", "present"], required=False, default="present"
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = dict(changed=False, warnings=warnings)
+
+ interface = module.params["interface"].lower()
+ group = module.params["group"]
+ version = module.params["version"]
+ state = module.params["state"]
+ priority = module.params["priority"]
+ preempt = module.params["preempt"]
+ vip = module.params["vip"]
+ auth_type = module.params["auth_type"]
+ auth_full_string = module.params["auth_string"]
+ auth_enc = "0"
+ auth_string = None
+ if auth_full_string:
+ kstr = auth_full_string.split()
+ if len(kstr) == 2:
+ auth_enc = kstr[0]
+ auth_string = kstr[1]
+ elif len(kstr) == 1:
+ auth_string = kstr[0]
+ else:
+ module.fail_json(msg="Invalid auth_string")
+ if auth_enc != "0" and auth_enc != "7":
+ module.fail_json(msg="Invalid auth_string, only 0 or 7 allowed")
+
+ device_info = get_capabilities(module)
+ network_api = device_info.get("network_api", "nxapi")
+
+ intf_type = get_interface_type(interface)
+ if intf_type != "ethernet" and network_api == "cliconf":
+ if is_default(interface, module) == "DNE":
+ module.fail_json(
+ msg="That interface does not exist yet. Create " "it first.",
+ interface=interface,
+ )
+ if intf_type == "loopback":
+ module.fail_json(
+ msg="Loopback interfaces don't support HSRP.",
+ interface=interface,
+ )
+
+ mode = get_interface_mode(interface, intf_type, module)
+ if mode == "layer2":
+ module.fail_json(
+ msg="That interface is a layer2 port.\nMake it "
+ "a layer 3 port first.",
+ interface=interface,
+ )
+
+ if auth_type or auth_string:
+ if not (auth_type and auth_string):
+ module.fail_json(
+ msg="When using auth parameters, you need BOTH "
+ "auth_type AND auth_string."
+ )
+
+ args = dict(
+ group=group,
+ version=version,
+ priority=priority,
+ preempt=preempt,
+ vip=vip,
+ auth_type=auth_type,
+ auth_string=auth_string,
+ auth_enc=auth_enc,
+ )
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ existing = get_hsrp_group(group, interface, module)
+
+ # This will enforce better practice with md5 and hsrp version.
+ if proposed.get("auth_type", None) == "md5":
+ if proposed["version"] == "1":
+ module.fail_json(
+ msg="It's recommended to use HSRP v2 " "when auth_type=md5"
+ )
+
+ elif not proposed.get("auth_type", None) and existing:
+ if (
+ proposed["version"] == "1" and existing["auth_type"] == "md5"
+ ) and state == "present":
+ module.fail_json(
+ msg="Existing auth_type is md5. It's recommended "
+ "to use HSRP v2 when using md5"
+ )
+
+ commands = []
+ if state == "present":
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = get_commands_config_hsrp(
+ delta, interface, args, existing
+ )
+ commands.extend(command)
+
+ elif state == "absent":
+ if existing:
+ command = get_commands_remove_hsrp(group, interface)
+ commands.extend(command)
+
+ if commands:
+ if module.check_mode:
+ module.exit_json(**results)
+ else:
+ load_config(module, commands)
+
+ # validate IP
+ if network_api == "cliconf" and state == "present":
+ commands.insert(0, "config t")
+ body = run_commands(module, commands)
+ validate_config(body, vip, module)
+
+ results["changed"] = True
+
+ if "configure" in commands:
+ commands.pop(0)
+
+ results["commands"] = commands
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp_interfaces.py
new file mode 100644
index 00000000..3b42f4c6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_hsrp_interfaces.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_hsrp_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_hsrp_interfaces
+short_description: HSRP interfaces resource module
+description: Manages Hot Standby Router Protocol (HSRP) interface attributes.
+version_added: 1.0.0
+author: Chris Van Heuveln (@chrisvanheuveln)
+notes:
+- Tested against NX-OS 7.0(3)I5(1).
+- Feature bfd should be enabled for this module.
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section '^interface').
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: The provided configuration
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ type: str
+ description: The name of the interface.
+ bfd:
+ type: str
+ description:
+ - Enable/Disable HSRP Bidirectional Forwarding Detection (BFD) on the interface.
+ choices:
+ - enable
+ - disable
+ state:
+ description:
+ - The state the configuration should be left in
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using deleted
+
+- name: Configure hsrp attributes on interfaces
+ cisco.nxos.nxos_hsrp_interfaces:
+ config:
+ - name: Ethernet1/1
+ - name: Ethernet1/2
+ operation: deleted
+
+
+# Using merged
+
+- name: Configure hsrp attributes on interfaces
+ cisco.nxos.nxos_hsrp_interfaces:
+ config:
+ - name: Ethernet1/1
+ bfd: enable
+ - name: Ethernet1/2
+ bfd: disable
+ operation: merged
+
+
+# Using overridden
+
+- name: Configure hsrp attributes on interfaces
+ cisco.nxos.nxos_hsrp_interfaces:
+ config:
+ - name: Ethernet1/1
+ bfd: enable
+ - name: Ethernet1/2
+ bfd: disable
+ operation: overridden
+
+
+# Using replaced
+
+- name: Configure hsrp attributes on interfaces
+ cisco.nxos.nxos_hsrp_interfaces:
+ config:
+ - name: Ethernet1/1
+ bfd: enable
+ - name: Ethernet1/2
+ bfd: disable
+ operation: replaced
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_hsrp_interfaces:
+ config:
+ - name: Ethernet1/800
+ bfd: enable
+ - name: Ethernet1/801
+ bfd: enable
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/800"
+# - "hsrp bfd"
+# - "interface Ethernet1/801"
+# - "hsrp bfd"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# interface Ethernet1/800
+# no switchport
+# hsrp bfd
+# interface Ethernet1/801
+# no switchport
+# hsrp bfd
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_hsrp_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# - name: Ethernet1/800
+# bfd: enable
+# - name: Ethernet1/801
+# bfd: enable
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+
+# interface Ethernet1/1
+# no switchport
+# hsrp bfd
+# interface Ethernet1/2
+# no switchport
+# hsrp bfd
+# interface Ethernet1/3
+# no switchport
+
+- name: Gather hsrp_interfaces facts from the device using nxos_hsrp_interfaces
+ cisco.nxos.nxos_hsrp_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+
+# gathered:
+# - name: Ethernet1/1
+# bfd: enable
+# - name: Ethernet1/2
+# bfd: enable
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface Ethernet1/1', 'hsrp bfd']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.hsrp_interfaces.hsrp_interfaces import (
+ Hsrp_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.hsrp_interfaces.hsrp_interfaces import (
+ Hsrp_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=Hsrp_interfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Hsrp_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp.py
new file mode 100644
index 00000000..4909a877
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp.py
@@ -0,0 +1,168 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_igmp
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages IGMP global configuration.
+description:
+- Manages IGMP global configuration configuration settings.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- When C(state=default), all supported params will be reset to a default state.
+- If restart is set to true with other params set, the restart will happen last, i.e.
+ after the configuration takes place.
+options:
+ flush_routes:
+ description:
+ - Removes routes when the IGMP process is restarted. By default, routes are not
+ flushed.
+ type: bool
+ enforce_rtr_alert:
+ description:
+ - Enables or disables the enforce router alert option check for IGMPv2 and IGMPv3
+ packets.
+ type: bool
+ restart:
+ description:
+ - Restarts the igmp process (using an exec config command).
+ type: bool
+ default: False
+ state:
+ description:
+ - Manages desired state of the resource.
+ default: present
+ choices:
+ - present
+ - default
+ type: str
+"""
+EXAMPLES = """
+- name: Default igmp global params (all params except restart)
+ cisco.nxos.nxos_igmp:
+ state: default
+
+- name: Ensure the following igmp global config exists on the device
+ cisco.nxos.nxos_igmp:
+ flush_routes: true
+ enforce_rtr_alert: true
+
+- name: Restart the igmp process
+ cisco.nxos.nxos_igmp:
+ restart: true
+"""
+
+RETURN = """
+updates:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["ip igmp flush-routes"]
+"""
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_current(module):
+ output = run_commands(
+ module, {"command": "show running-config", "output": "text"}
+ )
+ return {
+ "flush_routes": "ip igmp flush-routes" in output[0],
+ "enforce_rtr_alert": "ip igmp enforce-router-alert" in output[0],
+ }
+
+
+def get_desired(module):
+ return {
+ "flush_routes": module.params["flush_routes"],
+ "enforce_rtr_alert": module.params["enforce_rtr_alert"],
+ }
+
+
+def main():
+ argument_spec = dict(
+ flush_routes=dict(type="bool"),
+ enforce_rtr_alert=dict(type="bool"),
+ restart=dict(type="bool", default=False),
+ state=dict(choices=["present", "default"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ current = get_current(module)
+ desired = get_desired(module)
+
+ state = module.params["state"]
+
+ commands = list()
+
+ if state == "default":
+ if current["flush_routes"]:
+ commands.append("no ip igmp flush-routes")
+ if current["enforce_rtr_alert"]:
+ commands.append("no ip igmp enforce-router-alert")
+
+ elif state == "present":
+ ldict = {
+ "flush_routes": "flush-routes",
+ "enforce_rtr_alert": "enforce-router-alert",
+ }
+ for arg in ["flush_routes", "enforce_rtr_alert"]:
+ if desired[arg] and not current[arg]:
+ commands.append("ip igmp {0}".format(ldict.get(arg)))
+ elif current[arg] and not desired[arg]:
+ commands.append("no ip igmp {0}".format(ldict.get(arg)))
+
+ result = {"changed": False, "updates": commands, "warnings": warnings}
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ if module.params["restart"]:
+ cmd = {"command": "restart igmp", "output": "text"}
+ run_commands(module, cmd)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_interface.py
new file mode 100644
index 00000000..07f6b18f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_interface.py
@@ -0,0 +1,687 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_igmp_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages IGMP interface configuration.
+description:
+- Manages IGMP interface configuration settings.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- When C(state=default), supported params will be reset to a default state. These
+ include C(version), C(startup_query_interval), C(startup_query_count), C(robustness),
+ C(querier_timeout), C(query_mrt), C(query_interval), C(last_member_qrt), C(last_member_query_count),
+ C(group_timeout), C(report_llg), and C(immediate_leave).
+- When C(state=absent), all configs for C(oif_ps), and C(oif_routemap) will be removed.
+- PIM must be enabled to use this module.
+- This module is for Layer 3 interfaces.
+- Route-map check not performed (same as CLI) check when configuring route-map with
+ 'static-oif'
+- If restart is set to true with other params set, the restart will happen last, i.e.
+ after the configuration takes place. However, 'restart' itself is not idempotent
+ as it is an action and not configuration.
+options:
+ interface:
+ description:
+ - The full interface name for IGMP configuration. e.g. I(Ethernet1/2).
+ required: true
+ type: str
+ version:
+ description:
+ - IGMP version. It can be 2 or 3 or keyword 'default'.
+ choices:
+ - '2'
+ - '3'
+ - default
+ type: str
+ startup_query_interval:
+ description:
+ - Query interval used when the IGMP process starts up. The range is from 1 to
+ 18000 or keyword 'default'. The default is 31.
+ type: str
+ startup_query_count:
+ description:
+ - Query count used when the IGMP process starts up. The range is from 1 to 10
+ or keyword 'default'. The default is 2.
+ type: str
+ robustness:
+ description:
+ - Sets the robustness variable. Values can range from 1 to 7 or keyword 'default'.
+ The default is 2.
+ type: str
+ querier_timeout:
+ description:
+ - Sets the querier timeout that the software uses when deciding to take over as
+ the querier. Values can range from 1 to 65535 seconds or keyword 'default'.
+ The default is 255 seconds.
+ type: str
+ query_mrt:
+ description:
+ - Sets the response time advertised in IGMP queries. Values can range from 1 to
+ 25 seconds or keyword 'default'. The default is 10 seconds.
+ type: str
+ query_interval:
+ description:
+ - Sets the frequency at which the software sends IGMP host query messages. Values
+ can range from 1 to 18000 seconds or keyword 'default'. The default is 125 seconds.
+ type: str
+ last_member_qrt:
+ description:
+ - Sets the query interval waited after sending membership reports before the software
+ deletes the group state. Values can range from 1 to 25 seconds or keyword 'default'.
+ The default is 1 second.
+ type: str
+ last_member_query_count:
+ description:
+ - Sets the number of times that the software sends an IGMP query in response to
+ a host leave message. Values can range from 1 to 5 or keyword 'default'. The
+ default is 2.
+ type: str
+ group_timeout:
+ description:
+ - Sets the group membership timeout for IGMPv2. Values can range from 3 to 65,535
+ seconds or keyword 'default'. The default is 260 seconds.
+ type: str
+ report_llg:
+ description:
+ - Configures report-link-local-groups. Enables sending reports for groups in 224.0.0.0/24.
+ Reports are always sent for nonlink local groups. By default, reports are not
+ sent for link local groups.
+ type: bool
+ immediate_leave:
+ description:
+ - Enables the device to remove the group entry from the multicast routing table
+ immediately upon receiving a leave message for the group. Use this command to
+ minimize the leave latency of IGMPv2 group memberships on a given IGMP interface
+ because the device does not send group-specific queries. The default is disabled.
+ type: bool
+ oif_routemap:
+ description:
+ - Configure a routemap for static outgoing interface (OIF) or keyword 'default'.
+ type: str
+ oif_ps:
+ description:
+ - Configure prefixes and sources for static outgoing interface (OIF). This is
+ a list of dict where each dict has source and prefix defined or just prefix
+ if source is not needed. The specified values will be configured on the device
+ and if any previous prefix/sources exist, they will be removed. Keyword 'default'
+ is also accepted which removes all existing prefix/sources.
+ type: raw
+ restart:
+ description:
+ - Restart IGMP. This is NOT idempotent as this is action only.
+ type: bool
+ default: false
+ state:
+ description:
+ - Manages desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ - default
+ type: str
+"""
+EXAMPLES = """
+- cisco.nxos.nxos_igmp_interface:
+ interface: ethernet1/32
+ startup_query_interval: 30
+ oif_ps:
+ - {prefix: 238.2.2.6}
+ - {source: 192.168.0.1, prefix: 238.2.2.5}
+ state: present
+"""
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"startup_query_count": "30",
+ "oif_ps": [{'prefix': '238.2.2.6'}, {'source': '192.168.0.1', 'prefix': '238.2.2.5'}]}
+existing:
+ description: k/v pairs of existing igmp_interface configuration
+ returned: always
+ type: dict
+ sample: {"startup_query_count": "2", "oif_ps": []}
+end_state:
+ description: k/v pairs of igmp interface configuration after module execution
+ returned: always
+ type: dict
+ sample: {"startup_query_count": "30",
+ "oif_ps": [{'prefix': '238.2.2.6'}, {'source': '192.168.0.1', 'prefix': '238.2.2.5'}]}
+updates:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface Ethernet1/32", "ip igmp startup-query-count 30",
+ "ip igmp static-oif 238.2.2.6", "ip igmp static-oif 238.2.2.5 source 192.168.0.1"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_interface_type,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+import re
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ if command_type == "cli_show_ascii":
+ cmds = [{"command": command, "output": "text"}]
+ else:
+ cmds = [{"command": command, "output": "json"}]
+
+ return run_commands(module, cmds)
+
+
+def get_interface_mode(interface, intf_type, module):
+ command = "show interface {0}".format(interface)
+ interface = {}
+ mode = "unknown"
+
+ if intf_type in ["ethernet", "portchannel"]:
+ body = execute_show_command(command, module)[0]
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ mode = str(interface_table.get("eth_mode", "layer3"))
+ if mode == "access" or mode == "trunk":
+ mode = "layer2"
+ elif intf_type == "loopback" or intf_type == "svi":
+ mode = "layer3"
+ return mode
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ value = table.get(key)
+ if value:
+ new_dict[new_key] = value
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_igmp_interface(module, interface):
+ command = "show ip igmp interface {0}".format(interface)
+ igmp = {}
+
+ key_map = {
+ "IGMPVersion": "version",
+ "ConfiguredStartupQueryInterval": "startup_query_interval",
+ "StartupQueryCount": "startup_query_count",
+ "RobustnessVariable": "robustness",
+ "ConfiguredQuerierTimeout": "querier_timeout",
+ "ConfiguredMaxResponseTime": "query_mrt",
+ "ConfiguredQueryInterval": "query_interval",
+ "LastMemberMTR": "last_member_qrt",
+ "LastMemberQueryCount": "last_member_query_count",
+ "ConfiguredGroupTimeout": "group_timeout",
+ }
+
+ body = execute_show_command(command, module)[0]
+
+ if body:
+ if "not running" in body:
+ return igmp
+ resource = body["TABLE_vrf"]["ROW_vrf"]["TABLE_if"]["ROW_if"]
+ igmp = apply_key_map(key_map, resource)
+ report_llg = str(resource["ReportingForLinkLocal"]).lower()
+ if report_llg == "true":
+ igmp["report_llg"] = True
+ elif report_llg == "false":
+ igmp["report_llg"] = False
+
+ immediate_leave = str(
+ resource["ImmediateLeave"]
+ ).lower() # returns en or dis
+ if re.search(r"^en|^true|^enabled", immediate_leave):
+ igmp["immediate_leave"] = True
+ elif re.search(r"^dis|^false|^disabled", immediate_leave):
+ igmp["immediate_leave"] = False
+
+ # the next block of code is used to retrieve anything with:
+ # ip igmp static-oif *** i.e.. could be route-map ROUTEMAP
+ # or PREFIX source <ip>, etc.
+ command = "show run interface {0} | inc oif".format(interface)
+
+ body = execute_show_command(
+ command, module, command_type="cli_show_ascii"
+ )[0]
+
+ staticoif = []
+ if body:
+ split_body = body.split("\n")
+ route_map_regex = (
+ r".*ip igmp static-oif route-map\s+" r"(?P<route_map>\S+).*"
+ )
+ prefix_source_regex = (
+ r".*ip igmp static-oif\s+(?P<prefix>"
+ r"((\d+.){3}\d+))(\ssource\s"
+ r"(?P<source>\S+))?.*"
+ )
+
+ for line in split_body:
+ temp = {}
+ try:
+ match_route_map = re.match(route_map_regex, line, re.DOTALL)
+ route_map = match_route_map.groupdict()["route_map"]
+ except AttributeError:
+ route_map = ""
+
+ try:
+ match_prefix_source = re.match(
+ prefix_source_regex, line, re.DOTALL
+ )
+ prefix_source_group = match_prefix_source.groupdict()
+ prefix = prefix_source_group["prefix"]
+ source = prefix_source_group["source"]
+ except AttributeError:
+ prefix = ""
+ source = ""
+
+ if route_map:
+ temp["route_map"] = route_map
+ if prefix:
+ temp["prefix"] = prefix
+ if source:
+ temp["source"] = source
+ if temp:
+ staticoif.append(temp)
+
+ igmp["oif_routemap"] = None
+ igmp["oif_prefix_source"] = []
+
+ if staticoif:
+ if len(staticoif) == 1 and staticoif[0].get("route_map"):
+ igmp["oif_routemap"] = staticoif[0]["route_map"]
+ else:
+ igmp["oif_prefix_source"] = staticoif
+
+ return igmp
+
+
+def config_igmp_interface(delta, existing, existing_oif_prefix_source):
+ CMDS = {
+ "version": "ip igmp version {0}",
+ "startup_query_interval": "ip igmp startup-query-interval {0}",
+ "startup_query_count": "ip igmp startup-query-count {0}",
+ "robustness": "ip igmp robustness-variable {0}",
+ "querier_timeout": "ip igmp querier-timeout {0}",
+ "query_mrt": "ip igmp query-max-response-time {0}",
+ "query_interval": "ip igmp query-interval {0}",
+ "last_member_qrt": "ip igmp last-member-query-response-time {0}",
+ "last_member_query_count": "ip igmp last-member-query-count {0}",
+ "group_timeout": "ip igmp group-timeout {0}",
+ "report_llg": "ip igmp report-link-local-groups",
+ "immediate_leave": "ip igmp immediate-leave",
+ "oif_prefix_source": "ip igmp static-oif {0} source {1} ",
+ "oif_routemap": "ip igmp static-oif route-map {0}",
+ "oif_prefix": "ip igmp static-oif {0}",
+ }
+
+ commands = []
+ command = None
+ def_vals = get_igmp_interface_defaults()
+
+ for key, value in delta.items():
+ if key == "oif_ps" and value != "default":
+ for each in value:
+ if each in existing_oif_prefix_source:
+ existing_oif_prefix_source.remove(each)
+ else:
+ # add new prefix/sources
+ pf = each["prefix"]
+ src = ""
+ if "source" in each.keys():
+ src = each["source"]
+ if src:
+ commands.append(
+ CMDS.get("oif_prefix_source").format(pf, src)
+ )
+ else:
+ commands.append(CMDS.get("oif_prefix").format(pf))
+ if existing_oif_prefix_source:
+ for each in existing_oif_prefix_source:
+ # remove stale prefix/sources
+ pf = each["prefix"]
+ src = ""
+ if "source" in each.keys():
+ src = each["source"]
+ if src:
+ commands.append(
+ "no "
+ + CMDS.get("oif_prefix_source").format(pf, src)
+ )
+ else:
+ commands.append(
+ "no " + CMDS.get("oif_prefix").format(pf)
+ )
+ elif key == "oif_routemap":
+ if value == "default":
+ if existing.get(key):
+ command = "no " + CMDS.get(key).format(existing.get(key))
+ else:
+ command = CMDS.get(key).format(value)
+ elif value:
+ if value == "default":
+ if def_vals.get(key) != existing.get(key):
+ command = CMDS.get(key).format(def_vals.get(key))
+ else:
+ command = CMDS.get(key).format(value)
+ elif not value:
+ command = "no {0}".format(CMDS.get(key).format(value))
+
+ if command:
+ if command not in commands:
+ commands.append(command)
+ command = None
+
+ return commands
+
+
+def get_igmp_interface_defaults():
+ version = "2"
+ startup_query_interval = "31"
+ startup_query_count = "2"
+ robustness = "2"
+ querier_timeout = "255"
+ query_mrt = "10"
+ query_interval = "125"
+ last_member_qrt = "1"
+ last_member_query_count = "2"
+ group_timeout = "260"
+ report_llg = False
+ immediate_leave = False
+
+ args = dict(
+ version=version,
+ startup_query_interval=startup_query_interval,
+ startup_query_count=startup_query_count,
+ robustness=robustness,
+ querier_timeout=querier_timeout,
+ query_mrt=query_mrt,
+ query_interval=query_interval,
+ last_member_qrt=last_member_qrt,
+ last_member_query_count=last_member_query_count,
+ group_timeout=group_timeout,
+ report_llg=report_llg,
+ immediate_leave=immediate_leave,
+ )
+
+ default = dict(
+ (param, value) for (param, value) in args.items() if value is not None
+ )
+
+ return default
+
+
+def config_default_igmp_interface(existing, delta):
+ commands = []
+ proposed = get_igmp_interface_defaults()
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = config_igmp_interface(
+ delta, existing, existing_oif_prefix_source=None
+ )
+
+ if command:
+ for each in command:
+ commands.append(each)
+
+ return commands
+
+
+def config_remove_oif(existing, existing_oif_prefix_source):
+ commands = []
+ command = None
+ if existing.get("oif_routemap"):
+ commands.append(
+ "no ip igmp static-oif route-map {0}".format(
+ existing.get("oif_routemap")
+ )
+ )
+ elif existing_oif_prefix_source:
+ for each in existing_oif_prefix_source:
+ if each.get("prefix") and each.get("source"):
+ command = "no ip igmp static-oif {0} source {1} ".format(
+ each.get("prefix"), each.get("source")
+ )
+ elif each.get("prefix"):
+ command = "no ip igmp static-oif {0}".format(
+ each.get("prefix")
+ )
+ if command:
+ commands.append(command)
+ command = None
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ interface=dict(required=True, type="str"),
+ version=dict(
+ required=False, type="str", choices=["2", "3", "default"]
+ ),
+ startup_query_interval=dict(required=False, type="str"),
+ startup_query_count=dict(required=False, type="str"),
+ robustness=dict(required=False, type="str"),
+ querier_timeout=dict(required=False, type="str"),
+ query_mrt=dict(required=False, type="str"),
+ query_interval=dict(required=False, type="str"),
+ last_member_qrt=dict(required=False, type="str"),
+ last_member_query_count=dict(required=False, type="str"),
+ group_timeout=dict(required=False, type="str"),
+ report_llg=dict(type="bool"),
+ immediate_leave=dict(type="bool"),
+ oif_routemap=dict(required=False, type="str"),
+ oif_ps=dict(required=False, type="raw"),
+ restart=dict(type="bool", default=False),
+ state=dict(
+ choices=["present", "absent", "default"], default="present"
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+ mutually_exclusive = [("oif_ps", "oif_routemap")]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+
+ state = module.params["state"]
+ interface = module.params["interface"]
+ oif_routemap = module.params["oif_routemap"]
+ oif_ps = module.params["oif_ps"]
+
+ intf_type = get_interface_type(interface)
+ if get_interface_mode(interface, intf_type, module) == "layer2":
+ module.fail_json(msg="this module only works on Layer 3 interfaces")
+
+ existing = get_igmp_interface(module, interface)
+ existing_copy = existing.copy()
+ end_state = existing_copy
+
+ if not existing.get("version"):
+ module.fail_json(msg="pim needs to be enabled on the interface")
+
+ existing_oif_prefix_source = existing.get("oif_prefix_source")
+ # not json serializable
+ existing.pop("oif_prefix_source")
+
+ if oif_routemap and existing_oif_prefix_source:
+ module.fail_json(
+ msg="Delete static-oif configurations on this "
+ "interface if you want to use a routemap"
+ )
+
+ if oif_ps and existing.get("oif_routemap"):
+ module.fail_json(
+ msg="Delete static-oif route-map configuration "
+ "on this interface if you want to config "
+ "static entries"
+ )
+
+ args = [
+ "version",
+ "startup_query_interval",
+ "startup_query_count",
+ "robustness",
+ "querier_timeout",
+ "query_mrt",
+ "query_interval",
+ "last_member_qrt",
+ "last_member_query_count",
+ "group_timeout",
+ "report_llg",
+ "immediate_leave",
+ "oif_routemap",
+ ]
+
+ changed = False
+ commands = []
+ proposed = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ CANNOT_ABSENT = [
+ "version",
+ "startup_query_interval",
+ "startup_query_count",
+ "robustness",
+ "querier_timeout",
+ "query_mrt",
+ "query_interval",
+ "last_member_qrt",
+ "last_member_query_count",
+ "group_timeout",
+ "report_llg",
+ "immediate_leave",
+ ]
+
+ if state == "absent":
+ for each in CANNOT_ABSENT:
+ if each in proposed:
+ module.fail_json(
+ msg="only params: "
+ "oif_ps, oif_routemap can be used when "
+ "state=absent"
+ )
+
+ # delta check for all params except oif_ps
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ if oif_ps:
+ if oif_ps == "default":
+ delta["oif_ps"] = []
+ else:
+ delta["oif_ps"] = oif_ps
+
+ if state == "present":
+ if delta:
+ command = config_igmp_interface(
+ delta, existing, existing_oif_prefix_source
+ )
+ if command:
+ commands.append(command)
+
+ elif state == "default":
+ command = config_default_igmp_interface(existing, delta)
+ if command:
+ commands.append(command)
+ elif state == "absent":
+ command = None
+ if existing.get("oif_routemap") or existing_oif_prefix_source:
+ command = config_remove_oif(existing, existing_oif_prefix_source)
+
+ if command:
+ commands.append(command)
+
+ command = config_default_igmp_interface(existing, delta)
+ if command:
+ commands.append(command)
+
+ cmds = []
+ results = {}
+ if commands:
+ commands.insert(0, ["interface {0}".format(interface)])
+ cmds = flatten_list(commands)
+
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ load_config(module, cmds)
+ changed = True
+ end_state = get_igmp_interface(module, interface)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ if module.params["restart"]:
+ cmd = {"command": "restart igmp", "output": "text"}
+ run_commands(module, cmd)
+
+ results["proposed"] = proposed
+ results["existing"] = existing_copy
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+ results["end_state"] = end_state
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_snooping.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_snooping.py
new file mode 100644
index 00000000..194e610d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_igmp_snooping.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_igmp_snooping
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages IGMP snooping global configuration.
+description:
+- Manages IGMP snooping global configuration.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- When C(state=default), params will be reset to a default state.
+- C(group_timeout) also accepts I(never) as an input.
+options:
+ snooping:
+ description:
+ - Enables/disables IGMP snooping on the switch.
+ type: bool
+ group_timeout:
+ description:
+ - Group membership timeout value for all VLANs on the device. Accepted values
+ are integer in range 1-10080, I(never) and I(default).
+ type: str
+ link_local_grp_supp:
+ description:
+ - Global link-local groups suppression.
+ type: bool
+ report_supp:
+ description:
+ - Global IGMPv1/IGMPv2 Report Suppression.
+ type: bool
+ v3_report_supp:
+ description:
+ - Global IGMPv3 Report Suppression and Proxy Reporting.
+ type: bool
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - default
+ type: str
+"""
+
+EXAMPLES = """
+# ensure igmp snooping params supported in this module are in there default state
+- cisco.nxos.nxos_igmp_snooping:
+ state: default
+
+# ensure following igmp snooping params are in the desired state
+- cisco.nxos.nxos_igmp_snooping:
+ group_timeout: never
+ snooping: true
+ link_local_grp_supp: false
+ optimize_mcast_flood: false
+ report_supp: true
+ v3_report_supp: true
+"""
+
+RETURN = """
+commands:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["ip igmp snooping link-local-groups-suppression",
+ "ip igmp snooping group-timeout 50",
+ "no ip igmp snooping report-suppression",
+ "no ip igmp snooping v3-report-suppression",
+ "no ip igmp snooping"]
+"""
+
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module, output="text"):
+ command = {"command": command, "output": output}
+
+ return run_commands(module, [command])
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_group_timeout(config):
+ match = re.search(r" Group timeout configured: (\S+)", config, re.M)
+ if match:
+ value = match.group(1)
+ else:
+ value = ""
+ return value
+
+
+def get_igmp_snooping(module):
+ command = "show ip igmp snooping"
+ existing = {}
+
+ try:
+ body = execute_show_command(command, module, output="json")[0]
+ except IndexError:
+ body = []
+
+ if body:
+ snooping = str(body.get("enabled")).lower()
+ if snooping == "true" or snooping == "enabled":
+ existing["snooping"] = True
+ else:
+ existing["snooping"] = False
+
+ report_supp = str(body.get("grepsup")).lower()
+ if report_supp == "true" or report_supp == "enabled":
+ existing["report_supp"] = True
+ else:
+ existing["report_supp"] = False
+
+ link_local_grp_supp = str(body.get("glinklocalgrpsup")).lower()
+ if link_local_grp_supp == "true" or link_local_grp_supp == "enabled":
+ existing["link_local_grp_supp"] = True
+ else:
+ existing["link_local_grp_supp"] = False
+
+ v3_report_supp = str(body.get("gv3repsup")).lower()
+ if v3_report_supp == "true" or v3_report_supp == "enabled":
+ existing["v3_report_supp"] = True
+ else:
+ existing["v3_report_supp"] = False
+
+ command = "show ip igmp snooping"
+ body = execute_show_command(command, module)[0]
+ if body:
+ existing["group_timeout"] = get_group_timeout(body)
+
+ return existing
+
+
+def config_igmp_snooping(delta, existing, default=False):
+ CMDS = {
+ "snooping": "ip igmp snooping",
+ "group_timeout": "ip igmp snooping group-timeout {}",
+ "link_local_grp_supp": "ip igmp snooping link-local-groups-suppression",
+ "v3_report_supp": "ip igmp snooping v3-report-suppression",
+ "report_supp": "ip igmp snooping report-suppression",
+ }
+
+ commands = []
+ command = None
+ gt_command = None
+ for key, value in delta.items():
+ if value:
+ if default and key == "group_timeout":
+ if existing.get(key):
+ gt_command = "no " + CMDS.get(key).format(
+ existing.get(key)
+ )
+ elif value == "default" and key == "group_timeout":
+ if existing.get(key):
+ command = "no " + CMDS.get(key).format(existing.get(key))
+ else:
+ command = CMDS.get(key).format(value)
+ else:
+ command = "no " + CMDS.get(key).format(value)
+
+ if command:
+ commands.append(command)
+ command = None
+
+ if gt_command:
+ # ensure that group-timeout command is configured last
+ commands.append(gt_command)
+ return commands
+
+
+def get_igmp_snooping_defaults():
+ group_timeout = "dummy"
+ report_supp = True
+ link_local_grp_supp = True
+ v3_report_supp = False
+ snooping = True
+
+ args = dict(
+ snooping=snooping,
+ link_local_grp_supp=link_local_grp_supp,
+ report_supp=report_supp,
+ v3_report_supp=v3_report_supp,
+ group_timeout=group_timeout,
+ )
+
+ default = dict(
+ (param, value) for (param, value) in args.items() if value is not None
+ )
+
+ return default
+
+
+def igmp_snooping_gt_dependency(command, existing, module):
+ # group-timeout will fail if igmp snooping is disabled
+ gt = [i for i in command if i.startswith("ip igmp snooping group-timeout")]
+ if gt:
+ if "no ip igmp snooping" in command or (
+ existing["snooping"] is False and "ip igmp snooping" not in command
+ ):
+ msg = "group-timeout cannot be enabled or changed when ip igmp snooping is disabled"
+ module.fail_json(msg=msg)
+ else:
+ # ensure that group-timeout command is configured last
+ command.remove(gt[0])
+ command.append(gt[0])
+
+
+def main():
+ argument_spec = dict(
+ snooping=dict(required=False, type="bool"),
+ group_timeout=dict(required=False, type="str"),
+ link_local_grp_supp=dict(required=False, type="bool"),
+ report_supp=dict(required=False, type="bool"),
+ v3_report_supp=dict(required=False, type="bool"),
+ state=dict(choices=["present", "default"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ snooping = module.params["snooping"]
+ link_local_grp_supp = module.params["link_local_grp_supp"]
+ report_supp = module.params["report_supp"]
+ v3_report_supp = module.params["v3_report_supp"]
+ group_timeout = module.params["group_timeout"]
+ state = module.params["state"]
+
+ args = dict(
+ snooping=snooping,
+ link_local_grp_supp=link_local_grp_supp,
+ report_supp=report_supp,
+ v3_report_supp=v3_report_supp,
+ group_timeout=group_timeout,
+ )
+
+ proposed = dict(
+ (param, value) for (param, value) in args.items() if value is not None
+ )
+
+ existing = get_igmp_snooping(module)
+
+ commands = []
+ if state == "present":
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = config_igmp_snooping(delta, existing)
+ if command:
+ if group_timeout:
+ igmp_snooping_gt_dependency(command, existing, module)
+ commands.append(command)
+ elif state == "default":
+ proposed = get_igmp_snooping_defaults()
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = config_igmp_snooping(delta, existing, default=True)
+ if command:
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_install_os.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_install_os.py
new file mode 100644
index 00000000..bd9a8858
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_install_os.py
@@ -0,0 +1,607 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_install_os
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Set boot options like boot, kickstart image and issu.
+description:
+- Install an operating system by setting the boot options like boot image and kickstart
+ image and optionally select to install using ISSU (In Server Software Upgrade).
+version_added: 1.0.0
+notes:
+- Tested against the following platforms and images - N9k 7.0(3)I4(6), 7.0(3)I5(3),
+ 7.0(3)I6(1), 7.0(3)I7(1), 7.0(3)F2(2), 7.0(3)F3(2) - N3k 6.0(2)A8(6), 6.0(2)A8(8),
+ 7.0(3)I6(1), 7.0(3)I7(1) - N7k 7.3(0)D1(1), 8.0(1), 8.1(1), 8.2(1)
+- This module requires both the ANSIBLE_PERSISTENT_CONNECT_TIMEOUT and ANSIBLE_PERSISTENT_COMMAND_TIMEOUT
+ timers to be set to 600 seconds or higher. The module will exit if the timers are
+ not set properly.
+- When using connection local, ANSIBLE_PERSISTENT_CONNECT_TIMEOUT and ANSIBLE_PERSISTENT_COMMAND_TIMEOUT
+ can only be set using ENV variables or the ansible.cfg file.
+- Do not include full file paths, just the name of the file(s) stored on the top level
+ flash directory.
+- This module attempts to install the software immediately, which may trigger a reboot.
+- In check mode, the module will indicate if an upgrade is needed and whether or not
+ the upgrade is disruptive or non-disruptive(ISSU).
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbibo (@GGabriele)
+options:
+ system_image_file:
+ description:
+ - Name of the system (or combined) image file on flash.
+ required: true
+ type: str
+ kickstart_image_file:
+ description:
+ - Name of the kickstart image file on flash. (Not required on all Nexus platforms)
+ type: str
+ issu:
+ description:
+ - Upgrade using In Service Software Upgrade (ISSU). (Supported on N5k, N7k, N9k
+ platforms)
+ - Selecting 'required' or 'yes' means that upgrades will only proceed if the switch
+ is capable of ISSU.
+ - Selecting 'desired' means that upgrades will use ISSU if possible but will fall
+ back to disruptive upgrade if needed.
+ - Selecting 'no' means do not use ISSU. Forced disruptive.
+ choices:
+ - "required"
+ - "desired"
+ - "yes"
+ - "no"
+ default: "no"
+ type: str
+"""
+
+EXAMPLES = """
+- name: Install OS on N9k
+ check_mode: no
+ cisco.nxos.nxos_install_os:
+ system_image_file: nxos.7.0.3.I6.1.bin
+ issu: desired
+
+- name: Wait for device to come back up with new image
+ wait_for:
+ port: 22
+ state: started
+ timeout: 500
+ delay: 60
+ host: '{{ inventory_hostname }}'
+
+- name: Check installed OS for newly installed version
+ nxos_command:
+ commands: [show version | json]
+ provider: '{{ connection }}'
+ register: output
+- assert:
+ that:
+ - output['stdout'][0]['kickstart_ver_str'] == '7.0(3)I6(1)'
+"""
+
+RETURN = """
+install_state:
+ description: Boot and install information.
+ returned: always
+ type: dict
+ sample: {
+ "install_state": [
+ "Compatibility check is done:",
+ "Module bootable Impact Install-type Reason",
+ "------ -------- -------------- ------------ ------",
+ " 1 yes non-disruptive reset ",
+ "Images will be upgraded according to following table:",
+ "Module Image Running-Version(pri:alt) New-Version Upg-Required",
+ "------ ---------- ---------------------------------------- -------------------- ------------",
+ " 1 nxos 7.0(3)I6(1) 7.0(3)I7(1) yes",
+ " 1 bios v4.4.0(07/12/2017) v4.4.0(07/12/2017) no"
+ ],
+ }
+"""
+
+
+import re
+from time import sleep
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+# Output options are 'text' or 'json'
+def execute_show_command(module, command, output="text"):
+ cmds = [{"command": command, "output": output}]
+
+ return run_commands(module, cmds)
+
+
+def get_platform(module):
+ """Determine platform type"""
+ data = execute_show_command(module, "show inventory", "json")
+ pid = data[0]["TABLE_inv"]["ROW_inv"][0]["productid"]
+
+ if re.search(r"N3K", pid):
+ type = "N3K"
+ elif re.search(r"N5K", pid):
+ type = "N5K"
+ elif re.search(r"N6K", pid):
+ type = "N6K"
+ elif re.search(r"N7K", pid):
+ type = "N7K"
+ elif re.search(r"N9K", pid):
+ type = "N9K"
+ else:
+ type = "unknown"
+
+ return type
+
+
+def parse_show_install(data):
+ """Helper method to parse the output of the 'show install all impact' or
+ 'install all' commands.
+
+ Sample Output:
+
+ Installer will perform impact only check. Please wait.
+
+ Verifying image bootflash:/nxos.7.0.3.F2.2.bin for boot variable "nxos".
+ [####################] 100% -- SUCCESS
+
+ Verifying image type.
+ [####################] 100% -- SUCCESS
+
+ Preparing "bios" version info using image bootflash:/nxos.7.0.3.F2.2.bin.
+ [####################] 100% -- SUCCESS
+
+ Preparing "nxos" version info using image bootflash:/nxos.7.0.3.F2.2.bin.
+ [####################] 100% -- SUCCESS
+
+ Performing module support checks.
+ [####################] 100% -- SUCCESS
+
+ Notifying services about system upgrade.
+ [####################] 100% -- SUCCESS
+
+
+
+ Compatibility check is done:
+ Module bootable Impact Install-type Reason
+ ------ -------- -------------- ------------ ------
+ 8 yes disruptive reset Incompatible image for ISSU
+ 21 yes disruptive reset Incompatible image for ISSU
+
+
+ Images will be upgraded according to following table:
+ Module Image Running-Version(pri:alt) New-Version Upg-Required
+ ------ ---------- ---------------------------------------- ------------
+ 8 lcn9k 7.0(3)F3(2) 7.0(3)F2(2) yes
+ 8 bios v01.17 v01.17 no
+ 21 lcn9k 7.0(3)F3(2) 7.0(3)F2(2) yes
+ 21 bios v01.70 v01.70 no
+ """
+ if len(data) > 0:
+ data = massage_install_data(data)
+ ud = {"raw": data}
+ ud["processed"] = []
+ ud["disruptive"] = False
+ ud["upgrade_needed"] = False
+ ud["error"] = False
+ ud["invalid_command"] = False
+ ud["install_in_progress"] = False
+ ud["server_error"] = False
+ ud["upgrade_succeeded"] = False
+ ud["use_impact_data"] = False
+
+ # Check for server errors
+ if isinstance(data, int):
+ if data == -1:
+ ud["server_error"] = True
+ elif data >= 500:
+ ud["server_error"] = True
+ elif data == -32603:
+ ud["server_error"] = True
+ elif data == 1:
+ ud["server_error"] = True
+ return ud
+ else:
+ ud["list_data"] = data.split("\n")
+
+ for x in ud["list_data"]:
+ # Check for errors and exit if found.
+ if re.search(r"Pre-upgrade check failed", x):
+ ud["error"] = True
+ break
+ if re.search(r"[I|i]nvalid command", x):
+ ud["invalid_command"] = True
+ ud["error"] = True
+ break
+ if re.search(r"No install all data found", x):
+ ud["error"] = True
+ break
+
+ # Check for potentially transient conditions
+ if re.search(r"Another install procedure may\s*be in progress", x):
+ ud["install_in_progress"] = True
+ break
+ if re.search(r"Backend processing error", x):
+ ud["server_error"] = True
+ break
+ if re.search(r"timed out", x):
+ ud["server_error"] = True
+ break
+ if re.search(r"^(-1|5\d\d)$", x):
+ ud["server_error"] = True
+ break
+
+ # Check for messages indicating a successful upgrade.
+ if re.search(r"Finishing the upgrade", x):
+ ud["upgrade_succeeded"] = True
+ break
+ if re.search(r"Install has been successful", x):
+ ud["upgrade_succeeded"] = True
+ break
+ if re.search(r"Switching over onto standby", x):
+ ud["upgrade_succeeded"] = True
+ break
+
+ # We get these messages when the upgrade is non-disruptive and
+ # we loose connection with the switchover but far enough along that
+ # we can be confident the upgrade succeeded.
+ if re.search(r"timeout .*trying to send command: install", x):
+ ud["upgrade_succeeded"] = True
+ ud["use_impact_data"] = True
+ break
+ if re.search(r"[C|c]onnection failure: timed out", x):
+ ud["upgrade_succeeded"] = True
+ ud["use_impact_data"] = True
+ break
+
+ # Begin normal parsing.
+ if re.search(r"----|Module|Images will|Compatibility", x):
+ ud["processed"].append(x)
+ continue
+ # Check to see if upgrade will be disruptive or non-disruptive and
+ # build dictionary of individual modules and their status.
+ # Sample Line:
+ #
+ # Module bootable Impact Install-type Reason
+ # ------ -------- ---------- ------------ ------
+ # 8 yes disruptive reset Incompatible image
+ rd = r"(\d+)\s+(\S+)\s+(disruptive|non-disruptive)\s+(\S+)"
+ mo = re.search(rd, x)
+ if mo:
+ ud["processed"].append(x)
+ key = "m%s" % mo.group(1)
+ field = "disruptive"
+ if mo.group(3) == "non-disruptive":
+ ud[key] = {field: False}
+ else:
+ ud[field] = True
+ ud[key] = {field: True}
+ field = "bootable"
+ if mo.group(2) == "yes":
+ ud[key].update({field: True})
+ else:
+ ud[key].update({field: False})
+ continue
+
+ # Check to see if switch needs an upgrade and build a dictionary
+ # of individual modules and their individual upgrade status.
+ # Sample Line:
+ #
+ # Module Image Running-Version(pri:alt) New-Version Upg-Required
+ # ------ ----- ---------------------------------------- ------------
+ # 8 lcn9k 7.0(3)F3(2) 7.0(3)F2(2) yes
+ mo = re.search(r"(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(yes|no)", x)
+ if mo:
+ ud["processed"].append(x)
+ key = "m%s_%s" % (mo.group(1), mo.group(2))
+ field = "upgrade_needed"
+ if mo.group(5) == "yes":
+ ud[field] = True
+ ud[key] = {field: True}
+ else:
+ ud[key] = {field: False}
+ continue
+
+ return ud
+
+
+def massage_install_data(data):
+ # Transport cli returns a list containing one result item.
+ # Transport nxapi returns a list containing two items. The second item
+ # contains the data we are interested in.
+ default_error_msg = "No install all data found"
+ if len(data) == 1:
+ result_data = data[0]
+ elif len(data) == 2:
+ result_data = data[1]
+ else:
+ result_data = default_error_msg
+
+ # Further processing may be needed for result_data
+ if len(data) == 2 and isinstance(data[1], dict):
+ if "clierror" in data[1].keys():
+ result_data = data[1]["clierror"]
+ elif "code" in data[1].keys() and data[1]["code"] == "500":
+ # We encountered a backend processing error for nxapi
+ result_data = data[1]["msg"]
+ else:
+ result_data = default_error_msg
+ return result_data
+
+
+def build_install_cmd_set(issu, image, kick, type, force=True):
+ commands = ["terminal dont-ask"]
+
+ # Different NX-OS platforms behave differently for
+ # disruptive and non-disruptive upgrade paths.
+ #
+ # 1) Combined kickstart/system image:
+ # * Use option 'non-disruptive' for issu.
+ # * Omit option 'non-disruptive' for disruptive upgrades.
+ # 2) Separate kickstart + system images.
+ # * Omit hidden 'force' option for issu.
+ # * Use hidden 'force' option for disruptive upgrades.
+ # * Note: Not supported on all platforms
+ if re.search(r"required|desired|yes", issu):
+ if kick is None:
+ issu_cmd = "non-disruptive"
+ else:
+ issu_cmd = ""
+ else:
+ if kick is None:
+ issu_cmd = ""
+ else:
+ issu_cmd = "force" if force else ""
+
+ if type == "impact":
+ rootcmd = "show install all impact"
+ # The force option is not available for the impact command.
+ if kick:
+ issu_cmd = ""
+ else:
+ rootcmd = "install all"
+ if kick is None:
+ commands.append("%s nxos %s %s" % (rootcmd, image, issu_cmd))
+ else:
+ commands.append(
+ "%s %s system %s kickstart %s" % (rootcmd, issu_cmd, image, kick)
+ )
+
+ return commands
+
+
+def parse_show_version(data):
+ version_data = {"raw": data[0].split("\n")}
+ version_data["version"] = ""
+ version_data["error"] = False
+ for x in version_data["raw"]:
+ mo = re.search(r"(kickstart|system|NXOS):\s+version\s+(\S+)", x)
+ if mo:
+ version_data["version"] = mo.group(2)
+ continue
+
+ if version_data["version"] == "":
+ version_data["error"] = True
+
+ return version_data
+
+
+def check_mode_legacy(module, issu, image, kick=None):
+ """Some platforms/images/transports don't support the 'install all impact'
+ command so we need to use a different method."""
+ current = execute_show_command(module, "show version", "json")[0]
+ # Call parse_show_data on empty string to create the default upgrade
+ # data structure dictionary
+ data = parse_show_install("")
+ upgrade_msg = "No upgrade required"
+
+ # Process System Image
+ data["error"] = False
+ tsver = "show version image bootflash:%s" % image
+ data["upgrade_cmd"] = [tsver]
+ target_image = parse_show_version(execute_show_command(module, tsver))
+ if target_image["error"]:
+ data["error"] = True
+ data["raw"] = target_image["raw"]
+ if (
+ current["kickstart_ver_str"] != target_image["version"]
+ and not data["error"]
+ ):
+ data["upgrade_needed"] = True
+ data["disruptive"] = True
+ upgrade_msg = "Switch upgraded: system: %s" % tsver
+
+ # Process Kickstart Image
+ if kick is not None and not data["error"]:
+ tkver = "show version image bootflash:%s" % kick
+ data["upgrade_cmd"].append(tsver)
+ target_kick = parse_show_version(execute_show_command(module, tkver))
+ if target_kick["error"]:
+ data["error"] = True
+ data["raw"] = target_kick["raw"]
+ if (
+ current["kickstart_ver_str"] != target_kick["version"]
+ and not data["error"]
+ ):
+ data["upgrade_needed"] = True
+ data["disruptive"] = True
+ upgrade_msg = upgrade_msg + " kickstart: %s" % tkver
+
+ data["list_data"] = data["raw"]
+ data["processed"] = upgrade_msg
+ return data
+
+
+def check_mode_nextgen(module, issu, image, kick=None):
+ """Use the 'install all impact' command for check_mode"""
+ opts = {"ignore_timeout": True}
+ commands = build_install_cmd_set(issu, image, kick, "impact")
+ data = parse_show_install(load_config(module, commands, True, opts))
+ # If an error is encountered when issu is 'desired' then try again
+ # but set issu to 'no'
+ if data["error"] and issu == "desired":
+ issu = "no"
+ commands = build_install_cmd_set(issu, image, kick, "impact")
+ # The system may be busy from the previous call to check_mode so loop
+ # until it's done.
+ data = check_install_in_progress(module, commands, opts)
+ if data["server_error"]:
+ data["error"] = True
+ data["upgrade_cmd"] = commands
+ return data
+
+
+def check_install_in_progress(module, commands, opts):
+ for attempt in range(20):
+ data = parse_show_install(load_config(module, commands, True, opts))
+ if data["install_in_progress"]:
+ sleep(1)
+ continue
+ break
+ return data
+
+
+def check_mode(module, issu, image, kick=None):
+ """Check switch upgrade impact using 'show install all impact' command"""
+ data = check_mode_nextgen(module, issu, image, kick)
+ if data["server_error"]:
+ # We encountered an unrecoverable error in the attempt to get upgrade
+ # impact data from the 'show install all impact' command.
+ # Fallback to legacy method.
+ data = check_mode_legacy(module, issu, image, kick)
+ if data["invalid_command"]:
+ # If we are upgrading from a device running a separate kickstart and
+ # system image the impact command will fail.
+ # Fallback to legacy method.
+ data = check_mode_legacy(module, issu, image, kick)
+ return data
+
+
+def do_install_all(module, issu, image, kick=None):
+ """Perform the switch upgrade using the 'install all' command"""
+ impact_data = check_mode(module, issu, image, kick)
+ if module.check_mode:
+ # Check mode set in the playbook so just return the impact data.
+ msg = "*** SWITCH WAS NOT UPGRADED: IMPACT DATA ONLY ***"
+ impact_data["processed"].append(msg)
+ return impact_data
+ if impact_data["error"]:
+ # Check mode discovered an error so return with this info.
+ return impact_data
+ elif not impact_data["upgrade_needed"]:
+ # The switch is already upgraded. Nothing more to do.
+ return impact_data
+ else:
+ # If we get here, check_mode returned no errors and the switch
+ # needs to be upgraded.
+ if impact_data["disruptive"]:
+ # Check mode indicated that ISSU is not possible so issue the
+ # upgrade command without the non-disruptive flag unless the
+ # playbook specified issu: yes/required.
+ if issu == "yes":
+ msg = "ISSU/ISSD requested but impact data indicates ISSU/ISSD is not possible"
+ module.fail_json(msg=msg, raw_data=impact_data["list_data"])
+ else:
+ issu = "no"
+
+ commands = build_install_cmd_set(issu, image, kick, "install")
+ opts = {"ignore_timeout": True}
+ # The system may be busy from the call to check_mode so loop until
+ # it's done.
+ upgrade = check_install_in_progress(module, commands, opts)
+ if upgrade["invalid_command"] and "force" in commands[1]:
+ # Not all platforms support the 'force' keyword. Check for this
+ # condition and re-try without the 'force' keyword if needed.
+ commands = build_install_cmd_set(
+ issu, image, kick, "install", False
+ )
+ upgrade = check_install_in_progress(module, commands, opts)
+ upgrade["upgrade_cmd"] = commands
+
+ # Special case: If we encounter a server error at this stage
+ # it means the command was sent and the upgrade was started but
+ # we will need to use the impact data instead of the current install
+ # data.
+ if upgrade["server_error"]:
+ upgrade["upgrade_succeeded"] = True
+ upgrade["use_impact_data"] = True
+
+ if upgrade["use_impact_data"]:
+ if upgrade["upgrade_succeeded"]:
+ upgrade = impact_data
+ upgrade["upgrade_succeeded"] = True
+ else:
+ upgrade = impact_data
+ upgrade["upgrade_succeeded"] = False
+
+ if not upgrade["upgrade_succeeded"]:
+ upgrade["error"] = True
+ return upgrade
+
+
+def main():
+ argument_spec = dict(
+ system_image_file=dict(required=True),
+ kickstart_image_file=dict(required=False),
+ issu=dict(choices=["required", "desired", "no", "yes"], default="no"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ # Get system_image_file(sif), kickstart_image_file(kif) and
+ # issu settings from module params.
+ sif = module.params["system_image_file"]
+ kif = module.params["kickstart_image_file"]
+ issu = module.params["issu"]
+
+ if re.search(r"(yes|required)", issu):
+ issu = "yes"
+
+ if kif == "null" or kif == "":
+ kif = None
+
+ install_result = do_install_all(module, issu, sif, kick=kif)
+ if install_result["error"]:
+ cmd = install_result["upgrade_cmd"]
+ msg = "Failed to upgrade device using command: %s" % cmd
+ module.fail_json(msg=msg, raw_data=install_result["list_data"])
+
+ state = install_result["processed"]
+ changed = install_result["upgrade_needed"]
+ module.exit_json(changed=changed, install_state=state, warnings=warnings)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface.py
new file mode 100644
index 00000000..1393f883
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface.py
@@ -0,0 +1,991 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manages physical
+ attributes of interfaces.
+description:
+- Manages physical attributes of interfaces of NX-OS switches.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_interfaces
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+author:
+- Jason Edelman (@jedelman8)
+- Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- This module is also used to create logical interfaces such as svis and loopbacks.
+- Be cautious of platform specific idiosyncrasies. For example, when you default a
+ loopback interface, the admin state toggles on certain versions of NX-OS.
+- The M(cisco.nxos.nxos_overlay_global) C(anycast_gateway_mac) attribute must be set before setting
+ the C(fabric_forwarding_anycast_gateway) property.
+options:
+ name:
+ description:
+ - Full name of interface, i.e. Ethernet1/1, port-channel10.
+ aliases:
+ - interface
+ type: str
+ interface_type:
+ description:
+ - Interface type to be unconfigured from the device.
+ choices:
+ - loopback
+ - portchannel
+ - svi
+ - nve
+ type: str
+ speed:
+ description:
+ - Interface link speed. Applicable for ethernet interface only.
+ type: str
+ admin_state:
+ description:
+ - Administrative state of the interface.
+ default: up
+ choices:
+ - up
+ - down
+ type: str
+ description:
+ description:
+ - Interface description.
+ type: str
+ mode:
+ description:
+ - Manage Layer 2 or Layer 3 state of the interface. This option is supported for
+ ethernet and portchannel interface. Applicable for ethernet and portchannel
+ interface only.
+ choices:
+ - layer2
+ - layer3
+ type: str
+ mtu:
+ description:
+ - MTU for a specific interface. Must be an even number between 576 and 9216. Applicable
+ for ethernet interface only.
+ type: str
+ ip_forward:
+ description:
+ - Enable/Disable ip forward feature on SVIs.
+ choices:
+ - enable
+ - disable
+ type: str
+ fabric_forwarding_anycast_gateway:
+ description:
+ - Associate SVI with anycast gateway under VLAN configuration mode. Applicable
+ for SVI interface only.
+ type: bool
+ duplex:
+ description:
+ - Interface link status. Applicable for ethernet interface only.
+ choices:
+ - full
+ - half
+ - auto
+ type: str
+ tx_rate:
+ description:
+ - Transmit rate in bits per second (bps).
+ - This is state check parameter only.
+ - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
+ type: str
+ rx_rate:
+ description:
+ - Receiver rate in bits per second (bps).
+ - This is state check parameter only.
+ - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
+ type: str
+ neighbors:
+ description:
+ - Check the operational state of given interface C(name) for LLDP neighbor.
+ - The following suboptions are available. This is state check parameter only.
+ type: list
+ elements: dict
+ suboptions:
+ host:
+ description:
+ - LLDP neighbor host for given interface C(name).
+ type: str
+ port:
+ description:
+ - LLDP neighbor port to which given interface C(name) is connected.
+ type: str
+ aggregate:
+ description: List of Interfaces definitions.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Full name of interface, i.e. Ethernet1/1, port-channel10.
+ required: true
+ type: str
+ interface_type:
+ description:
+ - Interface type to be unconfigured from the device.
+ choices:
+ - loopback
+ - portchannel
+ - svi
+ - nve
+ type: str
+ speed:
+ description:
+ - Interface link speed. Applicable for ethernet interface only.
+ type: str
+ admin_state:
+ description:
+ - Administrative state of the interface.
+ choices:
+ - up
+ - down
+ type: str
+ description:
+ description:
+ - Interface description.
+ type: str
+ mode:
+ description:
+ - Manage Layer 2 or Layer 3 state of the interface. This option is supported for
+ ethernet and portchannel interface. Applicable for ethernet and portchannel
+ interface only.
+ choices:
+ - layer2
+ - layer3
+ type: str
+ mtu:
+ description:
+ - MTU for a specific interface. Must be an even number between 576 and 9216. Applicable
+ for ethernet interface only.
+ type: str
+ ip_forward:
+ description:
+ - Enable/Disable ip forward feature on SVIs.
+ choices:
+ - enable
+ - disable
+ type: str
+ fabric_forwarding_anycast_gateway:
+ description:
+ - Associate SVI with anycast gateway under VLAN configuration mode. Applicable
+ for SVI interface only.
+ type: bool
+ duplex:
+ description:
+ - Interface link status. Applicable for ethernet interface only.
+ choices:
+ - full
+ - half
+ - auto
+ type: str
+ tx_rate:
+ description:
+ - Transmit rate in bits per second (bps).
+ - This is state check parameter only.
+ - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
+ type: str
+ rx_rate:
+ description:
+ - Receiver rate in bits per second (bps).
+ - This is state check parameter only.
+ - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
+ type: str
+ neighbors:
+ description:
+ - Check the operational state of given interface C(name) for LLDP neighbor.
+ - The following suboptions are available. This is state check parameter only.
+ type: list
+ elements: dict
+ suboptions:
+ host:
+ description:
+ - LLDP neighbor host for given interface C(name).
+ type: str
+ port:
+ description:
+ - LLDP neighbor port to which given interface C(name) is connected.
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ choices:
+ - present
+ - absent
+ - default
+ type: str
+ delay:
+ description:
+ - Time in seconds to wait before checking for the operational state on remote
+ device. This wait is applicable for operational state arguments.
+ type: int
+ state:
+ description:
+ - Specify desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ - default
+ type: str
+ delay:
+ description:
+ - Time in seconds to wait before checking for the operational state on remote
+ device. This wait is applicable for operational state arguments.
+ default: 10
+ type: int
+
+
+"""
+
+EXAMPLES = """
+- name: Ensure an interface is a Layer 3 port and that it has the proper description
+ cisco.nxos.nxos_interface:
+ name: Ethernet1/1
+ description: Configured by Ansible
+ mode: layer3
+
+- name: Admin down an interface
+ cisco.nxos.nxos_interface:
+ name: Ethernet2/1
+ admin_state: down
+
+- name: Remove all loopback interfaces
+ cisco.nxos.nxos_interface:
+ name: loopback
+ state: absent
+
+- name: Remove all logical interfaces
+ cisco.nxos.nxos_interface:
+ interface_type: '{{ item }} '
+ state: absent
+ loop:
+ - loopback
+ - portchannel
+ - svi
+ - nve
+
+- name: Admin up all loopback interfaces
+ cisco.nxos.nxos_interface:
+ name: loopback 0-1023
+ admin_state: up
+
+- name: Admin down all loopback interfaces
+ cisco.nxos.nxos_interface:
+ name: loopback 0-1023
+ admin_state: down
+
+- name: Check neighbors intent arguments
+ cisco.nxos.nxos_interface:
+ name: Ethernet2/3
+ neighbors:
+ - port: Ethernet2/3
+ host: abc.mycompany.com
+
+- name: Add interface using aggregate
+ cisco.nxos.nxos_interface:
+ aggregate:
+ - {name: Ethernet0/1, mtu: 256, description: test-interface-1}
+ - {name: Ethernet0/2, mtu: 516, description: test-interface-2}
+ duplex: full
+ speed: 100
+ state: present
+
+- name: Delete interface using aggregate
+ cisco.nxos.nxos_interface:
+ aggregate:
+ - name: Loopback9
+ - name: Loopback10
+ state: absent
+
+- name: Check intent arguments
+ cisco.nxos.nxos_interface:
+ name: Ethernet0/2
+ state: up
+ tx_rate: ge(0)
+ rx_rate: le(0)
+"""
+
+RETURN = """
+commands:
+ description: command list sent to the device
+ returned: always
+ type: list
+ sample:
+ - interface Ethernet2/3
+ - mtu 1500
+ - speed 10
+"""
+
+import re
+import time
+
+from copy import deepcopy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ normalize_interface,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_interface_type,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ conditional,
+ remove_default_spec,
+)
+
+
+def execute_show_command(command, module):
+ if "show run" not in command:
+ output = "json"
+ else:
+ output = "text"
+ cmds = [{"command": command, "output": output}]
+ body = run_commands(module, cmds, check_rc=False)
+ if body and "Invalid" in body[0]:
+ return []
+ else:
+ return body
+
+
+def search_obj_in_list(name, lst):
+ for o in lst:
+ if o["name"] == name:
+ return o
+
+ return None
+
+
+def get_interfaces_dict(module):
+ """Gets all active interfaces on a given switch
+ """
+ try:
+ body = execute_show_command("show interface", module)[0]
+ except IndexError:
+ return {}
+
+ interfaces = {
+ "ethernet": [],
+ "svi": [],
+ "loopback": [],
+ "management": [],
+ "portchannel": [],
+ "nve": [],
+ "unknown": [],
+ }
+
+ if body:
+ interface_list = body["TABLE_interface"]["ROW_interface"]
+ for index in interface_list:
+ intf = index["interface"]
+ intf_type = get_interface_type(intf)
+ interfaces[intf_type].append(intf)
+
+ return interfaces
+
+
+def get_vlan_interface_attributes(name, intf_type, module):
+ """ Returns dictionary that has two k/v pairs:
+ admin_state & description if not an svi, returns None
+ """
+ command = "show run interface {0} all".format(name)
+ try:
+ body = execute_show_command(command, module)[0]
+ except (IndexError, TypeError):
+ return None
+ if body:
+ command_list = body.split("\n")
+ desc = None
+ admin_state = "down"
+ for each in command_list:
+ if "description" in each:
+ desc = each.lstrip().split("description")[1].lstrip()
+ elif "no shutdown" in each:
+ admin_state = "up"
+ return dict(description=desc, admin_state=admin_state)
+ else:
+ return None
+
+
+def get_interface_type_removed_cmds(interfaces):
+ commands = []
+
+ for interface in interfaces:
+ if interface != "Vlan1":
+ commands.append("no interface {0}".format(interface))
+
+ return commands
+
+
+def get_admin_state(admin_state):
+ command = ""
+ if admin_state == "up":
+ command = "no shutdown"
+ elif admin_state == "down":
+ command = "shutdown"
+ return command
+
+
+def is_default_interface(name, module):
+ """Checks to see if interface exists and if it is a default config
+ """
+ command = "show run interface {0}".format(name)
+
+ try:
+ body = execute_show_command(command, module)[0]
+ except (IndexError, TypeError):
+ body = ""
+
+ if body:
+ raw_list = body.split("\n")
+ found = False
+ for line in raw_list:
+ if line.startswith("interface"):
+ found = True
+ if found and line and not line.startswith("interface"):
+ return False
+ return True
+
+ else:
+ return "DNE"
+
+
+def add_command_to_interface(interface, cmd, commands):
+ if interface not in commands:
+ commands.append(interface)
+ commands.append(cmd)
+
+
+def map_obj_to_commands(updates, module):
+ commands = list()
+ commands2 = list()
+ want, have = updates
+
+ args = ("speed", "description", "duplex", "mtu")
+ for w in want:
+ name = w["name"]
+ mode = w["mode"]
+ ip_forward = w["ip_forward"]
+ fabric_forwarding_anycast_gateway = w[
+ "fabric_forwarding_anycast_gateway"
+ ]
+ admin_state = w["admin_state"]
+ state = w["state"]
+ interface_type = w["interface_type"]
+ del w["state"]
+ if name:
+ w["interface_type"] = None
+
+ if interface_type:
+ obj_in_have = {}
+ if state in ("present", "default"):
+ module.fail_json(
+ msg="The interface_type param can be used only with state absent."
+ )
+ else:
+ obj_in_have = search_obj_in_list(name, have)
+ is_default = is_default_interface(name, module)
+
+ if name:
+ interface = "interface " + name
+
+ if state == "absent":
+ if obj_in_have:
+ commands.append("no interface {0}".format(name))
+ elif interface_type and not obj_in_have:
+ intfs = get_interfaces_dict(module)[interface_type]
+ cmds = get_interface_type_removed_cmds(intfs)
+ commands.extend(cmds)
+
+ elif state == "present":
+ if obj_in_have:
+ # Don't run switchport command for loopback and svi interfaces
+ if get_interface_type(name) in ("ethernet", "portchannel"):
+ if mode == "layer2" and mode != obj_in_have.get("mode"):
+ add_command_to_interface(
+ interface, "switchport", commands
+ )
+ elif mode == "layer3" and mode != obj_in_have.get("mode"):
+ add_command_to_interface(
+ interface, "no switchport", commands
+ )
+
+ if admin_state == "up" and admin_state != obj_in_have.get(
+ "admin_state"
+ ):
+ add_command_to_interface(
+ interface, "no shutdown", commands
+ )
+ elif admin_state == "down" and admin_state != obj_in_have.get(
+ "admin_state"
+ ):
+ add_command_to_interface(interface, "shutdown", commands)
+
+ if ip_forward == "enable" and ip_forward != obj_in_have.get(
+ "ip_forward"
+ ):
+ add_command_to_interface(interface, "ip forward", commands)
+ elif ip_forward == "disable" and ip_forward != obj_in_have.get(
+ "ip forward"
+ ):
+ add_command_to_interface(
+ interface, "no ip forward", commands
+ )
+
+ if (
+ fabric_forwarding_anycast_gateway is True
+ and obj_in_have.get("fabric_forwarding_anycast_gateway")
+ is False
+ ):
+ add_command_to_interface(
+ interface,
+ "fabric forwarding mode anycast-gateway",
+ commands,
+ )
+
+ elif (
+ fabric_forwarding_anycast_gateway is False
+ and obj_in_have.get("fabric_forwarding_anycast_gateway")
+ is True
+ ):
+ add_command_to_interface(
+ interface,
+ "no fabric forwarding mode anycast-gateway",
+ commands,
+ )
+
+ for item in args:
+ candidate = w.get(item)
+ if candidate and candidate != obj_in_have.get(item):
+ cmd = item + " " + str(candidate)
+ add_command_to_interface(interface, cmd, commands)
+
+ if name and get_interface_type(name) == "ethernet":
+ if mode != obj_in_have.get("mode"):
+ admin_state = w.get("admin_state") or obj_in_have.get(
+ "admin_state"
+ )
+ if admin_state:
+ c1 = "interface {0}".format(
+ normalize_interface(w["name"])
+ )
+ c2 = get_admin_state(admin_state)
+ commands2.append(c1)
+ commands2.append(c2)
+
+ else:
+ commands.append(interface)
+ # Don't run switchport command for loopback and svi interfaces
+ if get_interface_type(name) in ("ethernet", "portchannel"):
+ if mode == "layer2":
+ commands.append("switchport")
+ elif mode == "layer3":
+ commands.append("no switchport")
+
+ if admin_state == "up":
+ commands.append("no shutdown")
+ elif admin_state == "down":
+ commands.append("shutdown")
+
+ if ip_forward == "enable":
+ commands.append("ip forward")
+ elif ip_forward == "disable":
+ commands.append("no ip forward")
+
+ if fabric_forwarding_anycast_gateway is True:
+ commands.append("fabric forwarding mode anycast-gateway")
+
+ elif fabric_forwarding_anycast_gateway is False:
+ commands.append(
+ "no fabric forwarding mode anycast-gateway"
+ )
+
+ for item in args:
+ candidate = w.get(item)
+ if candidate:
+ commands.append(item + " " + str(candidate))
+
+ elif state == "default":
+ if is_default is False:
+ commands.append("default interface {0}".format(name))
+ elif is_default == "DNE":
+ module.exit_json(
+ msg="interface you are trying to default does not exist"
+ )
+
+ return commands, commands2
+
+
+def map_params_to_obj(module):
+ obj = []
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ name = d["name"]
+ d["name"] = normalize_interface(name)
+ obj.append(d)
+
+ else:
+ obj.append(
+ {
+ "name": normalize_interface(module.params["name"]),
+ "description": module.params["description"],
+ "speed": module.params["speed"],
+ "mode": module.params["mode"],
+ "mtu": module.params["mtu"],
+ "duplex": module.params["duplex"],
+ "ip_forward": module.params["ip_forward"],
+ "fabric_forwarding_anycast_gateway": module.params[
+ "fabric_forwarding_anycast_gateway"
+ ],
+ "admin_state": module.params["admin_state"],
+ "state": module.params["state"],
+ "interface_type": module.params["interface_type"],
+ "tx_rate": module.params["tx_rate"],
+ "rx_rate": module.params["rx_rate"],
+ "neighbors": module.params["neighbors"],
+ }
+ )
+
+ return obj
+
+
+def map_config_to_obj(want, module):
+ objs = list()
+
+ for w in want:
+ obj = dict(
+ name=None,
+ description=None,
+ admin_state=None,
+ speed=None,
+ mtu=None,
+ mode=None,
+ duplex=None,
+ interface_type=None,
+ ip_forward=None,
+ fabric_forwarding_anycast_gateway=None,
+ )
+
+ if not w["name"]:
+ return obj
+
+ command = "show interface {0}".format(w["name"])
+ try:
+ body = execute_show_command(command, module)[0]
+ except IndexError:
+ return list()
+ if body:
+ try:
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ except (KeyError, TypeError):
+ return list()
+
+ if interface_table:
+ if interface_table.get("eth_mode") == "fex-fabric":
+ module.fail_json(
+ msg='nxos_interface does not support interfaces with mode "fex-fabric"'
+ )
+
+ intf_type = get_interface_type(w["name"])
+
+ if intf_type in ["portchannel", "ethernet"]:
+ mode = interface_table.get("eth_mode")
+ if mode in ("access", "trunk", "dot1q-tunnel"):
+ obj["mode"] = "layer2"
+ elif mode in ("routed", "layer3"):
+ obj["mode"] = "layer3"
+ else:
+ obj["mode"] = "layer3"
+
+ if intf_type == "ethernet":
+ obj["name"] = normalize_interface(
+ interface_table.get("interface")
+ )
+ obj["admin_state"] = interface_table.get("admin_state")
+ obj["description"] = interface_table.get("desc")
+ obj["mtu"] = interface_table.get("eth_mtu")
+ obj["duplex"] = interface_table.get("eth_duplex")
+
+ command = "show run interface {0}".format(obj["name"])
+ body = execute_show_command(command, module)[0]
+
+ speed_match = re.search(r"speed (\d+)", body)
+ if speed_match is None:
+ obj["speed"] = "auto"
+ else:
+ obj["speed"] = speed_match.group(1)
+
+ duplex_match = re.search(r"duplex (\S+)", body)
+ if duplex_match is None:
+ obj["duplex"] = "auto"
+ else:
+ obj["duplex"] = duplex_match.group(1)
+
+ if "ip forward" in body:
+ obj["ip_forward"] = "enable"
+ else:
+ obj["ip_forward"] = "disable"
+
+ elif intf_type == "svi":
+ obj["name"] = normalize_interface(
+ interface_table.get("interface")
+ )
+ attributes = get_vlan_interface_attributes(
+ obj["name"], intf_type, module
+ )
+ obj["admin_state"] = str(
+ attributes.get("admin_state", "nxapibug")
+ )
+ obj["description"] = str(
+ attributes.get("description", "nxapi_bug")
+ )
+ obj["mtu"] = interface_table.get("svi_mtu")
+
+ command = "show run interface {0}".format(obj["name"])
+ body = execute_show_command(command, module)[0]
+ if "ip forward" in body:
+ obj["ip_forward"] = "enable"
+ else:
+ obj["ip_forward"] = "disable"
+ if "fabric forwarding mode anycast-gateway" in body:
+ obj["fabric_forwarding_anycast_gateway"] = True
+ else:
+ obj["fabric_forwarding_anycast_gateway"] = False
+
+ elif intf_type in ("loopback", "management", "nve"):
+ obj["name"] = normalize_interface(
+ interface_table.get("interface")
+ )
+ obj["admin_state"] = interface_table.get("admin_state")
+ if obj["admin_state"] is None and intf_type == "loopback":
+ # Some platforms don't have the 'admin_state' key.
+ # For loopback interfaces it's safe to use the
+ # 'state' key instead.
+ obj["admin_state"] = interface_table.get("state")
+ obj["description"] = interface_table.get("desc")
+
+ elif intf_type == "portchannel":
+ obj["name"] = normalize_interface(
+ interface_table.get("interface")
+ )
+ obj["admin_state"] = interface_table.get("admin_state")
+ obj["description"] = interface_table.get("desc")
+ obj["mtu"] = interface_table.get("eth_mtu")
+
+ if obj["admin_state"] is None:
+ # Some nxos platforms do not have the 'admin_state' key.
+ # Use the 'state_rsn_desc' key instead to determine the
+ # admin state of the interface.
+ state_description = interface_table.get("state_rsn_desc")
+ if state_description == "Administratively down":
+ obj["admin_state"] = "down"
+ elif state_description is not None:
+ obj["admin_state"] = "up"
+
+ objs.append(obj)
+
+ return objs
+
+
+def check_declarative_intent_params(module, want):
+ failed_conditions = []
+ have_neighbors = None
+ for w in want:
+ if w["interface_type"]:
+ continue
+ want_tx_rate = w.get("tx_rate")
+ want_rx_rate = w.get("rx_rate")
+ want_neighbors = w.get("neighbors")
+ if not (want_tx_rate or want_rx_rate or want_neighbors):
+ continue
+
+ time.sleep(module.params["delay"])
+
+ cmd = [
+ {
+ "command": "show interface {0}".format(w["name"]),
+ "output": "text",
+ }
+ ]
+
+ try:
+ out = run_commands(module, cmd, check_rc=False)[0]
+ except (AttributeError, IndexError, TypeError):
+ out = ""
+
+ if want_tx_rate:
+ match = re.search(r"output rate (\d+)", out, re.M)
+ have_tx_rate = None
+
+ if match:
+ have_tx_rate = match.group(1)
+
+ if have_tx_rate is None or not conditional(
+ want_tx_rate, have_tx_rate.strip(), cast=int
+ ):
+ failed_conditions.append("tx_rate " + want_tx_rate)
+
+ if want_rx_rate:
+ match = re.search(r"input rate (\d+)", out, re.M)
+ have_rx_rate = None
+
+ if match:
+ have_rx_rate = match.group(1)
+
+ if have_rx_rate is None or not conditional(
+ want_rx_rate, have_rx_rate.strip(), cast=int
+ ):
+ failed_conditions.append("rx_rate " + want_rx_rate)
+
+ if want_neighbors:
+ have_host = []
+ have_port = []
+ if have_neighbors is None:
+ cmd = [
+ {
+ "command": "show lldp neighbors interface {0} detail".format(
+ w["name"]
+ ),
+ "output": "text",
+ }
+ ]
+ output = run_commands(module, cmd, check_rc=False)
+ if output:
+ have_neighbors = output[0]
+ else:
+ have_neighbors = ""
+ if (
+ have_neighbors
+ and "Total entries displayed: 0" not in have_neighbors
+ ):
+ for line in have_neighbors.strip().split("\n"):
+ if line.startswith("Port Description"):
+ have_port.append(line.split(": ")[1])
+ if line.startswith("System Name"):
+ have_host.append(line.split(": ")[1])
+
+ for item in want_neighbors:
+ host = item.get("host")
+ port = item.get("port")
+ if host and host not in have_host:
+ failed_conditions.append("host " + host)
+ if port and port not in have_port:
+ failed_conditions.append("port " + port)
+
+ return failed_conditions
+
+
+def main():
+ """ main entry point for module execution
+ """
+ neighbors_spec = dict(host=dict(), port=dict())
+
+ element_spec = dict(
+ name=dict(aliases=["interface"]),
+ admin_state=dict(default="up", choices=["up", "down"]),
+ description=dict(),
+ speed=dict(),
+ mode=dict(choices=["layer2", "layer3"]),
+ mtu=dict(),
+ duplex=dict(choices=["full", "half", "auto"]),
+ interface_type=dict(choices=["loopback", "portchannel", "svi", "nve"]),
+ ip_forward=dict(choices=["enable", "disable"]),
+ fabric_forwarding_anycast_gateway=dict(type="bool"),
+ tx_rate=dict(),
+ rx_rate=dict(),
+ neighbors=dict(type="list", elements="dict", options=neighbors_spec),
+ delay=dict(default=10, type="int"),
+ state=dict(
+ choices=["absent", "present", "default"], default="present"
+ ),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+ aggregate_spec["name"] = dict(required=True)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(
+ type="list",
+ elements="dict",
+ options=aggregate_spec,
+ mutually_exclusive=[["name", "interface_type"]],
+ )
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ required_one_of = [["name", "aggregate", "interface_type"]]
+ mutually_exclusive = [["name", "aggregate"], ["name", "interface_type"]]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=required_one_of,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+ warnings = list()
+
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(want, module)
+
+ commands = []
+ commands1, commands2 = map_obj_to_commands((want, have), module)
+ commands.extend(commands1)
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+ # if the mode changes from L2 to L3, the admin state
+ # seems to change after the API call, so adding a second API
+ # call to ensure it's in the desired state.
+ if commands2:
+ load_config(module, commands2)
+ commands.extend(commands2)
+ commands = [cmd for cmd in commands if cmd != "configure"]
+ result["commands"] = commands
+
+ if result["changed"]:
+ failed_conditions = check_declarative_intent_params(module, want)
+
+ if failed_conditions:
+ msg = "One or more conditional statements have not been satisfied"
+ module.fail_json(msg=msg, failed_conditions=failed_conditions)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface_ospf.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface_ospf.py
new file mode 100644
index 00000000..188682c8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interface_ospf.py
@@ -0,0 +1,614 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_interface_ospf
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-10-26) Manages configuration of an OSPF interface instance.
+description:
+- Manages configuration of an OSPF interface instance.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_ospf_interfaces
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-10-26'
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Default, where supported, restores params default value.
+- To remove an existing authentication configuration you should use C(message_digest_key_id=default)
+ plus all other options matching their existing values.
+- Loopback interfaces only support ospf network type 'point-to-point'.
+- C(state=absent) removes the whole OSPF interface configuration.
+options:
+ interface:
+ description:
+ - Name of this cisco_interface resource. Valid value is a string.
+ required: true
+ type: str
+ ospf:
+ description:
+ - Name of the ospf instance.
+ required: true
+ type: str
+ area:
+ description:
+ - Ospf area associated with this cisco_interface_ospf instance. Valid values are
+ a string, formatted as an IP address (i.e. "0.0.0.0") or as an integer.
+ required: true
+ type: str
+ bfd:
+ description:
+ - Enables bfd at interface level. This overrides the bfd variable set at the ospf
+ router level.
+ - Valid values are 'enable', 'disable' or 'default'.
+ - "Dependency: ''feature bfd''"
+ type: str
+ choices:
+ - enable
+ - disable
+ - default
+ cost:
+ description:
+ - The cost associated with this cisco_interface_ospf instance.
+ type: str
+ hello_interval:
+ description:
+ - Time between sending successive hello packets. Valid values are an integer or
+ the keyword 'default'.
+ type: str
+ dead_interval:
+ description:
+ - Time interval an ospf neighbor waits for a hello packet before tearing down
+ adjacencies. Valid values are an integer or the keyword 'default'.
+ type: str
+ passive_interface:
+ description:
+ - Enable or disable passive-interface state on this interface. true - (enable)
+ Prevent OSPF from establishing an adjacency or sending routing updates on this
+ interface. false - (disable) Override global 'passive-interface default' for
+ this interface.
+ type: bool
+ network:
+ description:
+ - Specifies interface ospf network type. Valid values are 'point-to-point' or
+ 'broadcast'.
+ choices:
+ - point-to-point
+ - broadcast
+ type: str
+ message_digest:
+ description:
+ - Enables or disables the usage of message digest authentication.
+ type: bool
+ message_digest_key_id:
+ description:
+ - Md5 authentication key-id associated with the ospf instance. If this is present,
+ message_digest_encryption_type, message_digest_algorithm_type and message_digest_password
+ are mandatory. Valid value is an integer and 'default'.
+ type: str
+ message_digest_algorithm_type:
+ description:
+ - Algorithm used for authentication among neighboring routers within an area.
+ Valid values are 'md5' and 'default'.
+ choices:
+ - md5
+ - default
+ type: str
+ message_digest_encryption_type:
+ description:
+ - Specifies the scheme used for encrypting message_digest_password. Valid values
+ are '3des' or 'cisco_type_7' encryption or 'default'.
+ choices:
+ - cisco_type_7
+ - 3des
+ - default
+ type: str
+ message_digest_password:
+ description:
+ - Specifies the message_digest password. Valid value is a string.
+ type: str
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+EXAMPLES = """
+- cisco.nxos.nxos_interface_ospf:
+ interface: ethernet1/32
+ ospf: 1
+ area: 1
+ bfd: disable
+ cost: default
+
+- cisco.nxos.nxos_interface_ospf:
+ interface: loopback0
+ ospf: prod
+ area: 0.0.0.0
+ bfd: enable
+ network: point-to-point
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface Ethernet1/32", "ip router ospf 1 area 0.0.0.1", "ip ospf bfd disable"]
+"""
+
+
+import re
+import struct
+import socket
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+BOOL_PARAMS = ["passive_interface", "message_digest"]
+PARAM_TO_COMMAND_KEYMAP = {
+ "interface": "",
+ "cost": "ip ospf cost",
+ "ospf": "ip router ospf",
+ "area": "ip router ospf",
+ "bfd": "ip ospf bfd",
+ "hello_interval": "ip ospf hello-interval",
+ "dead_interval": "ip ospf dead-interval",
+ "passive_interface": "ip ospf passive-interface",
+ "message_digest": "ip ospf authentication message-digest",
+ "message_digest_key_id": "ip ospf message-digest-key",
+ "message_digest_algorithm_type": "ip ospf message-digest-key",
+ "message_digest_encryption_type": "ip ospf message-digest-key",
+ "message_digest_password": "ip ospf message-digest-key",
+ "network": "ip ospf network",
+}
+
+
+def get_value(arg, config, module):
+ command = PARAM_TO_COMMAND_KEYMAP[arg]
+ has_command = re.search(r"\s+{0}\s*$".format(command), config, re.M)
+ has_command_val = re.search(
+ r"(?:{0}\s)(?P<value>.*)$".format(command), config, re.M
+ )
+
+ if command == "ip router ospf":
+ value = ""
+ if has_command_val:
+ value_list = has_command_val.group("value").split()
+ if arg == "ospf":
+ value = value_list[0]
+ elif arg == "area":
+ value = value_list[2]
+ value = normalize_area(value, module)
+ elif command == "ip ospf message-digest-key":
+ value = ""
+ if has_command_val:
+ value_list = has_command_val.group("value").split()
+ if arg == "message_digest_key_id":
+ value = value_list[0]
+ elif arg == "message_digest_algorithm_type":
+ value = value_list[1]
+ elif arg == "message_digest_encryption_type":
+ value = value_list[2]
+ if value == "3":
+ value = "3des"
+ elif value == "7":
+ value = "cisco_type_7"
+ elif arg == "message_digest_password":
+ value = value_list[3]
+ elif arg == "passive_interface":
+ has_no_command = re.search(
+ r"\s+no\s+{0}\s*$".format(command), config, re.M
+ )
+ if has_no_command:
+ value = False
+ elif has_command:
+ value = True
+ else:
+ value = None
+ elif arg == "bfd":
+ m = re.search(r"\s*ip ospf bfd(?P<disable> disable)?", config)
+ if m:
+ value = "disable" if m.group("disable") else "enable"
+ else:
+ value = "default"
+ elif arg in BOOL_PARAMS:
+ value = bool(has_command)
+ else:
+ value = ""
+ if has_command_val:
+ value = has_command_val.group("value")
+ return value
+
+
+def get_existing(module, args):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+ if module.params["interface"].startswith("loopback") or module.params[
+ "interface"
+ ].startswith("port-channel"):
+ parents = ["interface {0}".format(module.params["interface"])]
+ else:
+ parents = [
+ "interface {0}".format(module.params["interface"].capitalize())
+ ]
+ config = netcfg.get_section(parents)
+ if "ospf" in config:
+ for arg in args:
+ if arg not in ["interface"]:
+ existing[arg] = get_value(arg, config, module)
+ existing["interface"] = module.params["interface"]
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def get_default_commands(existing, proposed, existing_commands, key, module):
+ commands = list()
+ existing_value = existing_commands.get(key)
+ if key.startswith("ip ospf message-digest-key"):
+ check = False
+ for param in [
+ "message_digest_encryption_type",
+ "message_digest_algorithm_type",
+ "message_digest_password",
+ ]:
+ if existing[param] == proposed[param]:
+ check = True
+ if check:
+ if existing["message_digest_encryption_type"] == "3des":
+ encryption_type = "3"
+ elif existing["message_digest_encryption_type"] == "cisco_type_7":
+ encryption_type = "7"
+ command = "no {0} {1} {2} {3} {4}".format(
+ key,
+ existing["message_digest_key_id"],
+ existing["message_digest_algorithm_type"],
+ encryption_type,
+ existing["message_digest_password"],
+ )
+ commands.append(command)
+ elif "ip ospf bfd" in key:
+ commands.append("no {0}".format(key))
+ elif "passive-interface" in key:
+ commands.append("default ip ospf passive-interface")
+ else:
+ commands.append("no {0} {1}".format(key, existing_value))
+ return commands
+
+
+def get_custom_command(existing_cmd, proposed, key, module):
+ commands = list()
+
+ if key == "ip router ospf":
+ command = "{0} {1} area {2}".format(
+ key, proposed["ospf"], proposed["area"]
+ )
+ if command not in existing_cmd:
+ commands.append(command)
+
+ if key == "ip ospf network":
+ command = "{0} {1}".format(key, proposed["network"])
+
+ if command not in existing_cmd:
+ commands.append(command)
+
+ elif key.startswith("ip ospf message-digest-key"):
+ if (
+ proposed["message_digest_key_id"] != "default"
+ and "options" not in key
+ ):
+ if proposed["message_digest_encryption_type"] == "3des":
+ encryption_type = "3"
+ elif proposed["message_digest_encryption_type"] == "cisco_type_7":
+ encryption_type = "7"
+ command = "{0} {1} {2} {3} {4}".format(
+ key,
+ proposed["message_digest_key_id"],
+ proposed["message_digest_algorithm_type"],
+ encryption_type,
+ proposed["message_digest_password"],
+ )
+ commands.append(command)
+ return commands
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, value in proposed_commands.items():
+ if existing_commands.get(key):
+ if key == "ip router ospf":
+ if proposed["area"] == existing["area"]:
+ continue
+ if existing_commands[key] == proposed_commands[key]:
+ continue
+
+ if key == "ip ospf passive-interface" and module.params.get(
+ "interface"
+ ).upper().startswith("LO"):
+ module.fail_json(
+ msg="loopback interface does not support passive_interface"
+ )
+ if (
+ key == "ip ospf network"
+ and value == "broadcast"
+ and module.params.get("interface").upper().startswith("LO")
+ ):
+ module.fail_json(
+ msg="loopback interface does not support ospf network type broadcast"
+ )
+
+ if key == "ip ospf bfd":
+ cmd = key
+ if "disable" in value:
+ cmd += " disable"
+ elif "default" in value and existing.get("bfd") is not None:
+ cmd = "no " + cmd
+ commands.append(cmd)
+ continue
+
+ if value is True:
+ commands.append(key)
+ elif value is False:
+ commands.append("no {0}".format(key))
+ elif value == "default":
+ if existing_commands.get(key):
+ commands.extend(
+ get_default_commands(
+ existing, proposed, existing_commands, key, module
+ )
+ )
+ else:
+ if key == "ip router ospf" or key.startswith(
+ "ip ospf message-digest-key"
+ ):
+ commands.extend(
+ get_custom_command(commands, proposed, key, module)
+ )
+ else:
+ command = "{0} {1}".format(key, value.lower())
+ commands.append(command)
+
+ if commands:
+ parents = [
+ "interface {0}".format(module.params["interface"].capitalize())
+ ]
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, existing, proposed, candidate):
+ commands = []
+ parents = ["interface {0}".format(module.params["interface"].capitalize())]
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, value in existing_commands.items():
+ if "ip ospf bfd" in key:
+ if "default" not in value:
+ # cli is present when enabled or disabled; this removes either case
+ commands.append("no ip ospf bfd")
+ continue
+ if "ip ospf passive-interface" in key and value is not None:
+ # cli is present for both enabled or disabled; 'no' will not remove
+ commands.append("default ip ospf passive-interface")
+ continue
+
+ if value:
+ if key.startswith("ip ospf message-digest-key"):
+ if "options" not in key:
+ if existing["message_digest_encryption_type"] == "3des":
+ encryption_type = "3"
+ elif (
+ existing["message_digest_encryption_type"]
+ == "cisco_type_7"
+ ):
+ encryption_type = "7"
+ command = "no {0} {1} {2} {3} {4}".format(
+ key,
+ existing["message_digest_key_id"],
+ existing["message_digest_algorithm_type"],
+ encryption_type,
+ existing["message_digest_password"],
+ )
+ commands.append(command)
+ elif key in [
+ "ip ospf authentication message-digest",
+ "ip ospf network",
+ ]:
+ if value:
+ commands.append("no {0}".format(key))
+ elif key == "ip router ospf":
+ command = "no {0} {1} area {2}".format(
+ key, proposed["ospf"], proposed["area"]
+ )
+ if command not in commands:
+ commands.append(command)
+ else:
+ existing_value = existing_commands.get(key)
+ commands.append("no {0} {1}".format(key, existing_value))
+
+ candidate.add(commands, parents=parents)
+
+
+def normalize_area(area, module):
+ try:
+ area = int(area)
+ area = socket.inet_ntoa(struct.pack("!L", area))
+ except ValueError:
+ splitted_area = area.split(".")
+ if len(splitted_area) != 4:
+ module.fail_json(msg="Incorrect Area ID format", area=area)
+ return area
+
+
+def main():
+ argument_spec = dict(
+ interface=dict(required=True, type="str"),
+ ospf=dict(required=True, type="str"),
+ area=dict(required=True, type="str"),
+ bfd=dict(
+ choices=["enable", "disable", "default"],
+ required=False,
+ type="str",
+ ),
+ cost=dict(required=False, type="str"),
+ hello_interval=dict(required=False, type="str"),
+ dead_interval=dict(required=False, type="str"),
+ passive_interface=dict(required=False, type="bool"),
+ network=dict(
+ required=False, type="str", choices=["broadcast", "point-to-point"]
+ ),
+ message_digest=dict(required=False, type="bool"),
+ message_digest_key_id=dict(required=False, type="str"),
+ message_digest_algorithm_type=dict(
+ required=False, type="str", choices=["md5", "default"]
+ ),
+ message_digest_encryption_type=dict(
+ required=False,
+ type="str",
+ choices=["cisco_type_7", "3des", "default"],
+ ),
+ message_digest_password=dict(required=False, type="str", no_log=True),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_together=[
+ [
+ "message_digest_key_id",
+ "message_digest_algorithm_type",
+ "message_digest_encryption_type",
+ "message_digest_password",
+ ]
+ ],
+ supports_check_mode=True,
+ )
+
+ # Normalize interface input data.
+ #
+ # * For port-channel and loopback interfaces expection is all lower case names.
+ # * All other interfaces the expectation is an uppercase leading character
+ # followed by lower case characters.
+ #
+ if re.match(r"(port-channel|loopback)", module.params["interface"], re.I):
+ module.params["interface"] = module.params["interface"].lower()
+ else:
+ module.params["interface"] = module.params["interface"].capitalize()
+
+ warnings = list()
+ result = {"changed": False, "commands": [], "warnings": warnings}
+
+ for param in [
+ "message_digest_encryption_type",
+ "message_digest_algorithm_type",
+ "message_digest_password",
+ ]:
+ if (
+ module.params[param] == "default"
+ and module.params["message_digest_key_id"] != "default"
+ ):
+ module.exit_json(
+ msg="Use message_digest_key_id=default to remove an existing authentication configuration"
+ )
+
+ state = module.params["state"]
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+
+ existing = get_existing(module, args)
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key != "interface":
+ if str(value).lower() == "true":
+ value = True
+ elif str(value).lower() == "false":
+ value = False
+ elif str(value).lower() == "default":
+ value = "default"
+ elif key == "bfd":
+ value = str(value).lower()
+ if existing.get(key) or (not existing.get(key) and value):
+ proposed[key] = value
+ elif (
+ "passive_interface" in key
+ and existing.get(key) is None
+ and value is False
+ ):
+ proposed[key] = value
+
+ proposed["area"] = normalize_area(proposed["area"], module)
+ if "hello_interval" in proposed and proposed["hello_interval"] == "10":
+ proposed["hello_interval"] = "default"
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ elif (
+ state == "absent"
+ and existing.get("ospf") == proposed["ospf"]
+ and existing.get("area") == proposed["area"]
+ ):
+ state_absent(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ if not module.check_mode:
+ load_config(module, candidate)
+ result["changed"] = True
+ result["commands"] = candidate
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interfaces.py
new file mode 100644
index 00000000..b3ec69ac
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_interfaces.py
@@ -0,0 +1,403 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_interfaces
+short_description: Interfaces resource module
+description: This module manages the interface attributes of NX-OS interfaces.
+version_added: 1.0.0
+author: Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section ^interface)
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A dictionary of interface options
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Full name of interface, e.g. Ethernet1/1, port-channel10.
+ type: str
+ required: true
+ description:
+ description:
+ - Interface description.
+ type: str
+ enabled:
+ description:
+ - Administrative state of the interface. Set the value to C(true) to administratively
+ enable the interface or C(false) to disable it
+ type: bool
+ speed:
+ description:
+ - Interface link speed. Applicable for Ethernet interfaces only.
+ type: str
+ mode:
+ description:
+ - Manage Layer2 or Layer3 state of the interface. Applicable for Ethernet
+ and port channel interfaces only.
+ choices:
+ - layer2
+ - layer3
+ type: str
+ mtu:
+ description:
+ - MTU for a specific interface. Must be an even number between 576 and 9216.
+ Applicable for Ethernet interfaces only.
+ type: str
+ duplex:
+ description:
+ - Interface link status. Applicable for Ethernet interfaces only.
+ type: str
+ choices:
+ - full
+ - half
+ - auto
+ ip_forward:
+ description:
+ - Enable or disable IP forward feature on SVIs. Set the value to C(true) to
+ enable or C(false) to disable.
+ type: bool
+ fabric_forwarding_anycast_gateway:
+ description:
+ - Associate SVI with anycast gateway under VLAN configuration mode. Applicable
+ for SVI interfaces only.
+ type: bool
+ state:
+ description:
+ - The state of the configuration after module completion
+ - The state I(rendered) considers the system default mode for interfaces to be
+ "Layer 3" and the system default state for interfaces to be shutdown.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# description testing
+# mtu 1800
+
+- name: Merge provided configuration with device configuration
+ cisco.nxos.nxos_interfaces:
+ config:
+ - name: Ethernet1/1
+ description: Configured by Ansible
+ enabled: true
+ - name: Ethernet1/2
+ description: Configured by Ansible Network
+ enabled: false
+ state: merged
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# description Configured by Ansible
+# no shutdown
+# mtu 1800
+# interface Ethernet2
+# description Configured by Ansible Network
+# shutdown
+
+
+# Using replaced
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# description Interface 1/1
+# interface Ethernet1/2
+
+- name: Replaces device configuration of listed interfaces with provided configuration
+ cisco.nxos.nxos_interfaces:
+ config:
+ - name: Ethernet1/1
+ description: Configured by Ansible
+ enabled: true
+ mtu: 2000
+ - name: Ethernet1/2
+ description: Configured by Ansible Network
+ enabled: false
+ mode: layer2
+ state: replaced
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# description Configured by Ansible
+# no shutdown
+# mtu 1500
+# interface Ethernet2/2
+# description Configured by Ansible Network
+# shutdown
+# switchport
+
+
+# Using overridden
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# description Interface Ethernet1/1
+# interface Ethernet1/2
+# interface mgmt0
+# description Management interface
+# ip address dhcp
+
+- name: Override device configuration of all interfaces with provided configuration
+ cisco.nxos.nxos_interfaces:
+ config:
+ - name: Ethernet1/1
+ enabled: true
+ - name: Ethernet1/2
+ description: Configured by Ansible Network
+ enabled: false
+ state: overridden
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# interface Ethernet1/2
+# description Configured by Ansible Network
+# shutdown
+# interface mgmt0
+# ip address dhcp
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# description Interface Ethernet1/1
+# interface Ethernet1/2
+# interface mgmt0
+# description Management interface
+# ip address dhcp
+
+- name: Delete or return interface parameters to default settings
+ cisco.nxos.nxos_interfaces:
+ config:
+ - name: Ethernet1/1
+ state: deleted
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# interface Ethernet1/2
+# interface mgmt0
+# description Management interface
+# ip address dhcp
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_interfaces:
+ config:
+ - name: Ethernet1/1
+ description: outbound-intf
+ mode: layer3
+ speed: 100
+ - name: Ethernet1/2
+ mode: layer2
+ enabled: true
+ duplex: full
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/1"
+# - "description outbound-intf"
+# - "speed 100"
+# - "interface Ethernet1/2"
+# - "switchport"
+# - "duplex full"
+# - "no shutdown"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# interface Ethernet1/800
+# description test-1
+# speed 1000
+# shutdown
+# no switchport
+# duplex half
+# interface Ethernet1/801
+# description test-2
+# switchport
+# no shutdown
+# mtu 1800
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# parsed:
+# - description: "test-1"
+# duplex: "half"
+# enabled: false
+# mode: "layer3"
+# name: "Ethernet1/800"
+# speed: "1000"
+#
+# - description: "test-2"
+# enabled: true
+# mode: "layer2"
+# mtu: "1800"
+# name: "Ethernet1/801"
+
+# Using gathered
+
+# Existing device config state
+# -----------------------------
+# interface Ethernet1/1
+# description outbound-intf
+# switchport
+# no shutdown
+# interface Ethernet1/2
+# description intf-l3
+# speed 1000
+# interface Ethernet1/3
+# interface Ethernet1/4
+# interface Ethernet1/5
+
+- name: Gather interfaces facts from the device using nxos_interfaces
+ cisco.nxos.nxos_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# - name: Ethernet1/1
+# description: outbound-intf
+# mode: layer2
+# enabled: True
+# - name: Ethernet1/2
+# description: intf-l3
+# speed: "1000"
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface Ethernet1/1', 'mtu 1800']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.interfaces.interfaces import (
+ InterfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.interfaces.interfaces import (
+ Interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=InterfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interface.py
new file mode 100644
index 00000000..867a7da3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interface.py
@@ -0,0 +1,702 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2017, Ansible by Red Hat, inc
+# 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 = """
+module: nxos_l2_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manage Layer-2
+ interface on Cisco NXOS devices.
+description:
+- This module provides declarative management of Layer-2 interface on Cisco NXOS devices.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_l2_interfaces
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+author:
+- Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOSv 7.0(3)I5(1).
+options:
+ name:
+ description:
+ - Full name of the interface excluding any logical unit number, i.e. Ethernet1/1.
+ aliases:
+ - interface
+ type: str
+ mode:
+ description:
+ - Mode in which interface needs to be configured.
+ choices:
+ - access
+ - trunk
+ type: str
+ access_vlan:
+ description:
+ - Configure given VLAN in access port. If C(mode=access), used as the access VLAN
+ ID.
+ type: str
+ native_vlan:
+ description:
+ - Native VLAN to be configured in trunk port. If C(mode=trunk), used as the trunk
+ native VLAN ID.
+ type: str
+ trunk_vlans:
+ description:
+ - List of VLANs to be configured in trunk port. If C(mode=trunk), used as the
+ VLAN range to ADD or REMOVE from the trunk.
+ aliases:
+ - trunk_add_vlans
+ type: str
+ trunk_allowed_vlans:
+ description:
+ - List of allowed VLANs in a given trunk port. If C(mode=trunk), these are the
+ only VLANs that will be configured on the trunk, i.e. "2-10,15".
+ type: str
+ aggregate:
+ description:
+ - List of Layer-2 interface definitions.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Full name of the interface excluding any logical unit number, i.e. Ethernet1/1.
+ aliases:
+ - interface
+ type: str
+ mode:
+ description:
+ - Mode in which interface needs to be configured.
+ choices:
+ - access
+ - trunk
+ type: str
+ access_vlan:
+ description:
+ - Configure given VLAN in access port. If C(mode=access), used as the access VLAN
+ ID.
+ type: str
+ native_vlan:
+ description:
+ - Native VLAN to be configured in trunk port. If C(mode=trunk), used as the trunk
+ native VLAN ID.
+ type: str
+ trunk_vlans:
+ description:
+ - List of VLANs to be configured in trunk port. If C(mode=trunk), used as the
+ VLAN range to ADD or REMOVE from the trunk.
+ aliases:
+ - trunk_add_vlans
+ type: str
+ trunk_allowed_vlans:
+ description:
+ - List of allowed VLANs in a given trunk port. If C(mode=trunk), these are the
+ only VLANs that will be configured on the trunk, i.e. "2-10,15".
+ type: str
+ state:
+ description:
+ - Manage the state of the Layer-2 Interface configuration.
+ choices:
+ - present
+ - absent
+ - unconfigured
+ type: str
+
+ state:
+ description:
+ - Manage the state of the Layer-2 Interface configuration.
+ default: present
+ choices:
+ - present
+ - absent
+ - unconfigured
+ type: str
+
+
+"""
+
+EXAMPLES = """
+- name: Ensure Eth1/5 is in its default l2 interface state
+ cisco.nxos.nxos_l2_interface:
+ name: Ethernet1/5
+ state: unconfigured
+
+- name: Ensure Eth1/5 is configured for access vlan 20
+ cisco.nxos.nxos_l2_interface:
+ name: Ethernet1/5
+ mode: access
+ access_vlan: 20
+
+- name: Ensure Eth1/5 only has vlans 5-10 as trunk vlans
+ cisco.nxos.nxos_l2_interface:
+ name: Ethernet1/5
+ mode: trunk
+ native_vlan: 10
+ trunk_vlans: 5-10
+
+- name: Ensure eth1/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean
+ others aren't also being tagged)
+ cisco.nxos.nxos_l2_interface:
+ name: Ethernet1/5
+ mode: trunk
+ native_vlan: 10
+ trunk_vlans: 2-50
+
+- name: Ensure these VLANs are not being tagged on the trunk
+ cisco.nxos.nxos_l2_interface:
+ name: Ethernet1/5
+ mode: trunk
+ trunk_vlans: 51-4094
+ state: absent
+
+- name: Aggregate Configure interfaces for access_vlan with aggregate
+ cisco.nxos.nxos_l2_interface:
+ aggregate:
+ - {name: Ethernet1/2, access_vlan: 6}
+ - {name: Ethernet1/7, access_vlan: 15}
+ mode: access
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always, except for the platforms that use Netconf transport to manage the device.
+ type: list
+ sample:
+ - interface eth1/5
+ - switchport access vlan 20
+"""
+
+from copy import deepcopy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ get_interface_type,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+
+
+def get_interface_mode(name, module):
+ """Gets current mode of interface: layer2 or layer3
+ Args:
+ device (Device): This is the device object of an NX-API enabled device
+ using the Device class within device.py
+ interface (string): full name of interface, i.e. Ethernet1/1,
+ loopback10, port-channel20, vlan20
+ Returns:
+ str: 'layer2' or 'layer3'
+ """
+ command = "show interface {0} | json".format(name)
+ intf_type = get_interface_type(name)
+ mode = "unknown"
+ interface_table = {}
+
+ try:
+ body = run_commands(module, [command])[0]
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ except (KeyError, AttributeError, IndexError):
+ return mode
+
+ if interface_table:
+ # HACK FOR NOW
+ if intf_type in ["ethernet", "portchannel"]:
+ mode = str(interface_table.get("eth_mode", "layer3"))
+ if mode in ["access", "trunk"]:
+ mode = "layer2"
+ if mode == "routed":
+ mode = "layer3"
+ elif intf_type == "loopback" or intf_type == "svi":
+ mode = "layer3"
+ return mode
+
+
+def interface_is_portchannel(name, module):
+ """Checks to see if an interface is part of portchannel bundle
+ Args:
+ interface (str): full name of interface, i.e. Ethernet1/1
+ Returns:
+ True/False based on if interface is a member of a portchannel bundle
+ """
+ intf_type = get_interface_type(name)
+
+ if intf_type == "ethernet":
+ command = "show interface {0} | json".format(name)
+ try:
+ body = run_commands(module, [command])[0]
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ except (KeyError, AttributeError, IndexError):
+ interface_table = None
+
+ if interface_table:
+ state = interface_table.get("eth_bundle")
+ if state:
+ return True
+ else:
+ return False
+
+ return False
+
+
+def get_switchport(port, module):
+ """Gets current config of L2 switchport
+ Args:
+ device (Device): This is the device object of an NX-API enabled device
+ using the Device class within device.py
+ port (str): full name of interface, i.e. Ethernet1/1
+ Returns:
+ dictionary with k/v pairs for L2 vlan config
+ """
+
+ command = "show interface {0} switchport | json".format(port)
+
+ try:
+ body = run_commands(module, [command])[0]
+ sp_table = body["TABLE_interface"]["ROW_interface"]
+ except (KeyError, AttributeError, IndexError):
+ sp_table = None
+
+ if sp_table:
+ key_map = {
+ "interface": "name",
+ "oper_mode": "mode",
+ "switchport": "switchport",
+ "access_vlan": "access_vlan",
+ "access_vlan_name": "access_vlan_name",
+ "native_vlan": "native_vlan",
+ "native_vlan_name": "native_vlan_name",
+ "trunk_vlans": "trunk_vlans",
+ }
+ sp = apply_key_map(key_map, sp_table)
+ return sp
+
+ else:
+ return {}
+
+
+def remove_switchport_config_commands(name, existing, proposed, module):
+ mode = proposed.get("mode")
+ commands = []
+ command = None
+
+ if mode == "access":
+ av_check = existing.get("access_vlan") == proposed.get("access_vlan")
+ if av_check:
+ command = "no switchport access vlan {0}".format(
+ existing.get("access_vlan")
+ )
+ commands.append(command)
+
+ elif mode == "trunk":
+ existing_vlans = existing.get("trunk_vlans_list")
+ proposed_vlans = proposed.get("trunk_vlans_list")
+ vlans_to_remove = set(proposed_vlans).intersection(existing_vlans)
+
+ if vlans_to_remove:
+ proposed_allowed_vlans = proposed.get("trunk_allowed_vlans")
+ remove_trunk_allowed_vlans = proposed.get(
+ "trunk_vlans", proposed_allowed_vlans
+ )
+ command = "switchport trunk allowed vlan remove {0}".format(
+ remove_trunk_allowed_vlans
+ )
+ commands.append(command)
+
+ native_check = existing.get("native_vlan") == proposed.get(
+ "native_vlan"
+ )
+ if native_check and proposed.get("native_vlan"):
+ command = "no switchport trunk native vlan {0}".format(
+ existing.get("native_vlan")
+ )
+ commands.append(command)
+
+ if commands:
+ commands.insert(0, "interface " + name)
+ return commands
+
+
+def get_switchport_config_commands(name, existing, proposed, module):
+ """Gets commands required to config a given switchport interface
+ """
+
+ proposed_mode = proposed.get("mode")
+ existing_mode = existing.get("mode")
+ commands = []
+ command = None
+
+ if proposed_mode != existing_mode:
+ if proposed_mode == "trunk":
+ command = "switchport mode trunk"
+ elif proposed_mode == "access":
+ command = "switchport mode access"
+
+ if command:
+ commands.append(command)
+
+ if proposed_mode == "access":
+ av_check = str(existing.get("access_vlan")) == str(
+ proposed.get("access_vlan")
+ )
+ if not av_check:
+ command = "switchport access vlan {0}".format(
+ proposed.get("access_vlan")
+ )
+ commands.append(command)
+
+ elif proposed_mode == "trunk":
+ tv_check = existing.get("trunk_vlans_list") == proposed.get(
+ "trunk_vlans_list"
+ )
+
+ if not tv_check:
+ if proposed.get("allowed"):
+ command = "switchport trunk allowed vlan {0}".format(
+ proposed.get("trunk_allowed_vlans")
+ )
+ commands.append(command)
+
+ else:
+ existing_vlans = existing.get("trunk_vlans_list")
+ proposed_vlans = proposed.get("trunk_vlans_list")
+ vlans_to_add = set(proposed_vlans).difference(existing_vlans)
+ if vlans_to_add:
+ command = "switchport trunk allowed vlan add {0}".format(
+ proposed.get("trunk_vlans")
+ )
+ commands.append(command)
+
+ native_check = str(existing.get("native_vlan")) == str(
+ proposed.get("native_vlan")
+ )
+ if not native_check and proposed.get("native_vlan"):
+ command = "switchport trunk native vlan {0}".format(
+ proposed.get("native_vlan")
+ )
+ commands.append(command)
+
+ if commands:
+ commands.insert(0, "interface " + name)
+ return commands
+
+
+def is_switchport_default(existing):
+ """Determines if switchport has a default config based on mode
+ Args:
+ existing (dict): existing switchport configuration from Ansible mod
+ Returns:
+ boolean: True if switchport has OOB Layer 2 config, i.e.
+ vlan 1 and trunk all and mode is access
+ """
+
+ c1 = str(existing["access_vlan"]) == "1"
+ c2 = str(existing["native_vlan"]) == "1"
+ c3 = existing["trunk_vlans"] == "1-4094"
+ c4 = existing["mode"] == "access"
+
+ default = c1 and c2 and c3 and c4
+
+ return default
+
+
+def default_switchport_config(name):
+ commands = []
+ commands.append("interface " + name)
+ commands.append("switchport mode access")
+ commands.append("switch access vlan 1")
+ commands.append("switchport trunk native vlan 1")
+ commands.append("switchport trunk allowed vlan all")
+ return commands
+
+
+def vlan_range_to_list(vlans):
+ result = []
+ if vlans:
+ for part in vlans.split(","):
+ if part == "none":
+ break
+ if "-" in part:
+ a, b = part.split("-")
+ a, b = int(a), int(b)
+ result.extend(range(a, b + 1))
+ else:
+ a = int(part)
+ result.append(a)
+ return numerical_sort(result)
+ return result
+
+
+def get_list_of_vlans(module):
+
+ command = "show vlan | json"
+ vlan_list = []
+
+ try:
+ body = run_commands(module, [command])[0]
+ vlan_table = body["TABLE_vlanbrief"]["ROW_vlanbrief"]
+ except (KeyError, AttributeError, IndexError):
+ return []
+
+ if isinstance(vlan_table, list):
+ for vlan in vlan_table:
+ vlan_list.append(str(vlan["vlanshowbr-vlanid-utf"]))
+ else:
+ vlan_list.append("1")
+
+ return vlan_list
+
+
+def numerical_sort(string_int_list):
+ """Sorts list of strings/integers that are digits in numerical order.
+ """
+
+ as_int_list = []
+ as_str_list = []
+ for vlan in string_int_list:
+ as_int_list.append(int(vlan))
+ as_int_list.sort()
+ for vlan in as_int_list:
+ as_str_list.append(str(vlan))
+ return as_str_list
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = str(value)
+ return new_dict
+
+
+def apply_value_map(value_map, resource):
+ for key, value in value_map.items():
+ resource[key] = value[resource.get(key)]
+ return resource
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def map_params_to_obj(module):
+ obj = []
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ obj.append(d)
+ else:
+ obj.append(
+ {
+ "name": module.params["name"],
+ "mode": module.params["mode"],
+ "access_vlan": module.params["access_vlan"],
+ "native_vlan": module.params["native_vlan"],
+ "trunk_vlans": module.params["trunk_vlans"],
+ "trunk_allowed_vlans": module.params["trunk_allowed_vlans"],
+ "state": module.params["state"],
+ }
+ )
+
+ return obj
+
+
+def main():
+ """ main entry point for module execution
+ """
+ element_spec = dict(
+ name=dict(type="str", aliases=["interface"]),
+ mode=dict(choices=["access", "trunk"]),
+ access_vlan=dict(type="str"),
+ native_vlan=dict(type="str"),
+ trunk_vlans=dict(type="str", aliases=["trunk_add_vlans"]),
+ trunk_allowed_vlans=dict(type="str"),
+ state=dict(
+ choices=["absent", "present", "unconfigured"], default="present"
+ ),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec)
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[
+ ["access_vlan", "trunk_vlans"],
+ ["access_vlan", "native_vlan"],
+ ["access_vlan", "trunk_allowed_vlans"],
+ ],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ commands = []
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ for w in want:
+ name = w["name"]
+ mode = w["mode"]
+ access_vlan = w["access_vlan"]
+ state = w["state"]
+ trunk_vlans = w["trunk_vlans"]
+ native_vlan = w["native_vlan"]
+ trunk_allowed_vlans = w["trunk_allowed_vlans"]
+
+ args = dict(
+ name=name,
+ mode=mode,
+ access_vlan=access_vlan,
+ native_vlan=native_vlan,
+ trunk_vlans=trunk_vlans,
+ trunk_allowed_vlans=trunk_allowed_vlans,
+ )
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ name = name.lower()
+
+ if mode == "access" and state == "present" and not access_vlan:
+ module.fail_json(
+ msg="access_vlan param is required when mode=access && state=present"
+ )
+
+ if mode == "trunk" and access_vlan:
+ module.fail_json(
+ msg="access_vlan param not supported when using mode=trunk"
+ )
+
+ current_mode = get_interface_mode(name, module)
+
+ # Current mode will return layer3, layer2, or unknown
+ if current_mode == "unknown" or current_mode == "layer3":
+ module.fail_json(
+ msg="Ensure interface is configured to be a L2"
+ "\nport first before using this module. You can use"
+ "\nthe nxos_interface module for this."
+ )
+
+ if interface_is_portchannel(name, module):
+ module.fail_json(
+ msg="Cannot change L2 config on physical "
+ "\nport because it is in a portchannel. "
+ "\nYou should update the portchannel config."
+ )
+
+ # existing will never be null for Eth intfs as there is always a default
+ existing = get_switchport(name, module)
+
+ # Safeguard check
+ # If there isn't an existing, something is wrong per previous comment
+ if not existing:
+ module.fail_json(
+ msg="Make sure you are using the FULL interface name"
+ )
+
+ if trunk_vlans or trunk_allowed_vlans:
+ if trunk_vlans:
+ trunk_vlans_list = vlan_range_to_list(trunk_vlans)
+ elif trunk_allowed_vlans:
+ trunk_vlans_list = vlan_range_to_list(trunk_allowed_vlans)
+ proposed["allowed"] = True
+
+ existing_trunks_list = vlan_range_to_list(
+ (existing["trunk_vlans"])
+ )
+
+ existing["trunk_vlans_list"] = existing_trunks_list
+ proposed["trunk_vlans_list"] = trunk_vlans_list
+
+ current_vlans = get_list_of_vlans(module)
+
+ if state == "present":
+ if access_vlan and access_vlan not in current_vlans:
+ module.fail_json(
+ msg="You are trying to configure a VLAN"
+ " on an interface that\ndoes not exist on the "
+ " switch yet!",
+ vlan=access_vlan,
+ )
+ elif native_vlan and native_vlan not in current_vlans:
+ module.fail_json(
+ msg="You are trying to configure a VLAN"
+ " on an interface that\ndoes not exist on the "
+ " switch yet!",
+ vlan=native_vlan,
+ )
+ else:
+ command = get_switchport_config_commands(
+ name, existing, proposed, module
+ )
+ commands.append(command)
+ elif state == "unconfigured":
+ is_default = is_switchport_default(existing)
+ if not is_default:
+ command = default_switchport_config(name)
+ commands.append(command)
+ elif state == "absent":
+ command = remove_switchport_config_commands(
+ name, existing, proposed, module
+ )
+ commands.append(command)
+
+ if trunk_vlans or trunk_allowed_vlans:
+ existing.pop("trunk_vlans_list")
+ proposed.pop("trunk_vlans_list")
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ result["commands"] = cmds
+ result["warnings"] = warnings
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interfaces.py
new file mode 100644
index 00000000..1d7c4456
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l2_interfaces.py
@@ -0,0 +1,404 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_l2_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_l2_interfaces
+short_description: L2 interfaces resource module
+description: This module manages Layer-2 interfaces attributes of NX-OS Interfaces.
+version_added: 1.0.0
+author: Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section ^interface).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A dictionary of Layer-2 interface options
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Full name of interface, i.e. Ethernet1/1.
+ type: str
+ required: true
+ access:
+ description:
+ - Switchport mode access command to configure the interface as a Layer-2 access.
+ type: dict
+ suboptions:
+ vlan:
+ description:
+ - Configure given VLAN in access port. It's used as the access VLAN ID.
+ type: int
+ trunk:
+ description:
+ - Switchport mode trunk command to configure the interface as a Layer-2 trunk.
+ type: dict
+ suboptions:
+ native_vlan:
+ description:
+ - Native VLAN to be configured in trunk port. It is used as the trunk
+ native VLAN ID.
+ type: int
+ allowed_vlans:
+ description:
+ - List of allowed VLANs in a given trunk port. These are the only VLANs
+ that will be configured on the trunk.
+ type: str
+ mode:
+ description:
+ - Mode in which interface needs to be configured.
+ - Access mode is not shown in interface facts, so idempotency will not be
+ maintained for switchport mode access and every time the output will come
+ as changed=True.
+ type: str
+ choices:
+ - access
+ - trunk
+ - fex-fabric
+ - fabricpath
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# switchport access vlan 20
+# interface Ethernet1/2
+# switchport trunk native vlan 20
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+- name: Merge provided configuration with device configuration.
+ cisco.nxos.nxos_l2_interfaces:
+ config:
+ - name: Ethernet1/1
+ trunk:
+ native_vlan: 10
+ allowed_vlans: 2,4,15
+ - name: Ethernet1/2
+ access:
+ vlan: 30
+ state: merged
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# switchport trunk native vlan 10
+# switchport trunk allowed vlans 2,4,15
+# interface Ethernet1/2
+# switchport access vlan 30
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+
+# Using replaced
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# switchport access vlan 20
+# interface Ethernet1/2
+# switchport trunk native vlan 20
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+- name: Replace device configuration of specified L2 interfaces with provided configuration.
+ cisco.nxos.nxos_l2_interfaces:
+ config:
+ - name: Ethernet1/1
+ trunk:
+ native_vlan: 20
+ allowed_vlans: 5-10, 15
+ state: replaced
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# switchport trunk native vlan 20
+# switchport trunk allowed vlan 5-10,15
+# interface Ethernet1/2
+# switchport trunk native vlan 20
+# switchport mode trunk
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+
+# Using overridden
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# switchport access vlan 20
+# interface Ethernet1/2
+# switchport trunk native vlan 20
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+- name: Override device configuration of all L2 interfaces on device with provided
+ configuration.
+ cisco.nxos.nxos_l2_interfaces:
+ config:
+ - name: Ethernet1/2
+ access:
+ vlan: 30
+ state: overridden
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# interface Ethernet1/2
+# switchport access vlan 30
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/1
+# switchport access vlan 20
+# interface Ethernet1/2
+# switchport trunk native vlan 20
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+- name: Delete L2 attributes of given interfaces (Note This won't delete the interface
+ itself).
+ cisco.nxos.nxos_l2_interfaces:
+ config:
+ - name: Ethernet1/1
+ - name: Ethernet1/2
+ state: deleted
+
+# After state:
+# ------------
+#
+# interface Ethernet1/1
+# interface Ethernet1/2
+# interface mgmt0
+# ip address dhcp
+# ipv6 address auto-config
+
+# Using rendered
+
+- name: Render platform specific configuration lines (without connecting to the device)
+ cisco.nxos.nxos_l2_interfaces:
+ config:
+ - name: Ethernet1/1
+ trunk:
+ native_vlan: 10
+ allowed_vlans: 2,4,15
+ - name: Ethernet1/2
+ access:
+ vlan: 30
+ - name: Ethernet1/3
+ trunk:
+ native_vlan: 20
+ allowed_vlans: 5-10, 15
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/1"
+# - "switchport trunk allowed vlan 2,4,15"
+# - "switchport trunk native vlan 10"
+# - "interface Ethernet1/2"
+# - "switchport access vlan 30"
+# - "interface Ethernet1/3"
+# - "switchport trunk allowed vlan 5,6,7,8,9,10,15"
+# - "switchport trunk native vlan 20"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# interface Ethernet1/800
+# switchport access vlan 18
+# switchport trunk allowed vlan 210
+# interface Ethernet1/801
+# switchport trunk allowed vlan 2,4,15
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_l2_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# parsed:
+# - name: Ethernet1/800
+# access:
+# vlan: 18
+# trunk:
+# allowed_vlans: "210"
+# - name: Ethernet1/801
+# trunk:
+# allowed_vlans: "2,4,15"
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# Nexus9kvI5# sh running-config | section ^interface
+# interface Ethernet1/1
+# switchport access vlan 6
+# switchport trunk allowed vlan 200
+# interface Ethernet1/2
+# switchport trunk native vlan 10
+
+- name: Gather l2_interfaces facts from the device using nxos_l2_interfaces
+ cisco.nxos.nxos_l2_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# - name: "Ethernet1/1"
+# access:
+# vlan: 6
+# trunk:
+# allowed_vlans: "200"
+#
+# - name: "Ethernet1/2"
+# trunk:
+# native_vlan: 10
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - "interface Ethernet1/1"
+ - "switchport trunk allowed vlan 2,4,15"
+ - "switchport trunk native vlan 10"
+ - "interface Ethernet1/2"
+ - "switchport access vlan 30"
+ - "interface Ethernet1/3"
+ - "switchport trunk allowed vlan 5,6,7,8,9,10,15"
+ - "switchport trunk native vlan 20"
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.l2_interfaces.l2_interfaces import (
+ L2_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.l2_interfaces.l2_interfaces import (
+ L2_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=L2_interfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = L2_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interface.py
new file mode 100644
index 00000000..4d2635b9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interface.py
@@ -0,0 +1,290 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2017, Ansible by Red Hat, inc
+# 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 = """
+module: nxos_l3_interface
+author: Trishna Guha (@trishnaguha)
+short_description: (deprecated, removed after 2022-06-01) Manage L3 interfaces
+ on Cisco NXOS network devices
+description:
+- This module provides declarative management of L3 interfaces on Cisco NXOS network
+ devices.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_l3_interfaces
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+notes:
+- Tested against NXOSv 7.0(3)I5(1).
+options:
+ name:
+ description:
+ - Name of the L3 interface.
+ type: str
+ ipv4:
+ description:
+ - IPv4 of the L3 interface.
+ type: str
+ ipv6:
+ description:
+ - IPv6 of the L3 interface.
+ type: str
+ aggregate:
+ description: List of L3 interfaces definitions.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the L3 interface.
+ type: str
+ ipv4:
+ description:
+ - IPv4 of the L3 interface.
+ type: str
+ ipv6:
+ description:
+ - IPv6 of the L3 interface.
+ type: str
+ state:
+ description:
+ - State of the L3 interface configuration.
+ choices:
+ - present
+ - absent
+ type: str
+ state:
+ description:
+ - State of the L3 interface configuration.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+extends_documentation_fragment:
+- cisco.nxos.nxos
+"""
+
+EXAMPLES = """
+- name: Set interface IPv4 address
+ cisco.nxos.nxos_l3_interface:
+ name: Ethernet2/3
+ ipv4: 192.168.0.1/24
+
+- name: Remove interface IPv4 address
+ cisco.nxos.nxos_l3_interface:
+ name: Ethernet2/3
+ state: absent
+
+- name: Set IP addresses on aggregate
+ cisco.nxos.nxos_l3_interface:
+ aggregate:
+ - {name: Ethernet2/1, ipv4: 192.168.2.10/24}
+ - {name: Ethernet2/5, ipv4: 192.168.3.10/24, ipv6: fd5d:12c9:2201:1::1/64}
+
+- name: Remove IP addresses on aggregate
+ cisco.nxos.nxos_l3_interface:
+ aggregate:
+ - {name: Ethernet2/1, ipv4: 192.168.2.10/24}
+ - {name: Ethernet2/5, ipv4: 192.168.3.10/24, ipv6: fd5d:12c9:2201:1::1/64}
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always, except for the platforms that use Netconf transport to manage the device.
+ type: list
+ sample:
+ - interface ethernet2/3
+ - no switchport
+ - ip address 192.168.22.1/24
+ - ipv6 address "fd5d:12c9:2201:1::1/64"
+ - no ip address 192.168.22.1/24
+"""
+
+import re
+
+from copy import deepcopy
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ normalize_interface,
+)
+
+
+def search_obj_in_list(name, lst):
+ for o in lst:
+ if o["name"] == name:
+ return o
+
+
+def map_obj_to_commands(updates, module, warnings):
+ commands = list()
+ want, have = updates
+
+ for w in want:
+ name = w["name"]
+ ipv4 = w["ipv4"]
+ ipv6 = w["ipv6"]
+ state = w["state"]
+ del w["state"]
+
+ obj_in_have = search_obj_in_list(name, have)
+
+ if not obj_in_have:
+ warnings.append("Unknown interface {0}".format(name))
+ elif state == "absent":
+ command = []
+ if obj_in_have["name"] == name:
+ if ipv4 and ipv4 == obj_in_have["ipv4"]:
+ command.append("no ip address {0}".format(ipv4))
+ if ipv6 and ipv6 in obj_in_have["ipv6"]:
+ command.append("no ipv6 address {0}".format(ipv6))
+ if command:
+ command.append("exit")
+ command.insert(0, "interface {0}".format(name))
+ commands.extend(command)
+
+ elif state == "present":
+ command = []
+ if obj_in_have["name"] == name:
+ if ipv4 and ipv4 != obj_in_have["ipv4"]:
+ command.append("ip address {0}".format(ipv4))
+ if ipv6 and ipv6 not in obj_in_have["ipv6"]:
+ command.append("ipv6 address {0}".format(ipv6))
+ if command:
+ command.append("exit")
+ command.insert(0, "interface {0}".format(name))
+ elif not ipv4 and not ipv6:
+ command.append("interface {0}".format(name))
+ commands.extend(command)
+
+ return commands
+
+
+def map_params_to_obj(module):
+ obj = []
+
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ name = d["name"]
+ d["name"] = normalize_interface(name)
+ obj.append(d)
+
+ else:
+ obj.append(
+ {
+ "name": normalize_interface(module.params["name"]),
+ "ipv4": module.params["ipv4"],
+ "ipv6": module.params["ipv6"],
+ "state": module.params["state"],
+ }
+ )
+
+ return obj
+
+
+def map_config_to_obj(want, module):
+ objs = list()
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+
+ for w in want:
+ parents = ["interface {0}".format(w["name"])]
+ config = netcfg.get_section(parents)
+ obj = dict(name=None, ipv4=None, ipv6=[])
+
+ if config:
+ match_name = re.findall(r"interface (\S+)", config, re.M)
+ if match_name:
+ obj["name"] = normalize_interface(match_name[0])
+
+ match_ipv4 = re.findall(r"ip address (\S+)", config, re.M)
+ if match_ipv4:
+ obj["ipv4"] = match_ipv4[0]
+
+ match_ipv6 = re.findall(r"ipv6 address (\S+)", config, re.M)
+ if match_ipv6:
+ obj["ipv6"] = match_ipv6
+
+ objs.append(obj)
+ return objs
+
+
+def main():
+ """ main entry point for module execution
+ """
+ element_spec = dict(
+ name=dict(),
+ ipv4=dict(),
+ ipv6=dict(),
+ state=dict(default="present", choices=["present", "absent"]),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec)
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ required_one_of = [["name", "aggregate"]]
+ mutually_exclusive = [["name", "aggregate"]]
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=required_one_of,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False}
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(want, module)
+
+ commands = map_obj_to_commands((want, have), module, warnings)
+ result["commands"] = commands
+
+ if warnings:
+ result["warnings"] = warnings
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interfaces.py
new file mode 100644
index 00000000..3c623776
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_l3_interfaces.py
@@ -0,0 +1,404 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_l3_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_l3_interfaces
+short_description: L3 interfaces resource module
+description: This module manages Layer-3 interfaces attributes of NX-OS Interfaces.
+version_added: 1.0.0
+author: Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section '^interface').
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A dictionary of Layer-3 interface options
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Full name of L3 interface, i.e. Ethernet1/1.
+ type: str
+ required: true
+ dot1q:
+ description:
+ - Configures IEEE 802.1Q VLAN encapsulation on a subinterface.
+ type: int
+ ipv4:
+ description:
+ - IPv4 address and attributes of the L3 interface.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description:
+ - IPV4 address of the L3 interface.
+ type: str
+ tag:
+ description:
+ - URIB route tag value for local/direct routes.
+ type: int
+ secondary:
+ description:
+ - A boolean attribute to manage addition of secondary IP address.
+ type: bool
+ ipv6:
+ description:
+ - IPv6 address and attributes of the L3 interface.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description:
+ - IPV6 address of the L3 interface.
+ type: str
+ tag:
+ description:
+ - URIB route tag value for local/direct routes.
+ type: int
+ redirects:
+ description:
+ - Enables/disables ip redirects
+ type: bool
+ unreachables:
+ description:
+ - Enables/disables ip redirects
+ type: bool
+ evpn_multisite_tracking:
+ description:
+ - VxLAN evpn multisite Interface tracking. Supported only on selected model.
+ type: str
+ version_added: 1.1.0
+ choices:
+ - fabric-tracking
+ - dci-tracking
+ state:
+ description:
+ - The state of the configuration after module completion.
+ - The state I(overridden) would override the IP address configuration
+ of all interfaces on the device with the provided configuration in
+ the task. Use caution with this state as you may loose access to the
+ device.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/6
+
+- name: Merge provided configuration with device configuration.
+ cisco.nxos.nxos_l3_interfaces:
+ config:
+ - name: Ethernet1/6
+ ipv4:
+ - address: 192.168.1.1/24
+ tag: 5
+ - address: 10.1.1.1/24
+ secondary: true
+ tag: 10
+ ipv6:
+ - address: fd5d:12c9:2201:2::1/64
+ tag: 6
+ - name: Ethernet1/7.42
+ dot1q: 42
+ redirects: false
+ unreachables: false
+ state: merged
+
+# After state:
+# ------------
+#
+# interface Ethernet1/6
+# ip address 192.168.22.1/24 tag 5
+# ip address 10.1.1.1/24 secondary tag 10
+# interface Ethernet1/6
+# ipv6 address fd5d:12c9:2201:2::1/64 tag 6
+# interface Ethernet1/7.42
+# encapsulation dot1q 42
+# no ip redirects
+# no ip unreachables
+
+
+# Using replaced
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/6
+# ip address 192.168.22.1/24
+# ipv6 address "fd5d:12c9:2201:1::1/64"
+
+- name: Replace device configuration of specified L3 interfaces with provided configuration.
+ cisco.nxos.nxos_l3_interfaces:
+ config:
+ - name: Ethernet1/6
+ ipv4:
+ - address: 192.168.22.3/24
+ state: replaced
+
+# After state:
+# ------------
+#
+# interface Ethernet1/6
+# ip address 192.168.22.3/24
+
+
+# Using overridden
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/2
+# ip address 192.168.22.1/24
+# interface Ethernet1/6
+# ipv6 address "fd5d:12c9:2201:1::1/64"
+
+- name: Override device configuration of all L3 interfaces on device with provided
+ configuration.
+ cisco.nxos.nxos_l3_interfaces:
+ config:
+ - name: Ethernet1/2
+ ipv4: 192.168.22.3/4
+ state: overridden
+
+# After state:
+# ------------
+#
+# interface Ethernet1/2
+# ipv4 address 192.168.22.3/24
+# interface Ethernet1/6
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/6
+# ip address 192.168.22.1/24
+# interface Ethernet1/2
+# ipv6 address "fd5d:12c9:2201:1::1/64"
+
+- name: Delete L3 attributes of given interfaces (This won't delete the interface
+ itself).
+ cisco.nxos.nxos_l3_interfaces:
+ config:
+ - name: Ethernet1/6
+ - name: Ethernet1/2
+ state: deleted
+
+# After state:
+# ------------
+#
+# interface Ethernet1/6
+# interface Ethernet1/2
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_l3_interfaces:
+ config:
+ - name: Ethernet1/800
+ ipv4:
+ - address: 192.168.1.100/24
+ tag: 5
+ - address: 10.1.1.1/24
+ secondary: true
+ tag: 10
+ - name: Ethernet1/800
+ ipv6:
+ - address: fd5d:12c9:2201:2::1/64
+ tag: 6
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/800"
+# - "ip address 192.168.1.100/24 tag 5"
+# - "ip address 10.1.1.1/24 secondary tag 10"
+# - "interface Ethernet1/800"
+# - "ipv6 address fd5d:12c9:2201:2::1/64 tag 6"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# interface Ethernet1/800
+# ip address 192.168.1.100/24 tag 5
+# ip address 10.1.1.1/24 secondary tag 10
+# no ip redirects
+# interface Ethernet1/801
+# ipv6 address fd5d:12c9:2201:2::1/64 tag 6
+# ip unreachables
+# interface mgmt0
+# ip address dhcp
+# vrf member management
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_l3_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# - name: Ethernet1/800
+# ipv4:
+# - address: 192.168.1.100/24
+# tag: 5
+# - address: 10.1.1.1/24
+# secondary: True
+# tag: 10
+# redirects: False
+# - name: Ethernet1/801
+# ipv6:
+# - address: fd5d:12c9:2201:2::1/64
+# tag: 6
+# unreachables: True
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# interface Ethernet1/1
+# ip address 192.0.2.100/24
+# interface Ethernet1/2
+# no ip redirects
+# ip address 203.0.113.10/24
+# ip unreachables
+# ipv6 address 2001:db8::1/32
+
+- name: Gather l3_interfaces facts from the device using nxos_l3_interfaces
+ cisco.nxos.nxos_l3_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+
+# gathered:
+# - name: Ethernet1/1
+# ipv4:
+# - address: 192.0.2.100/24
+# - name: Ethernet1/2
+# ipv4:
+# - address: 203.0.113.10/24
+# ipv6:
+# - address: 2001:db8::1/32
+# redirects: False
+# unreachables: True
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface Ethernet1/2', 'ip address 192.168.0.1/2']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.l3_interfaces.l3_interfaces import (
+ L3_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.l3_interfaces.l3_interfaces import (
+ L3_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=L3_interfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = L3_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp.py
new file mode 100644
index 00000000..072499a2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp.py
@@ -0,0 +1,275 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_lacp
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_lacp
+short_description: LACP resource module
+description: This module manages Global Link Aggregation Control Protocol (LACP) on
+ NX-OS devices.
+version_added: 1.0.0
+author: Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL.
+- Feature lacp should be enabled for this module.
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | include lacp).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: LACP global options.
+ type: dict
+ suboptions:
+ system:
+ description:
+ - LACP system options
+ type: dict
+ suboptions:
+ priority:
+ description:
+ - The system priority to use in LACP negotiations.
+ type: int
+ mac:
+ description:
+ - MAC address to be used for the LACP Protocol exchanges
+ type: dict
+ suboptions:
+ address:
+ description:
+ - MAC-address (FORMAT :xxxx.xxxx.xxxx).
+ type: str
+ role:
+ description:
+ - The role for the Switch.
+ type: str
+ choices:
+ - primary
+ - secondary
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+
+- name: Merge provided configuration with device configuration.
+ cisco.nxos.nxos_lacp:
+ config:
+ system:
+ priority: 10
+ mac:
+ address: 00c1.4c00.bd15
+ state: merged
+
+# After state:
+# ------------
+#
+# lacp system-priority 10
+# lacp system-mac 00c1.4c00.bd15
+
+
+# Using replaced
+
+# Before state:
+# -------------
+#
+# lacp system-priority 10
+
+- name: Replace device global lacp configuration with the given configuration.
+ cisco.nxos.nxos_lacp:
+ config:
+ system:
+ mac:
+ address: 00c1.4c00.bd15
+ state: replaced
+
+# After state:
+# ------------
+#
+# lacp system-mac 00c1.4c00.bd15
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# lacp system-priority 10
+
+- name: Delete global LACP configurations.
+ cisco.nxos.nxos_lacp:
+ state: deleted
+
+# After state:
+# ------------
+#
+
+# Using rendered
+
+- name: Render platform specific configuration lines (without connecting to the device)
+ cisco.nxos.nxos_lacp:
+ config:
+ system:
+ priority: 10
+ mac:
+ address: 00c1.4c00.bd15
+ role: secondary
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "lacp system-priority 10"
+# - "lacp system-mac 00c1.4c00.bd15 role secondary"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# lacp system-priority 10
+# lacp system-mac 00c1.4c00.bd15 role secondary
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_lacp:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# parsed:
+# system:
+# priority: 10
+# mac:
+# address: 00c1.4c00.bd15
+# role: secondary
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# Nexus9000v# show running-config | include lacp
+# lacp system-priority 11
+# lacp system-mac 00c1.4c00.bd15 role primary
+
+- name: Gather lacp facts from the device using nxos_lacp
+ cisco.nxos.nxos_lacp:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# system:
+# priority: 11
+# mac:
+# address: 00c1.4c00.bd15
+# role: primary
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['lacp system-priority 15', 'lacp system-mac 00c1.4c00.bd15 role primary']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lacp.lacp import (
+ LacpArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.lacp.lacp import (
+ Lacp,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=LacpArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Lacp(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp_interfaces.py
new file mode 100644
index 00000000..e208fad1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lacp_interfaces.py
@@ -0,0 +1,378 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_lacp_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_lacp_interfaces
+short_description: LACP interfaces resource module
+description: This module manages Link Aggregation Control Protocol (LACP) attributes
+ of NX-OS Interfaces.
+version_added: 1.0.0
+author: Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section ^interface).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A dictionary of LACP interfaces options.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the interface.
+ required: true
+ type: str
+ port_priority:
+ description:
+ - LACP port priority for the interface. Range 1-65535. Applicable only for
+ Ethernet.
+ type: int
+ rate:
+ description:
+ - Rate at which PDUs are sent by LACP. Applicable only for Ethernet. At fast
+ rate LACP is transmitted once every 1 second. At normal rate LACP is transmitted
+ every 30 seconds after the link is bundled.
+ type: str
+ choices:
+ - fast
+ - normal
+ links:
+ description:
+ - This dict contains configurable options related to max and min port-channel
+ links. Applicable only for Port-channel.
+ type: dict
+ suboptions:
+ max:
+ description:
+ - Port-channel max bundle.
+ type: int
+ min:
+ description:
+ - Port-channel min links.
+ type: int
+ mode:
+ description:
+ - LACP mode. Applicable only for Port-channel.
+ type: str
+ choices:
+ - delay
+ suspend_individual:
+ description:
+ - port-channel lacp state. Disabling this will cause lacp to put the port
+ to individual state and not suspend the port in case it does not get LACP
+ BPDU from the peer ports in the port-channel.
+ type: bool
+ convergence:
+ description:
+ - This dict contains configurable options related to convergence. Applicable
+ only for Port-channel.
+ type: dict
+ suboptions:
+ graceful:
+ description:
+ - port-channel lacp graceful convergence. Disable this only with lacp
+ ports connected to Non-Nexus peer. Disabling this with Nexus peer can
+ lead to port suspension.
+ type: bool
+ vpc:
+ description:
+ - Enable lacp convergence for vPC port channels.
+ type: bool
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+
+- name: Merge provided configuration with device configuration.
+ cisco.nxos.nxos_lacp_interfaces:
+ config:
+ - name: Ethernet1/3
+ port_priority: 5
+ rate: fast
+ state: merged
+
+# After state:
+# ------------
+#
+# interface Ethernet1/3
+# lacp port-priority 5
+# lacp rate fast
+
+
+# Using replaced
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/3
+# lacp port-priority 5
+# interface port-channel11
+# lacp mode delay
+
+- name: Replace device lacp interfaces configuration with the given configuration.
+ cisco.nxos.nxos_lacp_interfaces:
+ config:
+ - name: port-channel11
+ links:
+ min: 4
+ state: replaced
+
+# After state:
+# ------------
+#
+# interface Ethernet1/3
+# lacp port-priority 5
+# interface port-channel11
+# lacp min-links 4
+
+
+# Using overridden
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/3
+# lacp port-priority 5
+# interface port-channel11
+# lacp mode delay
+
+- name: Override device configuration of all LACP interfaces attributes of given interfaces
+ on device with provided configuration.
+ cisco.nxos.nxos_lacp_interfaces:
+ config:
+ - name: port-channel11
+ links:
+ min: 4
+ state: overridden
+
+# After state:
+# ------------
+#
+# interface port-channel11
+# lacp min-links 4
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/3
+# lacp port-priority 5
+# interface port-channel11
+# lacp mode delay
+
+- name: Delete LACP interfaces configurations.
+ cisco.nxos.nxos_lacp_interfaces:
+ state: deleted
+
+# After state:
+# ------------
+#
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_lacp_interfaces:
+ config:
+ - name: Ethernet1/800
+ rate: fast
+ - name: Ethernet1/801
+ rate: fast
+ port_priority: 32
+ - name: port-channel10
+ links:
+ max: 15
+ min: 2
+ convergence:
+ graceful: true
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/800"
+# - "lacp rate fast"
+# - "interface Ethernet1/801"
+# - "lacp port-priority 32"
+# - "lacp rate fast"
+# - "interface port-channel10"
+# - "lacp min-links 2"
+# - "lacp max-bundle 15"
+# - "lacp graceful-convergence"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+
+# interface port-channel10
+# lacp min-links 10
+# lacp max-bundle 15
+# interface Ethernet1/800
+# lacp port-priority 100
+# lacp rate fast
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_lacp_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# - name: port-channel10
+# links:
+# max: 15
+# min: 10
+# - name: Ethernet1/800
+# port_priority: 100
+# rate: fast
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# interface Ethernet1/1
+# lacp port-priority 5
+# lacp rate fast
+# interface port-channel10
+# lacp mode delay
+# interface port-channel11
+# lacp max-bundle 10
+# lacp min-links 5
+
+- name: Gather lacp_interfaces facts from the device using nxos_lacp_interfaces
+ cisco.nxos.nxos_lacp_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# - name: Ethernet1/1
+# port_priority: 5
+# rate: fast
+# - name: port-channel10
+# mode: delay
+# - name: port-channel11
+# links:
+# max: 10
+# min: 5
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface port-channel10', 'lacp min-links 5', 'lacp mode delay']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lacp_interfaces.lacp_interfaces import (
+ Lacp_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.lacp_interfaces.lacp_interfaces import (
+ Lacp_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=Lacp_interfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Lacp_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lag_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lag_interfaces.py
new file mode 100644
index 00000000..cb9c6682
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lag_interfaces.py
@@ -0,0 +1,365 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_lag_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_lag_interfaces
+short_description: LAG interfaces resource module
+description: This module manages attributes of link aggregation groups of NX-OS Interfaces.
+version_added: 1.0.0
+author:
+- Trishna Guha (@trishnaguha)
+- Nilashish Chakraborty (@NilashishC)
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section ^interface).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A list of link aggregation group configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the link aggregation group (LAG).
+ type: str
+ required: true
+ members:
+ description:
+ - The list of interfaces that are part of the group.
+ type: list
+ elements: dict
+ suboptions:
+ member:
+ description:
+ - The interface name.
+ type: str
+ mode:
+ description:
+ - Link aggregation group (LAG).
+ type: str
+ choices:
+ - 'active'
+ - 'on'
+ - 'passive'
+ force:
+ description:
+ - When true it forces link aggregation group members to match what is
+ declared in the members param. This can be used to remove members.
+ type: bool
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL.
+- This module works with connection C(network_cli).
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/4
+
+- name: Merge provided configuration with device configuration.
+ cisco.nxos.nxos_lag_interfaces:
+ config:
+ - name: port-channel99
+ members:
+ - member: Ethernet1/4
+ state: merged
+
+# After state:
+# ------------
+#
+# interface Ethernet1/4
+# channel-group 99
+
+
+# Using replaced
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/4
+# channel-group 99 mode active
+
+- name: Replace device configuration of specified LAG attributes of given interfaces
+ with provided configuration.
+ cisco.nxos.nxos_lag_interfaces:
+ config:
+ - name: port-channel10
+ members:
+ - member: Ethernet1/4
+ state: replaced
+
+# After state:
+# ------------
+#
+# interface Ethernet1/4
+# channel-group 10
+
+
+# Using overridden
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/4
+# channel-group 10
+# interface Ethernet1/2
+# channel-group 99 mode passive
+
+- name: Override device configuration of all LAG attributes of given interfaces on
+ device with provided configuration.
+ cisco.nxos.nxos_lag_interfaces:
+ config:
+ - name: port-channel20
+ members:
+ - member: Ethernet1/6
+ force: true
+ state: overridden
+
+# After state:
+# ------------
+# interface Ethernet1/2
+# interface Ethernet1/4
+# interface Ethernet1/6
+# channel-group 20 force
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# interface Ethernet1/4
+# channel-group 99 mode active
+
+- name: Delete LAG attributes of given interface (This won't delete the port-channel
+ itself).
+ cisco.nxos.nxos_lag_interfaces:
+ config:
+ - port-channel: port-channel99
+ state: deleted
+
+- name: Delete LAG attributes of all the interfaces
+ cisco.nxos.nxos_lag_interfaces:
+ state: deleted
+
+# After state:
+# ------------
+#
+# interface Ethernet1/4
+# no channel-group 99
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_lag_interfaces:
+ config:
+ - name: port-channel10
+ members:
+ - member: Ethernet1/800
+ mode: active
+ - member: Ethernet1/801
+ - name: port-channel11
+ members:
+ - member: Ethernet1/802
+ mode: passive
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "interface Ethernet1/800"
+# - "channel-group 10 mode active"
+# - "interface Ethernet1/801"
+# - "channel-group 10"
+# - "interface Ethernet1/802"
+# - "channel-group 11 mode passive"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+
+# interface port-channel10
+# interface port-channel11
+# interface port-channel12
+# interface Ethernet1/800
+# channel-group 10 mode active
+# interface Ethernet1/801
+# channel-group 10 mode active
+# interface Ethernet1/802
+# channel-group 11 mode passive
+# interface Ethernet1/803
+# channel-group 11 mode passive
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_lag_interfaces:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# - members:
+# - member: Ethernet1/800
+# mode: active
+# - member: Ethernet1/801
+# mode: active
+# name: port-channel10
+#
+# - members:
+# - member: Ethernet1/802
+# mode: passive
+# - member: Ethernet1/803
+# mode: passive
+# name: port-channel11
+#
+# - name: port-channel12
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# interface port-channel10
+# interface port-channel11
+# interface Ethernet1/1
+# channel-group 10 mode active
+# interface Ethernet1/2
+# channel-group 11 mode passive
+#
+
+- name: Gather lag_interfaces facts from the device using nxos_lag_interfaces
+ cisco.nxos.nxos_lag_interfaces:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# - name: port-channel10
+# members:
+# - member: Ethernet1/1
+# mode: active
+# - name: port-channel11
+# members:
+# - member: Ethernet1/2
+# mode: passive
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - "interface Ethernet1/800"
+ - "channel-group 10 mode active"
+ - "interface Ethernet1/801"
+ - "channel-group 10"
+ - "interface Ethernet1/802"
+ - "channel-group 11 mode passive"
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lag_interfaces.lag_interfaces import (
+ Lag_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.lag_interfaces.lag_interfaces import (
+ Lag_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=Lag_interfacesArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Lag_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_linkagg.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_linkagg.py
new file mode 100644
index 00000000..2ff69d99
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_linkagg.py
@@ -0,0 +1,559 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2017, Ansible by Red Hat, inc
+# 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 = """
+module: nxos_linkagg
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manage link
+ aggregation groups on Cisco NXOS devices.
+description:
+- This module provides declarative management of link aggregation groups on Cisco
+ NXOS devices.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_lag_interfaces
+ why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
+author:
+- Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOSv 7.0(3)I5(1).
+- C(state=absent) removes the portchannel config and interface if it already exists.
+ If members to be removed are not explicitly passed, all existing members (if any),
+ are removed.
+- Members must be a list.
+- LACP needs to be enabled first if active/passive modes are used.
+options:
+ group:
+ description:
+ - Channel-group number for the port-channel Link aggregation group.
+ type: str
+ mode:
+ description:
+ - Mode for the link aggregation group.
+ choices:
+ - 'active'
+ - 'on'
+ - 'passive'
+ default: 'on'
+ type: str
+ min_links:
+ description:
+ - Minimum number of ports required up before bringing up the link aggregation
+ group.
+ type: int
+ members:
+ description:
+ - List of interfaces that will be managed in the link aggregation group.
+ type: list
+ elements: str
+ force:
+ description:
+ - When true it forces link aggregation group members to match what is declared
+ in the members param. This can be used to remove members.
+ type: bool
+ default: no
+ aggregate:
+ description: List of link aggregation definitions.
+ type: list
+ elements: dict
+ suboptions:
+ group:
+ description:
+ - Channel-group number for the port-channel Link aggregation group.
+ type: str
+ required: True
+ mode:
+ description:
+ - Mode for the link aggregation group.
+ choices:
+ - 'active'
+ - 'on'
+ - 'passive'
+ type: str
+ min_links:
+ description:
+ - Minimum number of ports required up before bringing up the link aggregation
+ group.
+ type: int
+ members:
+ description:
+ - List of interfaces that will be managed in the link aggregation group.
+ type: list
+ elements: str
+ force:
+ description:
+ - When true it forces link aggregation group members to match what is declared
+ in the members param. This can be used to remove members.
+ type: bool
+ state:
+ description:
+ - State of the link aggregation group.
+ choices:
+ - present
+ - absent
+ type: str
+ state:
+ description:
+ - State of the link aggregation group.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ purge:
+ description:
+ - Purge links not defined in the I(aggregate) parameter.
+ type: bool
+ default: no
+"""
+
+EXAMPLES = """
+- name: create link aggregation group
+ cisco.nxos.nxos_linkagg:
+ group: 99
+ state: present
+
+- name: delete link aggregation group
+ cisco.nxos.nxos_linkagg:
+ group: 99
+ state: absent
+
+- name: set link aggregation group to members
+ cisco.nxos.nxos_linkagg:
+ group: 10
+ min_links: 3
+ mode: active
+ members:
+ - Ethernet1/2
+ - Ethernet1/4
+
+- name: remove link aggregation group from Ethernet1/2
+ cisco.nxos.nxos_linkagg:
+ group: 10
+ min_links: 3
+ mode: active
+ members:
+ - Ethernet1/4
+
+- name: Create aggregate of linkagg definitions
+ cisco.nxos.nxos_linkagg:
+ aggregate:
+ - {group: 3}
+ - {group: 100, min_links: 3}
+
+- name: Remove aggregate of linkagg definitions
+ cisco.nxos.nxos_linkagg:
+ aggregate:
+ - {group: 3}
+ - {group: 100, min_links: 3}
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always, except for the platforms that use Netconf transport to manage the device.
+ type: list
+ sample:
+ - interface port-channel 30
+ - lacp min-links 5
+ - interface Ethernet2/1
+ - channel-group 30 mode active
+ - no interface port-channel 30
+"""
+
+import re
+from copy import deepcopy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ normalize_interface,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+
+
+def search_obj_in_list(group, lst):
+ for o in lst:
+ if o["group"] == group:
+ return o
+
+
+def get_diff(w, obj):
+ c = deepcopy(w)
+ o = deepcopy(obj)
+
+ if o["group"] == c["group"] and o.get("members") == c.get("members"):
+ if "members" in o:
+ del o["members"]
+ if "members" in c:
+ del c["members"]
+ diff_dict = dict(set(c.items()) - set(o.items()))
+ return diff_dict
+
+
+def map_obj_to_commands(updates, module):
+ commands = list()
+ want, have = updates
+ purge = module.params["purge"]
+ force = module.params["force"]
+
+ for w in want:
+ group = w["group"]
+ mode = w["mode"]
+ min_links = w["min_links"]
+ members = w.get("members") or []
+ state = w["state"]
+ del w["state"]
+
+ obj_in_have = search_obj_in_list(group, have)
+
+ if state == "absent":
+ if obj_in_have:
+ members_to_remove = list(
+ set(obj_in_have["members"]) - set(members)
+ )
+ if members_to_remove:
+ for m in members_to_remove:
+ commands.append("interface {0}".format(m))
+ commands.append(
+ "no channel-group {0}".format(obj_in_have["group"])
+ )
+ commands.append("exit")
+ commands.append("no interface port-channel {0}".format(group))
+
+ elif state == "present":
+ if not obj_in_have:
+ commands.append("interface port-channel {0}".format(group))
+ if min_links != "None":
+ commands.append("lacp min-links {0}".format(min_links))
+ commands.append("exit")
+ if members:
+ for m in members:
+ commands.append("interface {0}".format(m))
+ if force:
+ commands.append(
+ "channel-group {0} force mode {1}".format(
+ group, mode
+ )
+ )
+ else:
+ commands.append(
+ "channel-group {0} mode {1}".format(
+ group, mode
+ )
+ )
+
+ else:
+ if members:
+ if not obj_in_have["members"]:
+ for m in members:
+ commands.append(
+ "interface port-channel {0}".format(group)
+ )
+ commands.append("exit")
+ commands.append("interface {0}".format(m))
+ if force:
+ commands.append(
+ "channel-group {0} force mode {1}".format(
+ group, mode
+ )
+ )
+ else:
+ commands.append(
+ "channel-group {0} mode {1}".format(
+ group, mode
+ )
+ )
+
+ elif set(members) != set(obj_in_have["members"]):
+ missing_members = list(
+ set(members) - set(obj_in_have["members"])
+ )
+ for m in missing_members:
+ commands.append(
+ "interface port-channel {0}".format(group)
+ )
+ commands.append("exit")
+ commands.append("interface {0}".format(m))
+ if force:
+ commands.append(
+ "channel-group {0} force mode {1}".format(
+ group, mode
+ )
+ )
+ else:
+ commands.append(
+ "channel-group {0} mode {1}".format(
+ group, mode
+ )
+ )
+
+ superfluous_members = list(
+ set(obj_in_have["members"]) - set(members)
+ )
+ for m in superfluous_members:
+ commands.append(
+ "interface port-channel {0}".format(group)
+ )
+ commands.append("exit")
+ commands.append("interface {0}".format(m))
+ commands.append(
+ "no channel-group {0}".format(group)
+ )
+
+ else:
+ diff = get_diff(w, obj_in_have)
+ if diff and "mode" in diff:
+ mode = diff["mode"]
+ for i in members:
+ commands.append("interface {0}".format(i))
+ if force:
+ commands.append(
+ "channel-group {0} force mode {1}".format(
+ group, mode
+ )
+ )
+ else:
+ commands.append(
+ "channel-group {0} mode {1}".format(
+ group, mode
+ )
+ )
+
+ if purge:
+ for h in have:
+ obj_in_want = search_obj_in_list(h["group"], want)
+ if not obj_in_want:
+ commands.append(
+ "no interface port-channel {0}".format(h["group"])
+ )
+
+ return commands
+
+
+def map_params_to_obj(module):
+ obj = []
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ d["group"] = str(d["group"])
+ d["min_links"] = str(d["min_links"])
+ if d["members"]:
+ d["members"] = [normalize_interface(i) for i in d["members"]]
+
+ obj.append(d)
+ else:
+ members = None
+ if module.params["members"]:
+ members = [
+ normalize_interface(i) for i in module.params["members"]
+ ]
+
+ obj.append(
+ {
+ "group": str(module.params["group"]),
+ "mode": module.params["mode"],
+ "min_links": str(module.params["min_links"]),
+ "members": members,
+ "state": module.params["state"],
+ }
+ )
+
+ return obj
+
+
+def parse_min_links(module, group):
+ min_links = None
+
+ flags = ["| section interface.port-channel{0}".format(group)]
+ config = get_config(module, flags=flags)
+ match = re.search(r"lacp min-links (\S+)", config, re.M)
+ if match:
+ min_links = match.group(1)
+
+ return min_links
+
+
+def parse_mode(module, m):
+ mode = None
+
+ flags = ["| section interface.{0}".format(m)]
+ config = get_config(module, flags=flags)
+ match = re.search(
+ r"channel-group [0-9]+ (force )?mode (\S+)", config, re.M
+ )
+ if match:
+ mode = match.group(2)
+
+ return mode
+
+
+def get_members(channel):
+ members = []
+ if "TABLE_member" in channel.keys():
+ interfaces = channel["TABLE_member"]["ROW_member"]
+ else:
+ return list()
+
+ if isinstance(interfaces, dict):
+ members.append(normalize_interface(interfaces.get("port")))
+ elif isinstance(interfaces, list):
+ for i in interfaces:
+ members.append(normalize_interface(i.get("port")))
+
+ return members
+
+
+def parse_members(output, group):
+ channels = output["TABLE_channel"]["ROW_channel"]
+
+ if isinstance(channels, list):
+ for channel in channels:
+ if channel["group"] == group:
+ members = get_members(channel)
+ elif isinstance(channels, dict):
+ if channels["group"] == group:
+ members = get_members(channels)
+ else:
+ return list()
+
+ return members
+
+
+def parse_channel_options(module, output, channel):
+ obj = {}
+
+ group = channel["group"]
+ obj["group"] = str(group)
+ obj["min_links"] = parse_min_links(module, group)
+ members = parse_members(output, group)
+ obj["members"] = members
+ for m in members:
+ obj["mode"] = parse_mode(module, m)
+
+ return obj
+
+
+def map_config_to_obj(module):
+ objs = list()
+ output = run_commands(module, ["show port-channel summary | json"])[0]
+ if not output:
+ return list()
+
+ try:
+ channels = output["TABLE_channel"]["ROW_channel"]
+ except (TypeError, KeyError):
+ return objs
+
+ if channels:
+ if isinstance(channels, list):
+ for channel in channels:
+ obj = parse_channel_options(module, output, channel)
+ objs.append(obj)
+
+ elif isinstance(channels, dict):
+ obj = parse_channel_options(module, output, channels)
+ objs.append(obj)
+
+ return objs
+
+
+def main():
+ """ main entry point for module execution
+ """
+ element_spec = dict(
+ group=dict(type="str"),
+ mode=dict(
+ required=False,
+ choices=["on", "active", "passive"],
+ default="on",
+ type="str",
+ ),
+ min_links=dict(required=False, default=None, type="int"),
+ members=dict(
+ required=False, default=None, type="list", elements="str"
+ ),
+ force=dict(required=False, default=False, type="bool"),
+ state=dict(
+ required=False, choices=["absent", "present"], default="present"
+ ),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+ aggregate_spec["group"] = dict(required=True)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec),
+ purge=dict(default=False, type="bool"),
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ required_one_of = [["group", "aggregate"]]
+ mutually_exclusive = [["group", "aggregate"]]
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=required_one_of,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(module)
+
+ commands = map_obj_to_commands((want, have), module)
+ result["commands"] = commands
+
+ if commands:
+ if not module.check_mode:
+ resp = load_config(module, commands, True)
+ if resp:
+ for item in resp:
+ if item:
+ if isinstance(item, dict):
+ err_str = item["clierror"]
+ else:
+ err_str = item
+ if "cannot add" in err_str.lower():
+ module.fail_json(msg=err_str)
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp.py
new file mode 100644
index 00000000..c9ccfdb8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# (c) 2017, Ansible by Red Hat, inc
+# 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 = """
+module: nxos_lldp
+author: Ganesh Nalawade (@ganeshrn)
+short_description: (deprecated, removed after 2022-06-01) Manage LLDP
+ configuration on Cisco NXOS network devices.
+description:
+- This module provides declarative management of LLDP service on Cisco NXOS network
+ devices.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_lldp_global
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+notes:
+- Tested against NXOSv 7.0(3)I5(1).
+options:
+ state:
+ description:
+ - State of the LLDP configuration. If value is I(present) lldp will be enabled
+ else if it is I(absent) it will be disabled.
+ default: present
+ choices:
+ - present
+ - absent
+ - enabled
+ - disabled
+ type: str
+extends_documentation_fragment:
+- cisco.nxos.nxos
+
+
+"""
+
+EXAMPLES = """
+- name: Enable LLDP service
+ cisco.nxos.nxos_lldp:
+ state: present
+
+- name: Disable LLDP service
+ cisco.nxos.nxos_lldp:
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always, except for the platforms that use Netconf transport to manage the device.
+ type: list
+ sample:
+ - feature lldp
+"""
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+
+
+def has_lldp(module):
+ output = get_config(module, ["| section lldp"])
+ is_lldp_enable = False
+ if output and "feature lldp" in output:
+ is_lldp_enable = True
+
+ return is_lldp_enable
+
+
+def main():
+ """ main entry point for module execution
+ """
+ argument_spec = dict(
+ state=dict(
+ default="present",
+ choices=["present", "absent", "enabled", "disabled"],
+ )
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ result = {"changed": False}
+
+ if warnings:
+ result["warnings"] = warnings
+
+ HAS_LLDP = has_lldp(module)
+
+ commands = []
+
+ if module.params["state"] == "absent" and HAS_LLDP:
+ commands.append("no feature lldp")
+ elif module.params["state"] == "present" and not HAS_LLDP:
+ commands.append("feature lldp")
+
+ result["commands"] = commands
+
+ if commands:
+ # On N35 A8 images, some features return a yes/no prompt
+ # on enablement or disablement. Bypass using terminal dont-ask
+ commands.insert(0, "terminal dont-ask")
+ if not module.check_mode:
+ load_config(module, commands)
+
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_global.py
new file mode 100644
index 00000000..3bf38c19
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_global.py
@@ -0,0 +1,342 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The module file for nxos_lldp_global
+"""
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_lldp_global
+short_description: LLDP resource module
+description: This module configures and manages the Link Layer Discovery Protocol(LLDP)
+ attributes on NX-OS platforms.
+version_added: 1.0.0
+author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
+notes:
+- Tested against NxOS 7.3.(0)D1(1) on VIRL
+- The LLDP feature needs to be enabled before using this module
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | include lldp).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description:
+ - A list of link layer discovery configurations
+ type: dict
+ suboptions:
+ holdtime:
+ description:
+ - Amount of time the receiving device should hold the information (in seconds)
+ type: int
+ port_id:
+ description:
+ - This attribute defines if the interface names should be advertised in the
+ long(0) or short(1) form.
+ type: int
+ choices:
+ - 0
+ - 1
+ reinit:
+ description:
+ - Amount of time to delay the initialization of LLDP on any interface (in
+ seconds)
+ type: int
+ timer:
+ description:
+ - Frequency at which LLDP updates need to be transmitted (in seconds)
+ type: int
+ tlv_select:
+ description:
+ - This attribute can be used to specify the TLVs that need to be sent and
+ received in the LLDP packets. By default, all TLVs are advertised
+ type: dict
+ suboptions:
+ dcbxp:
+ description:
+ - Used to specify the Data Center Bridging Exchange Protocol TLV
+ type: bool
+ management_address:
+ description:
+ - Used to specify the management address in TLV messages
+ type: dict
+ suboptions:
+ v4:
+ description: Management address with TLV v4
+ type: bool
+ v6:
+ description: Management address with TLV v6
+ type: bool
+ port:
+ description:
+ - Used to manage port based attributes in TLV messages
+ type: dict
+ suboptions:
+ description:
+ description:
+ - Used to specify the port description TLV
+ type: bool
+ vlan:
+ description:
+ - Used to specify the port VLAN ID TLV
+ type: bool
+ power_management:
+ description:
+ - Used to specify IEEE 802.3 DTE Power via MDI TLV
+ type: bool
+ system:
+ description:
+ - Used to manage system based attributes in TLV messages
+ type: dict
+ suboptions:
+ capabilities:
+ description:
+ - Used to specify the system capabilities TLV
+ type: bool
+ description:
+ description:
+ - Used to specify the system description TLV
+ type: bool
+ name:
+ description:
+ - Used to specify the system name TLV
+ type: bool
+ state:
+ description:
+ - The state of the configuration after module completion
+ type: str
+ choices:
+ - merged
+ - replaced
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+# Before state:
+# -------------
+#
+# user(config)# show running-config | include lldp
+# feature lldp
+
+- name: Merge provided configuration with device configuration
+ cisco.nxos.nxos_lldp_global:
+ config:
+ timer: 35
+ holdtime: 100
+ state: merged
+
+# After state:
+# ------------
+#
+# user(config)# show running-config | include lldp
+# feature lldp
+# lldp timer 35
+# lldp holdtime 100
+
+
+# Using replaced
+# Before state:
+# -------------
+#
+# user(config)# show running-config | include lldp
+# feature lldp
+# lldp holdtime 100
+# lldp reinit 5
+# lldp timer 35
+
+- name: Replace device configuration of specific LLDP attributes with provided configuration
+ cisco.nxos.nxos_lldp_global:
+ config:
+ timer: 40
+ tlv_select:
+ system:
+ description: true
+ name: false
+ management_address:
+ v4: true
+ state: replaced
+
+# After state:
+# ------------
+#
+# user(config)# show running-config | include lldp
+# feature lldp
+# lldp timer 40
+# no lldp tlv-select system-name
+
+
+# Using deleted
+# Before state:
+# -------------
+#
+# user(config)# show running-config | include lldp
+# feature lldp
+# lldp holdtime 5
+# lldp reinit 3
+
+- name: Delete LLDP configuration (this will by default remove all lldp configuration)
+ cisco.nxos.nxos_lldp_global:
+ state: deleted
+
+# After state:
+# ------------
+#
+# user(config)# show running-config | include lldp
+# feature lldp
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_lldp_global:
+ config:
+ holdtime: 130
+ port_id: 1
+ reinit: 5
+ tlv_select:
+ dcbxp: yes
+ power_management: yes
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - "lldp tlv-select dcbxp"
+# - "lldp tlv-select power-management"
+# - "lldp portid-subtype 1"
+# - "lldp reinit 5"
+# - "lldp holdtime 130"
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# lldp holdtime 131
+# lldp reinit 7
+# no lldp tlv-select system-name
+# no lldp tlv-select system-description
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# holdtime: 131
+# reinit: 7
+# tlv_select:
+# system:
+# description: false
+# name: false
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# feature lldp
+# lldp holdtime 129
+# lldp reinit 5
+# lldp timer 35
+# no lldp tlv-select system-name
+
+# Task output (redacted)
+# -----------------------
+
+# gathered:
+# reinit: 5
+# timer: 35
+# tlv_select:
+# system:
+# name: False
+# holdtime: 129
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['lldp holdtime 125', 'lldp reinit 4', 'no lldp tlv-select system-name']
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lldp_global.lldp_global import (
+ Lldp_globalArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.lldp_global.lldp_global import (
+ Lldp_global,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ mutually_exclusive = [("config", "running_config")]
+
+ module = AnsibleModule(
+ argument_spec=Lldp_globalArgs.argument_spec,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = Lldp_global(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_interfaces.py
new file mode 100644
index 00000000..045686a6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_lldp_interfaces.py
@@ -0,0 +1,260 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The module file for nxos_lldp_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_lldp_interfaces
+short_description: LLDP interfaces resource module
+description: This module manages interfaces' configuration for Link Layer Discovery
+ Protocol (LLDP) on NX-OS platforms.
+version_added: 1.0.0
+author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL
+- The LLDP feature needs to be enabled before using this module
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section ^interface).
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description:
+ - A list of link layer discovery configurations for interfaces.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the interface
+ required: true
+ type: str
+ receive:
+ description:
+ - Used to enable or disable the reception of LLDP packets on that interface.
+ By default, this is enabled after LLDP is enabled globally.
+ type: bool
+ transmit:
+ description:
+ - Used to enable or disable the transmission of LLDP packets on that interface.
+ By default, this is enabled after LLDP is enabled globally.
+ type: bool
+ tlv_set:
+ description:
+ - Used to configure TLV parameters on the interface
+ type: dict
+ suboptions:
+ management_address:
+ description:
+ - Used to mention the IPv4 or IPv6 management address for the interface
+ type: str
+ vlan:
+ description:
+ - Used to mention the VLAN for the interface
+ type: int
+ state:
+ description:
+ - The state the configuration should be left in
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+
+- name: Merge provided configuration with device configuration
+ cisco.nxos.nxos_lldp_interfaces:
+ config:
+ - name: Ethernet1/4
+ receive: false
+ transmit: true
+ tlv_set:
+ management_address: 192.168.122.64
+ vlan: 12
+ state: merged
+
+# After state:
+# -------------
+#
+# interface Ethernet1/4
+# no lldp receive
+# lldp tlv-set management-address 192.168.122.64
+# lldp tlv-set vlan 12
+
+
+# Using replaced
+
+# Before state:
+# ------------
+#
+# interface Ethernet1/4
+# no lldp receive
+# lldp tlv-set management-address 192.168.122.64
+# interface Ethernet1/5
+# no lldp transmit
+# lldp tlv-set vlan 10
+
+- name: Replace LLDP configuration on interfaces with given configuration
+ cisco.nxos.nxos_lldp_interfaces:
+ config:
+ - name: Ethernet1/4
+ transmit: no
+ tlv_set:
+ vlan: 2
+ state: replaced
+
+
+# After state:
+# -----------
+#
+# interface Ethernet1/4
+# no lldp transmit
+# lldp tlv_set vlan 2
+# interface Ethernet1/5
+# no lldp transmit
+# lldp tlv-set vlan 10
+
+
+# Using overridden
+
+# Before state:
+# ------------
+#
+# interface Ethernet1/4
+# no lldp receive
+# lldp tlv-set management-address 192.168.122.64
+# interface Ethernet1/5
+# no lldp transmit
+# lldp tlv-set vlan 10
+
+- name: Override LLDP configuration on all interfaces with given configuration
+ cisco.nxos.nxos_lldp_interfaces:
+ config:
+ - name: Ethernet1/7
+ receive: no
+ tlv_set:
+ vlan: 12
+ state: overridden
+
+
+# After state:
+# -----------
+#
+# interface Ethernet1/7
+# no lldp receive
+# lldp tlv_set vlan 12
+
+
+# Using deleted
+
+# Before state:
+# ------------
+#
+# interface Ethernet1/4
+# lldp tlv-set management vlan 24
+# no lldp transmit
+# interface mgmt0
+# no lldp receive
+
+- name: Delete LLDP interfaces configuration
+ cisco.nxos.nxos_lldp_interfaces:
+ state: deleted
+
+# After state:
+# ------------
+#
+
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['interface Ethernet1/2', 'lldp receive', 'lldp tlv-set vlan 12']
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.lldp_interfaces.lldp_interfaces import (
+ Lldp_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.lldp_interfaces.lldp_interfaces import (
+ Lldp_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=Lldp_interfacesArgs.argument_spec,
+ supports_check_mode=True,
+ )
+
+ result = Lldp_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_logging.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_logging.py
new file mode 100644
index 00000000..b8845861
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_logging.py
@@ -0,0 +1,979 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+# Copyright: (c) 2017, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+DOCUMENTATION = """
+module: nxos_logging
+author: Trishna Guha (@trishnaguha)
+short_description: Manage logging on network devices
+description:
+- This module provides declarative management of logging on Cisco NX-OS devices.
+version_added: 1.0.0
+options:
+ dest:
+ description:
+ - Destination of the logs.
+ choices:
+ - console
+ - logfile
+ - module
+ - monitor
+ - server
+ type: str
+ remote_server:
+ description:
+ - Hostname or IP Address for remote logging (when dest is 'server').
+ type: str
+ use_vrf:
+ description:
+ - VRF to be used while configuring remote logging (when dest is 'server').
+ type: str
+ interface:
+ description:
+ - Interface to be used while configuring source-interface for logging (e.g., 'Ethernet1/2',
+ 'mgmt0')
+ type: str
+ name:
+ description:
+ - If value of C(dest) is I(logfile) it indicates file-name.
+ type: str
+ facility:
+ description:
+ - Facility name for logging.
+ type: str
+ dest_level:
+ description:
+ - Set logging severity levels.
+ aliases:
+ - level
+ type: int
+ facility_level:
+ description:
+ - Set logging severity levels for facility based log messages.
+ type: int
+ aggregate:
+ description: List of logging definitions.
+ type: list
+ elements: dict
+ state:
+ description:
+ - State of the logging configuration.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ event:
+ description:
+ - Link/trunk enable/default interface configuration logging
+ choices:
+ - link-enable
+ - link-default
+ - trunk-enable
+ - trunk-default
+ type: str
+ interface_message:
+ description:
+ - Add interface description to interface syslogs. Does not work with version 6.0
+ images using nxapi as a transport.
+ choices:
+ - add-interface-description
+ type: str
+ file_size:
+ description:
+ - Set logfile size
+ type: int
+ facility_link_status:
+ description:
+ - Set logging facility ethpm link status. Not idempotent with version 6.0 images.
+ choices:
+ - link-down-notif
+ - link-down-error
+ - link-up-notif
+ - link-up-error
+ type: str
+ timestamp:
+ description:
+ - Set logging timestamp format
+ choices:
+ - microseconds
+ - milliseconds
+ - seconds
+ type: str
+ purge:
+ description:
+ - Remove any switch logging configuration that does not match what has been configured
+ Not supported for ansible_connection local. All nxos_logging tasks must use
+ the same ansible_connection type.
+ type: bool
+ default: false
+extends_documentation_fragment:
+- cisco.nxos.nxos
+"""
+
+EXAMPLES = """
+- name: configure console logging with level
+ cisco.nxos.nxos_logging:
+ dest: console
+ level: 2
+ state: present
+- name: remove console logging configuration
+ cisco.nxos.nxos_logging:
+ dest: console
+ level: 2
+ state: absent
+- name: configure file logging with level
+ cisco.nxos.nxos_logging:
+ dest: logfile
+ name: testfile
+ dest_level: 3
+ state: present
+- name: Configure logging logfile with size
+ cisco.nxos.nxos_logging:
+ dest: logfile
+ name: testfile
+ dest_level: 3
+ file_size: 16384
+- name: configure facility level logging
+ cisco.nxos.nxos_logging:
+ facility: daemon
+ facility_level: 0
+ state: present
+- name: remove facility level logging
+ cisco.nxos.nxos_logging:
+ facility: daemon
+ facility_level: 0
+ state: absent
+- name: Configure Remote Logging
+ cisco.nxos.nxos_logging:
+ dest: server
+ remote_server: test-syslogserver.com
+ facility: auth
+ facility_level: 1
+ use_vrf: management
+ state: present
+- name: Configure Source Interface for Logging
+ cisco.nxos.nxos_logging:
+ interface: mgmt0
+ state: present
+- name: Purge nxos_logging configuration not managed by this playbook
+ cisco.nxos.nxos_logging:
+ purge: true
+- name: Configure logging timestamp
+ cisco.nxos.nxos_logging:
+ timestamp: milliseconds
+ state: present
+- name: Configure logging facility ethpm link status
+ cisco.nxos.nxos_logging:
+ facility: ethpm
+ facility_link_status: link-up-notif
+ state: present
+- name: Configure logging message ethernet description
+ cisco.nxos.nxos_logging:
+ interface_message: add-interface-description
+ state: present
+- name: Configure logging event link enable
+ cisco.nxos.nxos_logging:
+ event: link-enable
+ state: present
+- name: Configure logging using aggregate
+ cisco.nxos.nxos_logging:
+ aggregate:
+ - {dest: console, dest_level: 2}
+ - {dest: logfile, dest_level: 2, name: testfile}
+ - {facility: daemon, facility_level: 0}
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always
+ type: list
+ sample:
+ - logging console 2
+ - logging logfile testfile 3
+ - logging level daemon 0
+"""
+
+import re
+import copy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+ save_module_context,
+ read_module_context,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ normalize_interface,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+STATIC_CLI = {
+ "link-enable": "logging event link-status enable",
+ "link-default": "logging event link-status default",
+ "trunk-enable": "logging event trunk-status enable",
+ "trunk-default": "logging event trunk-status default",
+ "microseconds": "logging timestamp microseconds",
+ "milliseconds": "logging timestamp milliseconds",
+ "seconds": "logging timestamp seconds",
+ "link-up-error": "link-up error",
+ "link-up-notif": "link-up notif",
+ "link-down-error": "link-down error",
+ "link-down-notif": "link-down notif",
+ "add-interface-description": "logging message interface type ethernet description",
+}
+
+DEFAULT_LOGGING_LEVEL = {
+ 0: [],
+ 1: [],
+ 2: ["pktmgr"],
+ 3: ["adjmgr", "arp", "icmpv6", "l2rib", "netstack"],
+ 4: [],
+ 5: ["mrib", "m6rib"],
+ 6: [],
+ 7: [],
+}
+
+DEST_GROUP = ["console", "logfile", "module", "monitor", "server"]
+
+
+def map_obj_to_commands(module, updates):
+ commands = list()
+ want, have = updates
+
+ for w in want:
+ state = w["state"]
+ del w["state"]
+
+ if state == "absent" and w in have:
+ if w["facility"] is not None:
+ if (
+ not w["dest"]
+ and not w["facility_link_status"]
+ and w["facility"]
+ not in DEFAULT_LOGGING_LEVEL[int(w["facility_level"])]
+ ):
+ commands.append(
+ "no logging level {0} {1}".format(
+ w["facility"], w["facility_level"]
+ )
+ )
+
+ if w["facility_link_status"] and w["facility"] in ("ethpm"):
+ commands.append(
+ "no logging level {0} {1}".format(
+ w["facility"],
+ STATIC_CLI[w["facility_link_status"]],
+ )
+ )
+
+ if w["name"] is not None:
+ commands.append("no logging logfile")
+
+ if w["dest"] in ("console", "module", "monitor"):
+ commands.append("no logging {0}".format(w["dest"]))
+
+ if w["dest"] == "server":
+ commands.append(
+ "no logging server {0}".format(w["remote_server"])
+ )
+
+ if w["interface"]:
+ commands.append("no logging source-interface")
+
+ if w["event"] and w["event"] in STATIC_CLI:
+ commands.append("no " + STATIC_CLI[w["event"]])
+
+ if w["message"] and w["message"] in STATIC_CLI:
+ commands.append("no " + STATIC_CLI[w["message"]])
+
+ if w["timestamp"] and w["timestamp"] in STATIC_CLI:
+ commands.append("no " + STATIC_CLI[w["timestamp"]])
+
+ if state == "present" and w not in have:
+ if w["facility"] is None:
+ if w["dest"]:
+ if w["dest"] not in ("logfile", "server"):
+ commands.append(
+ "logging {0} {1}".format(
+ w["dest"], w["dest_level"]
+ )
+ )
+
+ elif w["dest"] == "logfile":
+ if w["file_size"]:
+ commands.append(
+ "logging logfile {0} {1} size {2}".format(
+ w["name"], w["dest_level"], w["file_size"]
+ )
+ )
+ else:
+ commands.append(
+ "logging logfile {0} {1}".format(
+ w["name"], w["dest_level"]
+ )
+ )
+
+ elif w["dest"] == "server":
+ if w["facility_level"]:
+ if w["use_vrf"]:
+ commands.append(
+ "logging server {0} {1} use-vrf {2}".format(
+ w["remote_server"],
+ w["facility_level"],
+ w["use_vrf"],
+ )
+ )
+ else:
+ commands.append(
+ "logging server {0} {1}".format(
+ w["remote_server"], w["facility_level"]
+ )
+ )
+
+ else:
+ if w["use_vrf"]:
+ commands.append(
+ "logging server {0} use-vrf {1}".format(
+ w["remote_server"], w["use_vrf"]
+ )
+ )
+ else:
+ commands.append(
+ "logging server {0}".format(
+ w["remote_server"]
+ )
+ )
+
+ if w["facility"]:
+ if w["dest"] == "server":
+ if w["facility_level"]:
+ if w["use_vrf"]:
+ commands.append(
+ "logging server {0} {1} facility {2} use-vrf {3}".format(
+ w["remote_server"],
+ w["facility_level"],
+ w["facility"],
+ w["use_vrf"],
+ )
+ )
+ else:
+ commands.append(
+ "logging server {0} {1} facility {2}".format(
+ w["remote_server"],
+ w["facility_level"],
+ w["facility"],
+ )
+ )
+ else:
+ if w["use_vrf"]:
+ commands.append(
+ "logging server {0} facility {1} use-vrf {2}".format(
+ w["remote_server"],
+ w["facility"],
+ w["use_vrf"],
+ )
+ )
+ else:
+ commands.append(
+ "logging server {0} facility {1}".format(
+ w["remote_server"], w["facility"]
+ )
+ )
+ else:
+ if w["facility_link_status"]:
+ commands.append(
+ "logging level {0} {1}".format(
+ w["facility"],
+ STATIC_CLI[w["facility_link_status"]],
+ )
+ )
+ else:
+ if not match_facility_default(
+ module, w["facility"], w["facility_level"]
+ ):
+ commands.append(
+ "logging level {0} {1}".format(
+ w["facility"], w["facility_level"]
+ )
+ )
+
+ if w["interface"]:
+ commands.append(
+ "logging source-interface {0} {1}".format(
+ *split_interface(w["interface"])
+ )
+ )
+
+ if w["event"] and w["event"] in STATIC_CLI:
+ commands.append(STATIC_CLI[w["event"]])
+
+ if w["message"] and w["message"] in STATIC_CLI:
+ commands.append(STATIC_CLI[w["message"]])
+
+ if w["timestamp"] and w["timestamp"] in STATIC_CLI:
+ commands.append(STATIC_CLI[w["timestamp"]])
+
+ return commands
+
+
+def match_facility_default(module, facility, want_level):
+ """ Check wanted facility to see if it matches current device default """
+
+ matches_default = False
+ # Sample output from show logging level command
+ # Facility Default Severity Current Session Severity
+ # -------- ---------------- ------------------------
+ # bfd 5 5
+ #
+ # 0(emergencies) 1(alerts) 2(critical)
+ # 3(errors) 4(warnings) 5(notifications)
+ # 6(information) 7(debugging)
+
+ regexl = r"\S+\s+(\d+)\s+(\d+)"
+ cmd = {
+ "command": "show logging level {0}".format(facility),
+ "output": "text",
+ }
+ facility_data = run_commands(module, cmd)
+ for line in facility_data[0].split("\n"):
+ mo = re.search(regexl, line)
+ if (
+ mo
+ and int(mo.group(1)) == int(want_level)
+ and int(mo.group(2)) == int(want_level)
+ ):
+ matches_default = True
+
+ return matches_default
+
+
+def split_interface(interface):
+ match = re.search(r"(\D+)(\S*)", interface, re.M)
+ if match:
+ return match.group(1), match.group(2)
+
+
+def parse_facility_link_status(line, facility, status):
+ facility_link_status = None
+
+ if facility is not None:
+ match = re.search(
+ r"logging level {0} {1} (\S+)".format(facility, status), line, re.M
+ )
+ if match:
+ facility_link_status = status + "-" + match.group(1)
+
+ return facility_link_status
+
+
+def parse_event_status(line, event):
+ status = None
+
+ match = re.search(
+ r"logging event {0} (\S+)".format(event + "-status"), line, re.M
+ )
+ if match:
+ state = match.group(1)
+ if state:
+ status = state
+
+ return status
+
+
+def parse_event(line):
+ event = None
+
+ match = re.search(r"logging event (\S+)", line, re.M)
+ if match:
+ state = match.group(1)
+ if state == "link-status":
+ event = "link"
+ elif state == "trunk-status":
+ event = "trunk"
+
+ return event
+
+
+def parse_message(line):
+ message = None
+
+ match = re.search(
+ r"logging message interface type ethernet description", line, re.M
+ )
+ if match:
+ message = "add-interface-description"
+
+ return message
+
+
+def parse_file_size(line, name, level):
+ file_size = None
+
+ match = re.search(
+ r"logging logfile {0} {1} size (\S+)".format(name, level), line, re.M
+ )
+ if match:
+ file_size = match.group(1)
+ if file_size == "8192":
+ file_size = None
+
+ return file_size
+
+
+def parse_timestamp(line):
+ timestamp = None
+
+ match = re.search(r"logging timestamp (\S+)", line, re.M)
+ if match:
+ timestamp = match.group(1)
+
+ return timestamp
+
+
+def parse_name(line, dest):
+ name = None
+
+ if dest is not None:
+ if dest == "logfile":
+ match = re.search(r"logging logfile (\S+)", line, re.M)
+ if match:
+ name = match.group(1)
+ else:
+ pass
+
+ return name
+
+
+def parse_remote_server(line, dest):
+ remote_server = None
+
+ if dest and dest == "server":
+ match = re.search(r"logging server (\S+)", line, re.M)
+ if match:
+ remote_server = match.group(1)
+
+ return remote_server
+
+
+def parse_dest_level(line, dest, name):
+ dest_level = None
+
+ def parse_match(match):
+ level = None
+ if match:
+ if int(match.group(1)) in range(0, 8):
+ level = match.group(1)
+ else:
+ pass
+ return level
+
+ if dest and dest != "server":
+ if dest == "logfile":
+ match = re.search(
+ r"logging logfile {0} (\S+)".format(name), line, re.M
+ )
+ if match:
+ dest_level = parse_match(match)
+
+ elif dest == "server":
+ match = re.search(r"logging server (?:\S+) (\d+)", line, re.M)
+ if match:
+ dest_level = parse_match(match)
+ else:
+ match = re.search(r"logging {0} (\S+)".format(dest), line, re.M)
+ if match:
+ dest_level = parse_match(match)
+
+ return dest_level
+
+
+def parse_facility_level(line, facility, dest):
+ facility_level = None
+
+ if dest == "server":
+ match = re.search(r"logging server (?:\S+) (\d+)", line, re.M)
+ if match:
+ facility_level = match.group(1)
+
+ elif facility is not None:
+ match = re.search(
+ r"logging level {0} (\S+)".format(facility), line, re.M
+ )
+ if match:
+ facility_level = match.group(1)
+
+ return facility_level
+
+
+def parse_facility(line):
+ facility = None
+
+ match = re.search(
+ r"logging server (?:\S+) (?:\d+) (?:\S+) (?:\S+) (?:\S+) (\S+)",
+ line,
+ re.M,
+ )
+ if match:
+ facility = match.group(1)
+
+ return facility
+
+
+def parse_use_vrf(line, dest):
+ use_vrf = None
+
+ if dest and dest == "server":
+ match = re.search(
+ r"logging server (?:\S+) (?:\d+) use-vrf (\S+)", line, re.M
+ )
+ if match:
+ use_vrf = match.group(1)
+
+ return use_vrf
+
+
+def parse_interface(line):
+ interface = None
+
+ match = re.search(r"logging source-interface (\S*)", line, re.M)
+ if match:
+ interface = match.group(1)
+
+ return interface
+
+
+def map_config_to_obj(module):
+ obj = []
+
+ data = get_config(module, flags=[" all | section logging"])
+
+ for line in data.split("\n"):
+ if re.search(r"no (\S+)", line, re.M):
+ state = "absent"
+ else:
+ state = "present"
+
+ match = re.search(r"logging (\S+)", line, re.M)
+ if state == "present" and match:
+ event_status = None
+ name = None
+ dest_level = None
+ dest = None
+ facility = None
+ remote_server = None
+ facility_link_status = None
+ file_size = None
+ facility_level = None
+
+ if match.group(1) in DEST_GROUP:
+ dest = match.group(1)
+
+ name = parse_name(line, dest)
+ remote_server = parse_remote_server(line, dest)
+ dest_level = parse_dest_level(line, dest, name)
+
+ if dest == "server":
+ facility = parse_facility(line)
+
+ facility_level = parse_facility_level(line, facility, dest)
+
+ if dest == "logfile":
+ file_size = parse_file_size(line, name, dest_level)
+
+ elif match.group(1) == "level":
+ match_facility = re.search(r"logging level (\S+)", line, re.M)
+ facility = match_facility.group(1)
+
+ level = parse_facility_level(line, facility, dest)
+ if level.isdigit():
+ facility_level = level
+ else:
+ facility_link_status = parse_facility_link_status(
+ line, facility, level
+ )
+
+ elif match.group(1) == "event" and state == "present":
+ event = parse_event(line)
+ if event:
+ status = parse_event_status(line, event)
+ if status:
+ event_status = event + "-" + status
+ else:
+ continue
+
+ else:
+ pass
+
+ obj.append(
+ {
+ "dest": dest,
+ "remote_server": remote_server,
+ "use_vrf": parse_use_vrf(line, dest),
+ "name": name,
+ "facility": facility,
+ "dest_level": dest_level,
+ "facility_level": facility_level,
+ "interface": parse_interface(line),
+ "facility_link_status": facility_link_status,
+ "event": event_status,
+ "file_size": file_size,
+ "message": parse_message(line),
+ "timestamp": parse_timestamp(line),
+ }
+ )
+
+ cmd = [
+ {
+ "command": "show logging | section enabled | section console",
+ "output": "text",
+ },
+ {
+ "command": "show logging | section enabled | section monitor",
+ "output": "text",
+ },
+ ]
+
+ default_data = run_commands(module, cmd)
+
+ for line in default_data:
+ flag = False
+ match = re.search(
+ r"Logging (\w+):(?:\s+) (?:\w+) (?:\W)Severity: (\w+)",
+ str(line),
+ re.M,
+ )
+ if match:
+ if match.group(1) == "console" and match.group(2) == "critical":
+ dest_level = "2"
+ flag = True
+ elif (
+ match.group(1) == "monitor"
+ and match.group(2) == "notifications"
+ ):
+ dest_level = "5"
+ flag = True
+ if flag:
+ obj.append(
+ {
+ "dest": match.group(1),
+ "remote_server": None,
+ "name": None,
+ "facility": None,
+ "dest_level": dest_level,
+ "facility_level": None,
+ "use_vrf": None,
+ "interface": None,
+ "facility_link_status": None,
+ "event": None,
+ "file_size": None,
+ "message": None,
+ "timestamp": None,
+ }
+ )
+
+ return obj
+
+
+def map_params_to_obj(module):
+ obj = []
+
+ if "aggregate" in module.params and module.params["aggregate"]:
+ args = {
+ "dest": "",
+ "remote_server": "",
+ "use_vrf": "",
+ "name": "",
+ "facility": "",
+ "dest_level": "",
+ "facility_level": "",
+ "interface": "",
+ "facility_link_status": None,
+ "event": None,
+ "file_size": None,
+ "message": None,
+ "timestamp": None,
+ }
+
+ for c in module.params["aggregate"]:
+ d = c.copy()
+
+ for key in args:
+ if key not in d:
+ d[key] = None
+
+ if d["dest_level"] is not None:
+ d["dest_level"] = str(d["dest_level"])
+
+ if d["facility_level"] is not None:
+ d["facility_level"] = str(d["facility_level"])
+
+ if d["interface"]:
+ d["interface"] = normalize_interface(d["interface"])
+
+ if "state" not in d:
+ d["state"] = module.params["state"]
+
+ if d["file_size"]:
+ d["file_size"] = str(d["file_size"])
+
+ obj.append(d)
+
+ else:
+ dest_level = None
+ facility_level = None
+ file_size = None
+
+ if module.params["dest_level"] is not None:
+ dest_level = str(module.params["dest_level"])
+
+ if module.params["facility_level"] is not None:
+ facility_level = str(module.params["facility_level"])
+
+ if module.params["file_size"] is not None:
+ file_size = str(module.params["file_size"])
+
+ obj.append(
+ {
+ "dest": module.params["dest"],
+ "remote_server": module.params["remote_server"],
+ "use_vrf": module.params["use_vrf"],
+ "name": module.params["name"],
+ "facility": module.params["facility"],
+ "dest_level": dest_level,
+ "facility_level": facility_level,
+ "interface": normalize_interface(module.params["interface"]),
+ "state": module.params["state"],
+ "facility_link_status": module.params["facility_link_status"],
+ "event": module.params["event"],
+ "message": module.params["interface_message"],
+ "file_size": file_size,
+ "timestamp": module.params["timestamp"],
+ }
+ )
+ return obj
+
+
+def merge_wants(wants, want):
+ if not wants:
+ wants = list()
+
+ for w in want:
+ w = copy.copy(w)
+ state = w["state"]
+ del w["state"]
+
+ if state == "absent":
+ if w in wants:
+ wants.remove(w)
+ elif w not in wants:
+ wants.append(w)
+
+ return wants
+
+
+def absent(h):
+ h["state"] = "absent"
+ return h
+
+
+def outliers(haves, wants):
+ wants = list(wants)
+ return [absent(h) for h in haves if not (h in wants or wants.append(h))]
+
+
+def main():
+ """ main entry point for module execution
+ """
+ argument_spec = dict(
+ dest=dict(choices=DEST_GROUP),
+ name=dict(),
+ facility=dict(),
+ remote_server=dict(),
+ use_vrf=dict(),
+ dest_level=dict(type="int", aliases=["level"]),
+ facility_level=dict(type="int"),
+ interface=dict(),
+ facility_link_status=dict(
+ choices=[
+ "link-down-notif",
+ "link-down-error",
+ "link-up-notif",
+ "link-up-error",
+ ]
+ ),
+ event=dict(
+ choices=[
+ "link-enable",
+ "link-default",
+ "trunk-enable",
+ "trunk-default",
+ ]
+ ),
+ interface_message=dict(choices=["add-interface-description"]),
+ file_size=dict(type="int"),
+ timestamp=dict(choices=["microseconds", "milliseconds", "seconds"]),
+ state=dict(default="present", choices=["present", "absent"]),
+ aggregate=dict(type="list", elements="dict"),
+ purge=dict(default=False, type="bool"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ required_if = [
+ ("dest", "logfile", ["name"]),
+ ("dest", "server", ["remote_server"]),
+ ]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=required_if,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ merged_wants = merge_wants(read_module_context(module), want)
+ have = map_config_to_obj(module)
+
+ commands = map_obj_to_commands(module, (want, have))
+ result["commands"] = commands
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ save_module_context(module, merged_wants)
+
+ if module.params.get("purge"):
+ pcommands = map_obj_to_commands(
+ module, (outliers(have, merged_wants), have)
+ )
+ if pcommands:
+ if not module.check_mode:
+ load_config(module, pcommands)
+ result["changed"] = True
+ result["commands"] += pcommands
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp.py
new file mode 100644
index 00000000..3a4f0ec4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp.py
@@ -0,0 +1,459 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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 = """
+module: nxos_ntp
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages core NTP configuration.
+description:
+- Manages core NTP configuration.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+options:
+ server:
+ description:
+ - Network address of NTP server.
+ type: str
+ peer:
+ description:
+ - Network address of NTP peer.
+ type: str
+ key_id:
+ description:
+ - Authentication key identifier to use with given NTP server or peer or keyword
+ 'default'.
+ type: str
+ prefer:
+ description:
+ - Makes given NTP server or peer the preferred NTP server or peer for the device.
+ choices:
+ - enabled
+ - disabled
+ type: str
+ vrf_name:
+ description:
+ - Makes the device communicate with the given NTP server or peer over a specific
+ VRF or keyword 'default'.
+ type: str
+ source_addr:
+ description:
+ - Local source address from which NTP messages are sent or keyword 'default'.
+ type: str
+ source_int:
+ description:
+ - Local source interface from which NTP messages are sent. Must be fully qualified
+ interface name or keyword 'default'
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# Set NTP Server with parameters
+- cisco.nxos.nxos_ntp:
+ server: 1.2.3.4
+ key_id: 32
+ prefer: enabled
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"address": "192.0.2.2", "key_id": "48",
+ "peer_type": "server", "prefer": "enabled",
+ "source": "192.0.2.3", "source_type": "source"}
+existing:
+ description:
+ - k/v pairs of existing ntp server/peer
+ returned: always
+ type: dict
+ sample: {"address": "192.0.2.2", "key_id": "32",
+ "peer_type": "server", "prefer": "enabled",
+ "source": "ethernet2/1", "source_type": "source-interface"}
+end_state:
+ description: k/v pairs of ntp info after module execution
+ returned: always
+ type: dict
+ sample: {"address": "192.0.2.2", "key_id": "48",
+ "peer_type": "server", "prefer": "enabled",
+ "source": "192.0.2.3", "source_type": "source"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["ntp server 192.0.2.2 prefer key 48",
+ "no ntp source-interface ethernet2/1", "ntp source 192.0.2.3"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+import re
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ nxos_argument_spec,
+ run_commands,
+)
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ if "show run" not in command:
+ output = "json"
+ else:
+ output = "text"
+
+ commands = [{"command": command, "output": output}]
+ return run_commands(module, commands)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_ntp_source(module):
+ source_type = None
+ source = None
+ command = "show run | inc ntp.source"
+ output = execute_show_command(
+ command, module, command_type="cli_show_ascii"
+ )
+
+ if output:
+ try:
+ if "interface" in output[0]:
+ source_type = "source-interface"
+ else:
+ source_type = "source"
+ source = output[0].split()[2].lower()
+ except (AttributeError, IndexError):
+ source_type = None
+ source = None
+
+ return source_type, source
+
+
+def get_ntp_peer(module):
+ command = "show run | inc ntp.(server|peer)"
+ ntp_peer_list = []
+ response = execute_show_command(
+ command, module, command_type="cli_show_ascii"
+ )
+
+ if response:
+ if isinstance(response, list):
+ ntp = response[0]
+ else:
+ ntp = response
+ if ntp:
+ ntp_regex = (
+ r".*ntp\s(server\s(?P<address>\S+)|peer\s(?P<peer_address>\S+))"
+ r"\s*((?P<prefer>prefer)\s*)?(use-vrf\s(?P<vrf_name>\S+)\s*)?"
+ r"(key\s(?P<key_id>\d+))?.*"
+ )
+
+ split_ntp = ntp.splitlines()
+ for peer_line in split_ntp:
+ if "access-group" in peer_line:
+ continue
+ ntp_peer = {}
+ try:
+ peer_address = None
+ vrf_name = "default"
+ prefer = None
+ key_id = None
+ match_ntp = re.match(ntp_regex, peer_line, re.DOTALL)
+ group_ntp = match_ntp.groupdict()
+
+ address = group_ntp["address"]
+ peer_address = group_ntp["peer_address"]
+ prefer = group_ntp["prefer"]
+ vrf_name = group_ntp["vrf_name"]
+ key_id = group_ntp["key_id"]
+
+ if prefer is not None:
+ prefer = "enabled"
+ else:
+ prefer = "disabled"
+
+ if address is not None:
+ peer_type = "server"
+ elif peer_address is not None:
+ peer_type = "peer"
+ address = peer_address
+
+ args = dict(
+ peer_type=peer_type,
+ address=address,
+ prefer=prefer,
+ vrf_name=vrf_name,
+ key_id=key_id,
+ )
+
+ ntp_peer = dict((k, v) for k, v in args.items())
+ ntp_peer_list.append(ntp_peer)
+ except AttributeError:
+ ntp_peer_list = []
+
+ return ntp_peer_list
+
+
+def get_ntp_existing(address, peer_type, module):
+ peer_dict = {}
+ peer_server_list = []
+
+ peer_list = get_ntp_peer(module)
+ for peer in peer_list:
+ if peer["address"] == address:
+ peer_dict.update(peer)
+ else:
+ peer_server_list.append(peer)
+
+ source_type, source = get_ntp_source(module)
+
+ if source_type is not None and source is not None:
+ peer_dict["source_type"] = source_type
+ peer_dict["source"] = source
+
+ return (peer_dict, peer_server_list)
+
+
+def set_ntp_server_peer(peer_type, address, prefer, key_id, vrf_name):
+ command_strings = []
+
+ if prefer:
+ command_strings.append(" prefer")
+ if key_id:
+ command_strings.append(" key {0}".format(key_id))
+ if vrf_name:
+ command_strings.append(" use-vrf {0}".format(vrf_name))
+
+ command_strings.insert(0, "ntp {0} {1}".format(peer_type, address))
+
+ command = "".join(command_strings)
+
+ return command
+
+
+def config_ntp(delta, existing):
+ if (
+ delta.get("address")
+ or delta.get("peer_type")
+ or delta.get("vrf_name")
+ or delta.get("key_id")
+ or delta.get("prefer")
+ ):
+ address = delta.get("address", existing.get("address"))
+ peer_type = delta.get("peer_type", existing.get("peer_type"))
+ key_id = delta.get("key_id", existing.get("key_id"))
+ prefer = delta.get("prefer", existing.get("prefer"))
+ vrf_name = delta.get("vrf_name", existing.get("vrf_name"))
+ if delta.get("key_id") == "default":
+ key_id = None
+ else:
+ peer_type = None
+ prefer = None
+
+ source_type = delta.get("source_type")
+ source = delta.get("source")
+
+ if prefer:
+ if prefer == "enabled":
+ prefer = True
+ elif prefer == "disabled":
+ prefer = False
+
+ if source:
+ source_type = delta.get("source_type", existing.get("source_type"))
+
+ ntp_cmds = []
+ if peer_type:
+ if existing.get("peer_type") and existing.get("address"):
+ ntp_cmds.append(
+ "no ntp {0} {1}".format(
+ existing.get("peer_type"), existing.get("address")
+ )
+ )
+ ntp_cmds.append(
+ set_ntp_server_peer(peer_type, address, prefer, key_id, vrf_name)
+ )
+ if source:
+ existing_source_type = existing.get("source_type")
+ existing_source = existing.get("source")
+ if existing_source_type and source_type != existing_source_type:
+ ntp_cmds.append(
+ "no ntp {0} {1}".format(existing_source_type, existing_source)
+ )
+ if source == "default":
+ if existing_source_type and existing_source:
+ ntp_cmds.append(
+ "no ntp {0} {1}".format(
+ existing_source_type, existing_source
+ )
+ )
+ else:
+ ntp_cmds.append("ntp {0} {1}".format(source_type, source))
+
+ return ntp_cmds
+
+
+def main():
+ argument_spec = dict(
+ server=dict(type="str"),
+ peer=dict(type="str"),
+ key_id=dict(type="str"),
+ prefer=dict(type="str", choices=["enabled", "disabled"]),
+ vrf_name=dict(type="str"),
+ source_addr=dict(type="str"),
+ source_int=dict(type="str"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[["server", "peer"], ["source_addr", "source_int"]],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+
+ server = module.params["server"] or None
+ peer = module.params["peer"] or None
+ key_id = module.params["key_id"]
+ prefer = module.params["prefer"]
+ vrf_name = module.params["vrf_name"]
+ source_addr = module.params["source_addr"]
+ source_int = module.params["source_int"]
+ state = module.params["state"]
+
+ if source_int is not None:
+ source_int = source_int.lower()
+
+ if server:
+ peer_type = "server"
+ address = server
+ elif peer:
+ peer_type = "peer"
+ address = peer
+ else:
+ peer_type = None
+ address = None
+
+ source_type = None
+ source = None
+ if source_addr:
+ source_type = "source"
+ source = source_addr
+ elif source_int:
+ source_type = "source-interface"
+ source = source_int
+
+ if key_id or vrf_name or prefer:
+ if not server and not peer:
+ module.fail_json(msg="Please supply the server or peer parameter")
+
+ args = dict(
+ peer_type=peer_type,
+ address=address,
+ key_id=key_id,
+ prefer=prefer,
+ vrf_name=vrf_name,
+ source_type=source_type,
+ source=source,
+ )
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ existing, peer_server_list = get_ntp_existing(address, peer_type, module)
+
+ end_state = existing
+ changed = False
+ commands = []
+
+ if state == "present":
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta.get("key_id") and delta.get("key_id") == "default":
+ if not existing.get("key_id"):
+ delta.pop("key_id")
+ if delta:
+ command = config_ntp(delta, existing)
+ if command:
+ commands.append(command)
+
+ elif state == "absent":
+ if existing.get("peer_type") and existing.get("address"):
+ command = "no ntp {0} {1}".format(
+ existing["peer_type"], existing["address"]
+ )
+ if command:
+ commands.append([command])
+
+ existing_source_type = existing.get("source_type")
+ existing_source = existing.get("source")
+ proposed_source_type = proposed.get("source_type")
+ proposed_source = proposed.get("source")
+
+ if proposed_source_type:
+ if proposed_source_type == existing_source_type:
+ if proposed_source == existing_source:
+ command = "no ntp {0} {1}".format(
+ existing_source_type, existing_source
+ )
+ if command:
+ commands.append([command])
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+ end_state = get_ntp_existing(address, peer_type, module)[0]
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+ results["end_state"] = end_state
+ results["peer_server_list"] = peer_server_list
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_auth.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_auth.py
new file mode 100644
index 00000000..4aee5b9f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_auth.py
@@ -0,0 +1,347 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_ntp_auth
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages NTP authentication.
+description:
+- Manages NTP authentication.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- If C(state=absent), the module will remove the given key configuration if it exists.
+- If C(state=absent) and C(authentication=on), authentication will be turned off.
+options:
+ key_id:
+ description:
+ - Authentication key identifier (numeric).
+ type: str
+ md5string:
+ description:
+ - MD5 String.
+ type: str
+ auth_type:
+ description:
+ - Whether the given md5string is in cleartext or has been encrypted. If in cleartext,
+ the device will encrypt it before storing it.
+ default: text
+ choices:
+ - text
+ - encrypt
+ type: str
+ trusted_key:
+ description:
+ - Whether the given key is required to be supplied by a time source for the device
+ to synchronize to the time source.
+ choices:
+ - 'false'
+ - 'true'
+ default: 'false'
+ type: str
+ authentication:
+ description:
+ - Turns NTP authentication on or off.
+ choices:
+ - "on"
+ - "off"
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# Basic NTP authentication configuration
+- cisco.nxos.nxos_ntp_auth:
+ key_id: 32
+ md5string: hello
+ auth_type: text
+"""
+
+RETURN = """
+commands:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["ntp authentication-key 32 md5 helloWorld 0", "ntp trusted-key 32"]
+"""
+
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ if "show run" not in command:
+ command = {"command": command, "output": "json"}
+ else:
+ command = {"command": command, "output": "text"}
+
+ return run_commands(module, [command])
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_ntp_auth(module):
+ command = "show ntp authentication-status"
+
+ body = execute_show_command(command, module)[0]
+ ntp_auth_str = body["authentication"]
+
+ if "enabled" in ntp_auth_str:
+ ntp_auth = True
+ else:
+ ntp_auth = False
+
+ return ntp_auth
+
+
+def get_ntp_trusted_key(module):
+ trusted_key_list = []
+ command = "show run | inc ntp.trusted-key"
+
+ trusted_key_str = execute_show_command(command, module)[0]
+ if trusted_key_str:
+ trusted_keys = trusted_key_str.splitlines()
+
+ else:
+ trusted_keys = []
+
+ for line in trusted_keys:
+ if line:
+ trusted_key_list.append(str(line.split()[2]))
+
+ return trusted_key_list
+
+
+def get_ntp_auth_key(key_id, module):
+ authentication_key = {}
+ command = "show run | inc ntp.authentication-key.{0}".format(key_id)
+ auth_regex = (
+ r".*ntp\sauthentication-key\s(?P<key_id>\d+)\s"
+ r"md5\s(?P<md5string>\S+)\s(?P<atype>\S+).*"
+ )
+
+ body = execute_show_command(command, module)[0]
+
+ try:
+ match_authentication = re.match(auth_regex, body, re.DOTALL)
+ group_authentication = match_authentication.groupdict()
+ authentication_key["key_id"] = group_authentication["key_id"]
+ authentication_key["md5string"] = group_authentication["md5string"]
+ if group_authentication["atype"] == "7":
+ authentication_key["auth_type"] = "encrypt"
+ else:
+ authentication_key["auth_type"] = "text"
+ except (AttributeError, TypeError):
+ authentication_key = {}
+
+ return authentication_key
+
+
+def get_ntp_auth_info(key_id, module):
+ auth_info = get_ntp_auth_key(key_id, module)
+ trusted_key_list = get_ntp_trusted_key(module)
+ auth_power = get_ntp_auth(module)
+
+ if key_id in trusted_key_list:
+ auth_info["trusted_key"] = "true"
+ else:
+ auth_info["trusted_key"] = "false"
+
+ if auth_power:
+ auth_info["authentication"] = "on"
+ else:
+ auth_info["authentication"] = "off"
+
+ return auth_info
+
+
+def auth_type_to_num(auth_type):
+ if auth_type == "encrypt":
+ return "7"
+ else:
+ return "0"
+
+
+def set_ntp_auth_key(
+ key_id, md5string, auth_type, trusted_key, authentication
+):
+ ntp_auth_cmds = []
+ if key_id and md5string:
+ auth_type_num = auth_type_to_num(auth_type)
+ ntp_auth_cmds.append(
+ "ntp authentication-key {0} md5 {1} {2}".format(
+ key_id, md5string, auth_type_num
+ )
+ )
+
+ if trusted_key == "true":
+ ntp_auth_cmds.append("ntp trusted-key {0}".format(key_id))
+ elif trusted_key == "false":
+ ntp_auth_cmds.append("no ntp trusted-key {0}".format(key_id))
+
+ if authentication == "on":
+ ntp_auth_cmds.append("ntp authenticate")
+ elif authentication == "off":
+ ntp_auth_cmds.append("no ntp authenticate")
+
+ return ntp_auth_cmds
+
+
+def remove_ntp_auth_key(
+ key_id, md5string, auth_type, trusted_key, authentication
+):
+ auth_remove_cmds = []
+ if key_id:
+ auth_type_num = auth_type_to_num(auth_type)
+ auth_remove_cmds.append(
+ "no ntp authentication-key {0} md5 {1} {2}".format(
+ key_id, md5string, auth_type_num
+ )
+ )
+
+ if authentication:
+ auth_remove_cmds.append("no ntp authenticate")
+ return auth_remove_cmds
+
+
+def main():
+ argument_spec = dict(
+ key_id=dict(type="str"),
+ md5string=dict(type="str"),
+ auth_type=dict(choices=["text", "encrypt"], default="text"),
+ trusted_key=dict(choices=["true", "false"], default="false"),
+ authentication=dict(choices=["on", "off"]),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ key_id = module.params["key_id"]
+ md5string = module.params["md5string"]
+ auth_type = module.params["auth_type"]
+ trusted_key = module.params["trusted_key"]
+ authentication = module.params["authentication"]
+ state = module.params["state"]
+
+ if key_id:
+ if not trusted_key and not md5string:
+ module.fail_json(msg="trusted_key or md5string MUST be specified")
+
+ args = dict(
+ key_id=key_id,
+ md5string=md5string,
+ auth_type=auth_type,
+ trusted_key=trusted_key,
+ authentication=authentication,
+ )
+
+ changed = False
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ existing = get_ntp_auth_info(key_id, module)
+ end_state = existing
+
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ commands = []
+ if state == "present":
+ if delta:
+ command = set_ntp_auth_key(
+ key_id,
+ md5string,
+ delta.get("auth_type"),
+ delta.get("trusted_key"),
+ delta.get("authentication"),
+ )
+ if command:
+ commands.append(command)
+ elif state == "absent":
+ auth_toggle = None
+ if existing.get("authentication") == "on":
+ auth_toggle = True
+ if not existing.get("key_id"):
+ key_id = None
+ command = remove_ntp_auth_key(
+ key_id, md5string, auth_type, trusted_key, auth_toggle
+ )
+ if command:
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ load_config(module, cmds)
+ end_state = get_ntp_auth_info(key_id, module)
+ delta = dict(set(end_state.items()).difference(existing.items()))
+ if delta or (len(existing) != len(end_state)):
+ changed = True
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+ results["end_state"] = end_state
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_options.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_options.py
new file mode 100644
index 00000000..21a47155
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ntp_options.py
@@ -0,0 +1,173 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_ntp_options
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages NTP options.
+description:
+- Manages NTP options, e.g. authoritative server and logging.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- When C(state=absent), master and logging will be set to False and stratum will be
+ removed as well
+options:
+ master:
+ description:
+ - Sets whether the device is an authoritative NTP server.
+ type: bool
+ stratum:
+ description:
+ - If C(master=true), an optional stratum can be supplied (1-15). The device default
+ is 8.
+ type: str
+ logging:
+ description:
+ - Sets whether NTP logging is enabled on the device.
+ type: bool
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+EXAMPLES = """
+# Basic NTP options configuration
+- cisco.nxos.nxos_ntp_options:
+ master: true
+ stratum: 12
+ logging: false
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["no ntp logging", "ntp master 12"]
+"""
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_current(module):
+ cmd = "show running-config | inc ntp"
+
+ master = False
+ logging = False
+ stratum = None
+
+ output = run_commands(module, ({"command": cmd, "output": "text"}))[0]
+
+ if output:
+ match = re.search(r"^ntp master(?: (\d+))", output, re.M)
+ if match:
+ master = True
+ stratum = match.group(1)
+ logging = "ntp logging" in output.lower()
+
+ return {"master": master, "stratum": stratum, "logging": logging}
+
+
+def main():
+ argument_spec = dict(
+ master=dict(required=False, type="bool"),
+ stratum=dict(required=False, type="str"),
+ logging=dict(required=False, type="bool"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ master = module.params["master"]
+ stratum = module.params["stratum"]
+ logging = module.params["logging"]
+ state = module.params["state"]
+
+ if stratum and master is False:
+ if stratum != 8:
+ module.fail_json(msg="master MUST be True when stratum is changed")
+
+ current = get_current(module)
+
+ result = {"changed": False}
+
+ commands = list()
+
+ if state == "absent":
+ if current["master"]:
+ commands.append("no ntp master")
+ if current["logging"]:
+ commands.append("no ntp logging")
+
+ elif state == "present":
+ if master and not current["master"]:
+ commands.append("ntp master")
+ elif master is False and current["master"]:
+ commands.append("no ntp master")
+ if stratum and stratum != current["stratum"]:
+ commands.append("ntp master %s" % stratum)
+
+ if logging and not current["logging"]:
+ commands.append("ntp logging")
+ elif logging is False and current["logging"]:
+ commands.append("no ntp logging")
+
+ result["commands"] = commands
+ result["updates"] = commands
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ result["warnings"] = warnings
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_nxapi.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_nxapi.py
new file mode 100644
index 00000000..f3d2fde9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_nxapi.py
@@ -0,0 +1,442 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+# pylint: skip-file
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_nxapi
+extends_documentation_fragment:
+- cisco.nxos.nxos
+author: Peter Sprygada (@privateip)
+short_description: Manage NXAPI configuration on an NXOS device.
+description:
+- Configures the NXAPI feature on devices running Cisco NXOS. The NXAPI feature is
+ absent from the configuration by default. Since this module manages the NXAPI feature
+ it only supports the use of the C(Cli) transport.
+version_added: 1.0.0
+options:
+ http_port:
+ description:
+ - Configure the port with which the HTTP server will listen on for requests. By
+ default, NXAPI will bind the HTTP service to the standard HTTP port 80. This
+ argument accepts valid port values in the range of 1 to 65535.
+ required: false
+ default: 80
+ type: int
+ http:
+ description:
+ - Controls the operating state of the HTTP protocol as one of the underlying transports
+ for NXAPI. By default, NXAPI will enable the HTTP transport when the feature
+ is first configured. To disable the use of the HTTP transport, set the value
+ of this argument to False.
+ required: false
+ default: true
+ type: bool
+ aliases:
+ - enable_http
+ https_port:
+ description:
+ - Configure the port with which the HTTPS server will listen on for requests. By
+ default, NXAPI will bind the HTTPS service to the standard HTTPS port 443. This
+ argument accepts valid port values in the range of 1 to 65535.
+ required: false
+ default: 443
+ type: int
+ https:
+ description:
+ - Controls the operating state of the HTTPS protocol as one of the underlying
+ transports for NXAPI. By default, NXAPI will disable the HTTPS transport when
+ the feature is first configured. To enable the use of the HTTPS transport,
+ set the value of this argument to True.
+ required: false
+ default: false
+ type: bool
+ aliases:
+ - enable_https
+ sandbox:
+ description:
+ - The NXAPI feature provides a web base UI for developers for entering commands. This
+ feature is initially disabled when the NXAPI feature is configured for the first
+ time. When the C(sandbox) argument is set to True, the developer sandbox URL
+ will accept requests and when the value is set to False, the sandbox URL is
+ unavailable. This is supported on NX-OS 7K series.
+ required: false
+ type: bool
+ aliases:
+ - enable_sandbox
+ state:
+ description:
+ - The C(state) argument controls whether or not the NXAPI feature is configured
+ on the remote device. When the value is C(present) the NXAPI feature configuration
+ is present in the device running-config. When the values is C(absent) the feature
+ configuration is removed from the running-config.
+ choices:
+ - present
+ - absent
+ required: false
+ default: present
+ type: str
+ ssl_strong_ciphers:
+ description:
+ - Controls the use of whether strong or weak ciphers are configured. By default,
+ this feature is disabled and weak ciphers are configured. To enable the use
+ of strong ciphers, set the value of this argument to True.
+ required: false
+ default: false
+ type: bool
+ tlsv1_0:
+ description:
+ - Controls the use of the Transport Layer Security version 1.0 is configured. By
+ default, this feature is enabled. To disable the use of TLSV1.0, set the value
+ of this argument to True.
+ required: false
+ default: true
+ type: bool
+ tlsv1_1:
+ description:
+ - Controls the use of the Transport Layer Security version 1.1 is configured. By
+ default, this feature is disabled. To enable the use of TLSV1.1, set the value
+ of this argument to True.
+ required: false
+ default: false
+ type: bool
+ tlsv1_2:
+ description:
+ - Controls the use of the Transport Layer Security version 1.2 is configured. By
+ default, this feature is disabled. To enable the use of TLSV1.2, set the value
+ of this argument to True.
+ required: false
+ default: false
+ type: bool
+"""
+
+EXAMPLES = """
+- name: Enable NXAPI access with default configuration
+ cisco.nxos.nxos_nxapi:
+ state: present
+
+- name: Enable NXAPI with no HTTP, HTTPS at port 9443 and sandbox disabled
+ cisco.nxos.nxos_nxapi:
+ enable_http: false
+ https_port: 9443
+ https: yes
+ enable_sandbox: no
+
+- name: remove NXAPI configuration
+ cisco.nxos.nxos_nxapi:
+ state: absent
+"""
+
+RETURN = """
+updates:
+ description:
+ - Returns the list of commands that need to be pushed into the remote
+ device to satisfy the arguments
+ returned: always
+ type: list
+ sample: ['no feature nxapi']
+"""
+import re
+
+from distutils.version import LooseVersion
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ run_commands,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def check_args(module, warnings, capabilities):
+ network_api = capabilities.get("network_api", "nxapi")
+ if network_api == "nxapi":
+ module.fail_json(msg="module not supported over nxapi transport")
+
+ os_platform = capabilities["device_info"]["network_os_platform"]
+ if "7K" not in os_platform and module.params["sandbox"]:
+ module.fail_json(
+ msg="sandbox or enable_sandbox is supported on NX-OS 7K series of switches"
+ )
+
+ state = module.params["state"]
+
+ if state == "started":
+ module.params["state"] = "present"
+ warnings.append(
+ "state=started is deprecated and will be removed in a "
+ "a future release. Please use state=present instead"
+ )
+ elif state == "stopped":
+ module.params["state"] = "absent"
+ warnings.append(
+ "state=stopped is deprecated and will be removed in a "
+ "a future release. Please use state=absent instead"
+ )
+
+ for key in ["http_port", "https_port"]:
+ if module.params[key] is not None:
+ if not 1 <= module.params[key] <= 65535:
+ module.fail_json(msg="%s must be between 1 and 65535" % key)
+
+ return warnings
+
+
+def map_obj_to_commands(want, have, module, warnings, capabilities):
+ send_commands = list()
+ commands = dict()
+ os_platform = None
+ os_version = None
+
+ device_info = capabilities.get("device_info")
+ if device_info:
+ os_version = device_info.get("network_os_version")
+ if os_version:
+ os_version = os_version[:3]
+ os_platform = device_info.get("network_os_platform")
+ if os_platform:
+ os_platform = os_platform[:3]
+
+ def needs_update(x):
+ return want.get(x) is not None and (want.get(x) != have.get(x))
+
+ if needs_update("state"):
+ if want["state"] == "absent":
+ return ["no feature nxapi"]
+ send_commands.append("feature nxapi")
+ elif want["state"] == "absent":
+ return send_commands
+
+ for parameter in ["http", "https"]:
+ port_param = parameter + "_port"
+ if needs_update(parameter):
+ if want.get(parameter) is False:
+ commands[parameter] = "no nxapi %s" % parameter
+ else:
+ commands[parameter] = "nxapi %s port %s" % (
+ parameter,
+ want.get(port_param),
+ )
+
+ if needs_update(port_param) and want.get(parameter) is True:
+ commands[parameter] = "nxapi %s port %s" % (
+ parameter,
+ want.get(port_param),
+ )
+
+ if needs_update("sandbox"):
+ commands["sandbox"] = "nxapi sandbox"
+ if not want["sandbox"]:
+ commands["sandbox"] = "no %s" % commands["sandbox"]
+
+ if os_platform and os_version:
+ if (os_platform == "N9K" or os_platform == "N3K") and LooseVersion(
+ os_version
+ ) >= "9.2":
+ if needs_update("ssl_strong_ciphers"):
+ commands["ssl_strong_ciphers"] = "nxapi ssl ciphers weak"
+ if want["ssl_strong_ciphers"] is True:
+ commands[
+ "ssl_strong_ciphers"
+ ] = "no nxapi ssl ciphers weak"
+
+ have_ssl_protocols = ""
+ want_ssl_protocols = ""
+ for key, value in {
+ "tlsv1_2": "TLSv1.2",
+ "tlsv1_1": "TLSv1.1",
+ "tlsv1_0": "TLSv1",
+ }.items():
+ if needs_update(key):
+ if want.get(key) is True:
+ want_ssl_protocols = " ".join(
+ [want_ssl_protocols, value]
+ )
+ elif have.get(key) is True:
+ have_ssl_protocols = " ".join([have_ssl_protocols, value])
+
+ if len(want_ssl_protocols) > 0:
+ commands["ssl_protocols"] = "nxapi ssl protocols%s" % (
+ " ".join([want_ssl_protocols, have_ssl_protocols])
+ )
+ else:
+ warnings.append(
+ "os_version and/or os_platform keys from "
+ "platform capabilities are not available. "
+ "Any NXAPI SSL optional arguments will be ignored"
+ )
+
+ send_commands.extend(commands.values())
+
+ return send_commands
+
+
+def parse_http(data):
+ http_res = [r"nxapi http port (\d+)"]
+ http_port = None
+
+ for regex in http_res:
+ match = re.search(regex, data, re.M)
+ if match:
+ http_port = int(match.group(1))
+ break
+
+ return {"http": http_port is not None, "http_port": http_port}
+
+
+def parse_https(data):
+ https_res = [r"nxapi https port (\d+)"]
+ https_port = None
+
+ for regex in https_res:
+ match = re.search(regex, data, re.M)
+ if match:
+ https_port = int(match.group(1))
+ break
+
+ return {"https": https_port is not None, "https_port": https_port}
+
+
+def parse_sandbox(data):
+ sandbox = [
+ item for item in data.split("\n") if re.search(r".*sandbox.*", item)
+ ]
+ value = False
+ if sandbox and sandbox[0] == "nxapi sandbox":
+ value = True
+ return {"sandbox": value}
+
+
+def parse_ssl_strong_ciphers(data):
+ ciphers_res = [r"(\w+) nxapi ssl ciphers weak"]
+ value = None
+
+ for regex in ciphers_res:
+ match = re.search(regex, data, re.M)
+ if match:
+ value = match.group(1)
+ break
+
+ return {"ssl_strong_ciphers": value == "no"}
+
+
+def parse_ssl_protocols(data):
+ tlsv1_0 = re.search(r"(?<!\S)TLSv1(?!\S)", data, re.M) is not None
+ tlsv1_1 = re.search(r"(?<!\S)TLSv1.1(?!\S)", data, re.M) is not None
+ tlsv1_2 = re.search(r"(?<!\S)TLSv1.2(?!\S)", data, re.M) is not None
+
+ return {"tlsv1_0": tlsv1_0, "tlsv1_1": tlsv1_1, "tlsv1_2": tlsv1_2}
+
+
+def map_config_to_obj(module):
+ out = run_commands(module, ["show run all | inc nxapi"], check_rc=False)[0]
+ match = re.search(r"no feature nxapi", out, re.M)
+ # There are two possible outcomes when nxapi is disabled on nxos platforms.
+ # 1. Nothing is displayed in the running config.
+ # 2. The 'no feature nxapi' command is displayed in the running config.
+ if match or out == "":
+ return {"state": "absent"}
+
+ out = str(out).strip()
+
+ obj = {"state": "present"}
+ obj.update(parse_http(out))
+ obj.update(parse_https(out))
+ obj.update(parse_sandbox(out))
+ obj.update(parse_ssl_strong_ciphers(out))
+ obj.update(parse_ssl_protocols(out))
+
+ return obj
+
+
+def map_params_to_obj(module):
+ obj = {
+ "http": module.params["http"],
+ "http_port": module.params["http_port"],
+ "https": module.params["https"],
+ "https_port": module.params["https_port"],
+ "sandbox": module.params["sandbox"],
+ "state": module.params["state"],
+ "ssl_strong_ciphers": module.params["ssl_strong_ciphers"],
+ "tlsv1_0": module.params["tlsv1_0"],
+ "tlsv1_1": module.params["tlsv1_1"],
+ "tlsv1_2": module.params["tlsv1_2"],
+ }
+
+ return obj
+
+
+def main():
+ """ main entry point for module execution
+ """
+ argument_spec = dict(
+ http=dict(aliases=["enable_http"], type="bool", default=True),
+ http_port=dict(type="int", default=80),
+ https=dict(aliases=["enable_https"], type="bool", default=False),
+ https_port=dict(type="int", default=443),
+ sandbox=dict(aliases=["enable_sandbox"], type="bool"),
+ state=dict(default="present", choices=["present", "absent"]),
+ ssl_strong_ciphers=dict(type="bool", default=False),
+ tlsv1_0=dict(type="bool", default=True),
+ tlsv1_1=dict(type="bool", default=False),
+ tlsv1_2=dict(type="bool", default=False),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ warning_msg = (
+ "Module nxos_nxapi currently defaults to configure 'http port 80'. "
+ )
+ warning_msg += "Default behavior is changing to configure 'https port 443'"
+ warning_msg += " when params 'http, http_port, https, https_port' are not set in the playbook"
+ module.deprecate(
+ msg=warning_msg, date="2022-06-01", collection_name="cisco.nxos"
+ )
+
+ capabilities = get_capabilities(module)
+
+ check_args(module, warnings, capabilities)
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(module)
+
+ commands = map_obj_to_commands(want, have, module, warnings, capabilities)
+
+ result = {"changed": False, "warnings": warnings, "commands": commands}
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf.py
new file mode 100644
index 00000000..53640b13
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf.py
@@ -0,0 +1,171 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_ospf
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manages configuration
+ of an ospf instance.
+description:
+- Manages configuration of an ospf instance.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+deprecated:
+ alternative: nxos_ospfv2 and nxos_ospfv3
+ why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
+options:
+ ospf:
+ description:
+ - Name of the ospf instance.
+ required: true
+ type: str
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ required: false
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_ospf:
+ ospf: 1
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["router ospf 1"]
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+PARAM_TO_COMMAND_KEYMAP = {"ospf": "router ospf"}
+
+
+def get_value(config, module):
+ splitted_config = config.splitlines()
+ value_list = []
+ REGEX = r"^router ospf\s(?P<ospf>\S+).*"
+ for line in splitted_config:
+ value = ""
+ if "router ospf" in line:
+ try:
+ match_ospf = re.match(REGEX, line, re.DOTALL)
+ ospf_group = match_ospf.groupdict()
+ value = ospf_group["ospf"]
+ except AttributeError:
+ value = ""
+ if value:
+ value_list.append(value)
+
+ return value_list
+
+
+def get_existing(module):
+ existing = {}
+ config = str(get_config(module))
+
+ value = get_value(config, module)
+ if value:
+ existing["ospf"] = value
+ return existing
+
+
+def state_present(module, proposed, candidate):
+ commands = ["router ospf {0}".format(proposed["ospf"])]
+ candidate.add(commands, parents=[])
+
+
+def state_absent(module, proposed, candidate):
+ commands = ["no router ospf {0}".format(proposed["ospf"])]
+ candidate.add(commands, parents=[])
+
+
+def main():
+ argument_spec = dict(
+ ospf=dict(required=True, type="str"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ result = dict(changed=False, warnings=warnings)
+
+ state = module.params["state"]
+ ospf = str(module.params["ospf"])
+
+ existing = get_existing(module)
+ proposed = dict(ospf=ospf)
+
+ if not existing:
+ existing_list = []
+ else:
+ existing_list = existing["ospf"]
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present" and ospf not in existing_list:
+ state_present(module, proposed, candidate)
+ if state == "absent" and ospf in existing_list:
+ state_absent(module, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ load_config(module, candidate)
+ result["changed"] = True
+ result["commands"] = candidate
+
+ else:
+ result["commands"] = []
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_interfaces.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_interfaces.py
new file mode 100644
index 00000000..1dc0c861
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_interfaces.py
@@ -0,0 +1,1445 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The module file for nxos_ospf_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+module: nxos_ospf_interfaces
+version_added: 1.3.0
+short_description: OSPF Interfaces Resource Module.
+description:
+- This module manages OSPF(v2/v3) configuration of interfaces on devices running Cisco NX-OS.
+author: Nilashish Chakraborty (@NilashishC)
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section "^interface").
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A list of OSPF configuration for interfaces.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name/Identifier of the interface.
+ type: str
+ required: True
+ address_family:
+ description:
+ - OSPF settings on the interfaces in address-family context.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Address Family Identifier (AFI) for OSPF settings on the interfaces.
+ type: str
+ choices: ['ipv4', 'ipv6']
+ required: True
+ processes:
+ description:
+ - Interfaces configuration for an OSPF process.
+ type: list
+ elements: dict
+ suboptions:
+ process_id:
+ description:
+ - OSPF process tag.
+ type: str
+ required: True
+ area:
+ description:
+ - Area associated with interface.
+ type: dict
+ suboptions:
+ area_id:
+ description:
+ - Area ID as a decimal or IP address format.
+ type: str
+ required: True
+ secondaries:
+ description:
+ - Do not include secondary IPv4/IPv6 addresses.
+ type: bool
+ multi_areas:
+ description:
+ - Multi-Areas associated with interface.
+ - Valid values are Area Ids as an integer or IP address.
+ type: list
+ elements: str
+ multi_areas:
+ description:
+ - Multi-Areas associated with interface (not tied to OSPF process).
+ - Valid values are Area Ids as an integer or IP address.
+ type: list
+ elements: str
+ authentication:
+ description:
+ - Authentication settings on the interface.
+ type: dict
+ suboptions:
+ key_chain:
+ description:
+ - Authentication password key-chain.
+ type: str
+ message_digest:
+ description:
+ - Use message-digest authentication.
+ type: bool
+ enable:
+ description:
+ - Enable/disable authentication on the interface.
+ type: bool
+ null_auth:
+ description:
+ - Use null(disable) authentication.
+ type: bool
+ authentication_key:
+ description:
+ - Configure the authentication key for the interface.
+ type: dict
+ suboptions:
+ encryption:
+ description:
+ - 0 Specifies an UNENCRYPTED authentication key will follow.
+ - 3 Specifies an 3DES ENCRYPTED authentication key will follow.
+ - 7 Specifies a Cisco type 7 ENCRYPTED authentication key will follow.
+ type: int
+ key:
+ description:
+ - Authentication key.
+ - Valid values are Cisco type 7 ENCRYPTED password, 3DES ENCRYPTED password
+ and UNENCRYPTED (cleartext) password based on the value of encryption key.
+ type: str
+ required: True
+ message_digest_key:
+ description:
+ - Message digest authentication password (key) settings.
+ type: dict
+ suboptions:
+ key_id:
+ description:
+ - Key ID.
+ type: int
+ required: True
+ encryption:
+ description:
+ - 0 Specifies an UNENCRYPTED ospf password (key) will follow.
+ - 3 Specifies an 3DES ENCRYPTED ospf password (key) will follow.
+ - 7 Specifies a Cisco type 7 ENCRYPTED the ospf password (key) will follow.
+ type: int
+ key:
+ description:
+ - Authentication key.
+ - Valid values are Cisco type 7 ENCRYPTED password, 3DES ENCRYPTED password
+ and UNENCRYPTED (cleartext) password based on the value of encryption key.
+ type: str
+ required: True
+ cost:
+ description:
+ - Cost associated with interface.
+ type: int
+ dead_interval:
+ description:
+ - Dead interval value (in seconds).
+ type: int
+ hello_interval:
+ description:
+ - Hello interval value (in seconds).
+ type: int
+ instance:
+ description:
+ - Instance identifier.
+ type: int
+ mtu_ignore:
+ description:
+ - Enable/disable OSPF MTU mismatch detection.
+ type: bool
+ network:
+ description:
+ - Network type.
+ type: str
+ choices: ["broadcast", "point-to-point"]
+ passive_interface:
+ description:
+ - Suppress routing updates on the interface.
+ type: bool
+ priority:
+ description:
+ - Router priority.
+ type: int
+ retransmit_interval:
+ description:
+ - Packet retransmission interval.
+ type: int
+ shutdown:
+ description:
+ - Shutdown OSPF on this interface.
+ type: bool
+ transmit_delay:
+ description:
+ - Packet transmission delay.
+ type: int
+ state:
+ description:
+ - The state the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - parsed
+ - rendered
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# interface Ethernet1/2
+# no switchport
+# interface Ethernet1/3
+# no switchport
+
+- name: Merge the provided configuration with the exisiting running configuration
+ cisco.nxos.nxos_ospf_interfaces:
+ config:
+ - name: Ethernet1/1
+ address_family:
+ - afi: ipv4
+ processes:
+ - process_id: "100"
+ area:
+ area_id: 1.1.1.1
+ secondaries: False
+ multi_areas:
+ - 11.11.11.11
+ - afi: ipv6
+ processes:
+ - process_id: "200"
+ area:
+ area_id: 2.2.2.2
+ multi_areas:
+ - 21.0.0.0
+ - process_id: "300"
+ multi_areas:
+ - 50.50.50.50
+ multi_areas:
+ - 16.10.10.10
+ - name: Ethernet1/2
+ address_family:
+ - afi: ipv4
+ authentication:
+ enable: True
+ key_chain: test-1
+ message_digest_key:
+ key_id: 10
+ encryption: 3
+ key: abc01d272be25d29
+ cost: 100
+ - afi: ipv6
+ network: broadcast
+ shutdown: True
+ - name: Ethernet1/3
+ address_family:
+ - afi: ipv4
+ authentication_key:
+ encryption: 7
+ key: 12090404011C03162E
+ state: merged
+
+# Task output
+# -------------
+# "before": [
+# {
+# "name": "Ethernet1/1"
+# },
+# {
+# "name": "Ethernet1/2"
+# },
+# {
+# "name": "Ethernet1/3"
+# },
+# ]
+#
+# "commands": [
+# "interface Ethernet1/1",
+# "ip router ospf multi-area 11.11.11.11",
+# "ip router ospf 100 area 1.1.1.1 secondaries none",
+# "ipv6 router ospfv3 multi-area 16.10.10.10",
+# "ipv6 router ospfv3 200 area 2.2.2.2",
+# "ipv6 router ospfv3 200 multi-area 21.0.0.0",
+# "ipv6 router ospfv3 300 multi-area 50.50.50.50",
+# "interface Ethernet1/2",
+# "ip ospf authentication key-chain test-1",
+# "ip ospf authentication",
+# "ip ospf message-digest-key 10 md5 3 abc01d272be25d29",
+# "ip ospf cost 100",
+# "ospfv3 network broadcast",
+# "ospfv3 shutdown",
+# "interface Ethernet1/3",
+# "ip ospf authentication-key 7 12090404011C03162E"
+# ]
+#
+# "after": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.11"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "multi_areas": [
+# "16.10.10.10"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "2.2.2.2"
+# },
+# "multi_areas": [
+# "21.0.0.0"
+# ],
+# "process_id": "200"
+# },
+# {
+# "multi_areas": [
+# "50.50.50.50"
+# ],
+# "process_id": "300"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+
+# After state:
+# -------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.11
+# ipv6 router ospfv3 200 area 2.2.2.2
+# ipv6 router ospfv3 multi-area 16.10.10.10
+# ipv6 router ospfv3 200 multi-area 21.0.0.0
+# ipv6 router ospfv3 300 multi-area 50.50.50.50
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+# ip ospf authentication-key 7 12090404011C03162E
+
+
+# Using replaced
+
+# Before state:
+# ------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.11
+# ipv6 router ospfv3 200 area 2.2.2.2
+# ipv6 router ospfv3 multi-area 16.10.10.10
+# ipv6 router ospfv3 200 multi-area 21.0.0.0
+# ipv6 router ospfv3 300 multi-area 50.50.50.50
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+# ip ospf authentication-key 7 12090404011C03162E
+
+- name: Replace OSPF configurations of listed interfaces with provided configurations
+ cisco.nxos.nxos_ospf_interfaces:
+ config:
+ - name: Ethernet1/1
+ address_family:
+ - afi: ipv4
+ processes:
+ - process_id: "100"
+ area:
+ area_id: 1.1.1.1
+ secondaries: False
+ multi_areas:
+ - 11.11.11.12
+ - name: Ethernet1/3
+ state: replaced
+
+# Task output
+# -------------
+# "before": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.11"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "multi_areas": [
+# "16.10.10.10"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "2.2.2.2"
+# },
+# "multi_areas": [
+# "21.0.0.0"
+# ],
+# "process_id": "200"
+# },
+# {
+# "multi_areas": [
+# "50.50.50.50"
+# ],
+# "process_id": "300"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+#
+# "commands": [
+# "interface Ethernet1/1",
+# "ip router ospf multi-area 11.11.11.12",
+# "no ip router ospf multi-area 11.11.11.11",
+# "no ipv6 router ospfv3 multi-area 16.10.10.10",
+# "no ipv6 router ospfv3 200 area 2.2.2.2",
+# "no ipv6 router ospfv3 200 multi-area 21.0.0.0",
+# "no ipv6 router ospfv3 300 multi-area 50.50.50.50",
+# "interface Ethernet1/3",
+# "no ip ospf authentication-key 7 12090404011C03162E"
+# ]
+#
+# "after": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.12"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "name": "Ethernet1/3"
+# },
+#
+# After state:
+# -------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.12
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+
+
+# Using overridden
+
+# Before state:
+# ------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.11
+# ipv6 router ospfv3 200 area 2.2.2.2
+# ipv6 router ospfv3 multi-area 16.10.10.10
+# ipv6 router ospfv3 200 multi-area 21.0.0.0
+# ipv6 router ospfv3 300 multi-area 50.50.50.50
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+# ip ospf authentication-key 7 12090404011C03162E
+
+- name: Overridde all OSPF interfaces configuration with provided configuration
+ cisco.nxos.nxos_ospf_interfaces:
+ config:
+ - name: Ethernet1/1
+ address_family:
+ - afi: ipv4
+ processes:
+ - process_id: "100"
+ area:
+ area_id: 1.1.1.1
+ secondaries: False
+ multi_areas:
+ - 11.11.11.12
+ state: overridden
+
+# Task output
+# -------------
+# "before": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.11"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "multi_areas": [
+# "16.10.10.10"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "2.2.2.2"
+# },
+# "multi_areas": [
+# "21.0.0.0"
+# ],
+# "process_id": "200"
+# },
+# {
+# "multi_areas": [
+# "50.50.50.50"
+# ],
+# "process_id": "300"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+#
+# "commands": [
+# "interface Ethernet1/2",
+# "no ip ospf authentication key-chain test-1",
+# "no ip ospf authentication",
+# "no ip ospf message-digest-key 10 md5 3 abc01d272be25d29",
+# "no ip ospf cost 100",
+# "no ospfv3 network broadcast",
+# "no ospfv3 shutdown",
+# "interface Ethernet1/3",
+# "no ip ospf authentication-key 7 12090404011C03162E",
+# "interface Ethernet1/1",
+# "ip router ospf multi-area 11.11.11.12",
+# "no ip router ospf multi-area 11.11.11.11",
+# "no ipv6 router ospfv3 multi-area 16.10.10.10",
+# "no ipv6 router ospfv3 200 area 2.2.2.2",
+# "no ipv6 router ospfv3 200 multi-area 21.0.0.0",
+# "no ipv6 router ospfv3 300 multi-area 50.50.50.50"
+# ]
+#
+# "after": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.12"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "name": "Ethernet1/2"
+# },
+# {
+# "name": "Ethernet1/3"
+# },
+# ]
+
+# After state:
+# -------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.12
+# interface Ethernet1/2
+# no switchport
+# interface Ethernet1/3
+# no switchport
+
+# Using deleted to delete OSPF config of a single interface
+
+# Before state:
+# ------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.11
+# ipv6 router ospfv3 200 area 2.2.2.2
+# ipv6 router ospfv3 multi-area 16.10.10.10
+# ipv6 router ospfv3 200 multi-area 21.0.0.0
+# ipv6 router ospfv3 300 multi-area 50.50.50.50
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+# ip ospf authentication-key 7 12090404011C03162E
+
+- name: Delete OSPF config from a single interface
+ cisco.nxos.nxos_ospf_interfaces:
+ config:
+ - name: Ethernet1/1
+ state: deleted
+
+# Task output
+# -------------
+# "before": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.11"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "multi_areas": [
+# "16.10.10.10"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "2.2.2.2"
+# },
+# "multi_areas": [
+# "21.0.0.0"
+# ],
+# "process_id": "200"
+# },
+# {
+# "multi_areas": [
+# "50.50.50.50"
+# ],
+# "process_id": "300"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+#
+# "commands": [
+# "interface Ethernet1/1",
+# "no ip router ospf multi-area 11.11.11.11",
+# "no ip router ospf 100 area 1.1.1.1 secondaries none",
+# "no ipv6 router ospfv3 multi-area 16.10.10.10",
+# "no ipv6 router ospfv3 200 area 2.2.2.2",
+# "no ipv6 router ospfv3 200 multi-area 21.0.0.0",
+# "no ipv6 router ospfv3 300 multi-area 50.50.50.50"
+# ]
+#
+# "before": [
+# {
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+
+# After state:
+# ------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+# ip ospf authentication-key 7 12090404011C03162E
+
+# Using deleted to delete OSPF config from all interfaces
+
+# Before state:
+# ------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.11
+# ipv6 router ospfv3 200 area 2.2.2.2
+# ipv6 router ospfv3 multi-area 16.10.10.10
+# ipv6 router ospfv3 200 multi-area 21.0.0.0
+# ipv6 router ospfv3 300 multi-area 50.50.50.50
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+# ip ospf authentication-key 7 12090404011C03162E
+
+- name: Delete OSPF config from all interfaces
+ cisco.nxos.nxos_ospf_interfaces:
+ state: deleted
+
+# Task output
+# -------------
+# "before": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.11"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "multi_areas": [
+# "16.10.10.10"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "2.2.2.2"
+# },
+# "multi_areas": [
+# "21.0.0.0"
+# ],
+# "process_id": "200"
+# },
+# {
+# "multi_areas": [
+# "50.50.50.50"
+# ],
+# "process_id": "300"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+#
+# "commands": [
+# "interface Ethernet1/1",
+# "no ip router ospf multi-area 11.11.11.11",
+# "no ip router ospf 100 area 1.1.1.1 secondaries none",
+# "no ipv6 router ospfv3 multi-area 16.10.10.10",
+# "no ipv6 router ospfv3 200 area 2.2.2.2",
+# "no ipv6 router ospfv3 200 multi-area 21.0.0.0",
+# "no ipv6 router ospfv3 300 multi-area 50.50.50.50",
+# "interface Ethernet1/2",
+# "no ip ospf authentication key-chain test-1",
+# "no ip ospf authentication",
+# "no ip ospf message-digest-key 10 md5 3 abc01d272be25d29",
+# "no ip ospf cost 100",
+# "no ospfv3 network broadcast",
+# "no ospfv3 shutdown",
+# "interface Ethernet1/3",
+# "no ip ospf authentication-key 7 12090404011C03162E"
+# ]
+#
+# "after": [
+# {
+# "name": "Ethernet1/1"
+# },
+# {
+# "name": "Ethernet1/2"
+# },
+# {
+# "name": "Ethernet1/3"
+# },
+# ]
+
+# After state:
+# ------------
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# interface Ethernet1/2
+# no switchport
+# interface Ethernet1/3
+# no switchport
+
+# Using rendered
+
+- name: Render platform specific configuration lines with state rendered (without connecting to the device)
+ cisco.nxos.nxos_ospf_interfaces:
+ config:
+ - name: Ethernet1/1
+ address_family:
+ - afi: ipv4
+ processes:
+ - process_id: "100"
+ area:
+ area_id: 1.1.1.1
+ secondaries: False
+ multi_areas:
+ - 11.11.11.11
+ - afi: ipv6
+ processes:
+ - process_id: "200"
+ area:
+ area_id: 2.2.2.2
+ multi_areas:
+ - 21.0.0.0
+ - process_id: "300"
+ multi_areas:
+ - 50.50.50.50
+ multi_areas:
+ - 16.10.10.10
+ - name: Ethernet1/2
+ address_family:
+ - afi: ipv4
+ authentication:
+ enable: True
+ key_chain: test-1
+ message_digest_key:
+ key_id: 10
+ encryption: 3
+ key: abc01d272be25d29
+ cost: 100
+ - afi: ipv6
+ network: broadcast
+ shutdown: True
+ - name: Ethernet1/3
+ address_family:
+ - afi: ipv4
+ authentication_key:
+ encryption: 7
+ key: 12090404011C03162E
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+# "rendered": [
+# "interface Ethernet1/1",
+# "ip router ospf multi-area 11.11.11.11",
+# "ip router ospf 100 area 1.1.1.1 secondaries none",
+# "ipv6 router ospfv3 multi-area 16.10.10.10",
+# "ipv6 router ospfv3 200 area 2.2.2.2",
+# "ipv6 router ospfv3 200 multi-area 21.0.0.0",
+# "ipv6 router ospfv3 300 multi-area 50.50.50.50",
+# "interface Ethernet1/2",
+# "ip ospf authentication key-chain test-1",
+# "ip ospf authentication",
+# "ip ospf message-digest-key 10 md5 3 abc01d272be25d29",
+# "ip ospf cost 100",
+# "ospfv3 network broadcast",
+# "ospfv3 shutdown",
+# "interface Ethernet1/3",
+# "ip ospf authentication-key 7 12090404011C03162E"
+# ]
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# interface Ethernet1/1
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.11
+# ipv6 router ospfv3 200 area 2.2.2.2
+# ipv6 router ospfv3 200 multi-area 21.0.0.0
+# ipv6 router ospfv3 300 multi-area 50.50.50.50
+# ipv6 router ospfv3 multi-area 16.10.10.10
+# interface Ethernet1/2
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# ip ospf authentication-key 7 12090404011C03162E
+
+- name: arse externally provided OSPF interfaces config
+ cisco.nxos.nxos_ospf_interfaces:
+ running_config: "{{ lookup('file', 'ospf_interfaces.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# "parsed": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.11"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "multi_areas": [
+# "16.10.10.10"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "2.2.2.2"
+# },
+# "multi_areas": [
+# "21.0.0.0"
+# ],
+# "process_id": "200"
+# },
+# {
+# "multi_areas": [
+# "50.50.50.50"
+# ],
+# "process_id": "300"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication_key": {
+# "encryption": 7,
+# "key": "12090404011C03162E"
+# }
+# }
+# ],
+# "name": "Ethernet1/3"
+# },
+# ]
+
+# Using gathered
+
+# On-box config
+
+# NXOS# show running-config | section ^interface
+# interface Ethernet1/1
+# no switchport
+# ip router ospf 100 area 1.1.1.1 secondaries none
+# ip router ospf multi-area 11.11.11.12
+# interface Ethernet1/2
+# no switchport
+# ip ospf authentication
+# ip ospf authentication key-chain test-1
+# ip ospf message-digest-key 10 md5 3 abc01d272be25d29
+# ip ospf cost 100
+# ospfv3 network broadcast
+# ospfv3 shutdown
+# interface Ethernet1/3
+# no switchport
+
+# Task output (redacted)
+# -----------------------
+# "gathered": [
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "multi_areas": [
+# "11.11.11.12"
+# ],
+# "processes": [
+# {
+# "area": {
+# "area_id": "1.1.1.1",
+# "secondaries": false
+# },
+# "process_id": "100"
+# }
+# ]
+# }
+# ],
+# "name": "Ethernet1/1"
+# },
+# {
+# "address_family": [
+# {
+# "afi": "ipv4",
+# "authentication": {
+# "enable": true,
+# "key_chain": "test-1"
+# },
+# "cost": 100,
+# "message_digest_key": {
+# "encryption": 3,
+# "key": "abc01d272be25d29",
+# "key_id": 10
+# }
+# },
+# {
+# "afi": "ipv6",
+# "network": "broadcast",
+# "shutdown": true
+# }
+# ],
+# "name": "Ethernet1/2"
+# },
+# {
+# "name": "Ethernet1/3"
+# },
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - interface Ethernet1/1
+ - ip router ospf multi-area 11.11.11.11
+ - ip router ospf 100 area 1.1.1.1 secondaries none
+ - no ipv6 router ospfv3 multi-area 16.10.10.10
+ - ipv6 router ospfv3 200 area 2.2.2.2
+ - ipv6 router ospfv3 200 multi-area 21.0.0.0
+ - ipv6 router ospfv3 300 multi-area 50.50.50.50
+ - interface Ethernet1/2
+ - no ip ospf authentication key-chain test-1
+ - ip ospf authentication
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.ospf_interfaces.ospf_interfaces import (
+ Ospf_interfacesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.ospf_interfaces.ospf_interfaces import (
+ Ospf_interfaces,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=Ospf_interfacesArgs.argument_spec,
+ mutually_exclusive=[["config", "running_config"]],
+ required_if=[
+ ["state", "merged", ["config"]],
+ ["state", "replaced", ["config"]],
+ ["state", "overridden", ["config"]],
+ ["state", "rendered", ["config"]],
+ ["state", "parsed", ["running_config"]],
+ ],
+ supports_check_mode=True,
+ )
+
+ result = Ospf_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_vrf.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_vrf.py
new file mode 100644
index 00000000..68729cf4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospf_vrf.py
@@ -0,0 +1,495 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_ospf_vrf
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-10-01)Manages a VRF for an OSPF router.
+description:
+- Manages a VRF for an OSPF router.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+deprecated:
+ alternative: nxos_ospfv2 and nxos_ospfv3
+ why: Updated modules released with more functionality.
+ removed_at_date: '2022-10-01'
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Value I(default) restores params default value, if any. Otherwise it removes the
+ existing param configuration.
+options:
+ vrf:
+ description:
+ - Name of the resource instance. Valid value is a string. The name 'default' is
+ a valid VRF representing the global OSPF.
+ default: default
+ type: str
+ ospf:
+ description:
+ - Name of the OSPF instance.
+ required: true
+ type: str
+ router_id:
+ description:
+ - Router Identifier (ID) of the OSPF router VRF instance.
+ type: str
+ default_metric:
+ description:
+ - Specify the default Metric value. Valid values are an integer or the keyword
+ 'default'.
+ type: str
+ log_adjacency:
+ description:
+ - Controls the level of log messages generated whenever a neighbor changes state.
+ Valid values are 'log', 'detail', and 'default'.
+ choices:
+ - log
+ - detail
+ - default
+ type: str
+ timer_throttle_lsa_start:
+ description:
+ - Specify the start interval for rate-limiting Link-State Advertisement (LSA)
+ generation. Valid values are an integer, in milliseconds, or the keyword 'default'.
+ type: str
+ timer_throttle_lsa_hold:
+ description:
+ - Specify the hold interval for rate-limiting Link-State Advertisement (LSA) generation.
+ Valid values are an integer, in milliseconds, or the keyword 'default'.
+ type: str
+ timer_throttle_lsa_max:
+ description:
+ - Specify the max interval for rate-limiting Link-State Advertisement (LSA) generation.
+ Valid values are an integer, in milliseconds, or the keyword 'default'.
+ type: str
+ timer_throttle_spf_start:
+ description:
+ - Specify initial Shortest Path First (SPF) schedule delay. Valid values are an
+ integer, in milliseconds, or the keyword 'default'.
+ type: str
+ timer_throttle_spf_hold:
+ description:
+ - Specify minimum hold time between Shortest Path First (SPF) calculations. Valid
+ values are an integer, in milliseconds, or the keyword 'default'.
+ type: str
+ timer_throttle_spf_max:
+ description:
+ - Specify the maximum wait time between Shortest Path First (SPF) calculations.
+ Valid values are an integer, in milliseconds, or the keyword 'default'.
+ type: str
+ auto_cost:
+ description:
+ - Specifies the reference bandwidth used to assign OSPF cost. Valid values are
+ an integer, in Mbps, or the keyword 'default'.
+ type: str
+ bfd:
+ description:
+ - Enables BFD on all OSPF interfaces.
+ - "Dependency: ''feature bfd''"
+ type: str
+ choices:
+ - enable
+ - disable
+ passive_interface:
+ description:
+ - Setting to C(yes) will suppress routing update on interface.
+ type: bool
+ state:
+ description:
+ - State of ospf vrf configuration.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_ospf_vrf:
+ ospf: 1
+ timer_throttle_spf_start: 50
+ timer_throttle_spf_hold: 1000
+ timer_throttle_spf_max: 2000
+ timer_throttle_lsa_start: 60
+ timer_throttle_lsa_hold: 1100
+ timer_throttle_lsa_max: 3000
+ vrf: test
+ bfd: enable
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - router ospf 1
+ - vrf test
+ - bfd
+ - timers throttle lsa 60 1100 3000
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+BOOL_PARAMS = ["passive_interface"]
+PARAM_TO_COMMAND_KEYMAP = {
+ "vrf": "vrf",
+ "router_id": "router-id",
+ "default_metric": "default-metric",
+ "log_adjacency": "log-adjacency-changes",
+ "timer_throttle_lsa_start": "timers throttle lsa",
+ "timer_throttle_lsa_max": "timers throttle lsa",
+ "timer_throttle_lsa_hold": "timers throttle lsa",
+ "timer_throttle_spf_max": "timers throttle spf",
+ "timer_throttle_spf_start": "timers throttle spf",
+ "timer_throttle_spf_hold": "timers throttle spf",
+ "auto_cost": "auto-cost reference-bandwidth",
+ "bfd": "bfd",
+ "passive_interface": "passive-interface default",
+}
+PARAM_TO_DEFAULT_KEYMAP = {
+ "timer_throttle_lsa_start": "0",
+ "timer_throttle_lsa_max": "5000",
+ "timer_throttle_lsa_hold": "5000",
+ "timer_throttle_spf_start": "200",
+ "timer_throttle_spf_max": "5000",
+ "timer_throttle_spf_hold": "1000",
+ "auto_cost": "40000",
+ "bfd": "disable",
+ "default_metric": "",
+ "passive_interface": False,
+ "router_id": "",
+ "log_adjacency": "",
+}
+
+
+def get_existing(module, args):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+ parents = ["router ospf {0}".format(module.params["ospf"])]
+
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+
+ config = netcfg.get_section(parents)
+ for arg in args:
+ if arg not in ["ospf", "vrf"]:
+ existing[arg] = PARAM_TO_DEFAULT_KEYMAP.get(arg)
+
+ if config:
+ if module.params["vrf"] == "default":
+ splitted_config = config.splitlines()
+ vrf_index = False
+ for index in range(0, len(splitted_config) - 1):
+ if "vrf" in splitted_config[index].strip():
+ vrf_index = index
+ break
+ if vrf_index:
+ config = "\n".join(splitted_config[0:vrf_index])
+
+ splitted_config = config.splitlines()
+ for line in splitted_config:
+ if "passive" in line:
+ existing["passive_interface"] = True
+ elif "router-id" in line:
+ existing["router_id"] = re.search(
+ r"router-id (\S+)", line
+ ).group(1)
+ elif "metric" in line:
+ existing["default_metric"] = re.search(
+ r"default-metric (\S+)", line
+ ).group(1)
+ elif "adjacency" in line:
+ log = re.search(
+ r"log-adjacency-changes(?: (\S+))?", line
+ ).group(1)
+ if log:
+ existing["log_adjacency"] = log
+ else:
+ existing["log_adjacency"] = "log"
+ elif "auto" in line:
+ cost = re.search(
+ r"auto-cost reference-bandwidth (\d+) (\S+)", line
+ ).group(1)
+ if "Gbps" in line:
+ cost = int(cost) * 1000
+ existing["auto_cost"] = str(cost)
+ elif "bfd" in line:
+ existing["bfd"] = "enable"
+ elif "timers throttle lsa" in line:
+ tmp = re.search(r"timers throttle lsa (\S+) (\S+) (\S+)", line)
+ existing["timer_throttle_lsa_start"] = tmp.group(1)
+ existing["timer_throttle_lsa_hold"] = tmp.group(2)
+ existing["timer_throttle_lsa_max"] = tmp.group(3)
+ elif "timers throttle spf" in line:
+ tmp = re.search(r"timers throttle spf (\S+) (\S+) (\S+)", line)
+ existing["timer_throttle_spf_start"] = tmp.group(1)
+ existing["timer_throttle_spf_hold"] = tmp.group(2)
+ existing["timer_throttle_spf_max"] = tmp.group(3)
+ existing["vrf"] = module.params["vrf"]
+ existing["ospf"] = module.params["ospf"]
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key in table:
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = table.get(key)
+ return new_dict
+
+
+def get_timer_prd(key, proposed):
+ if proposed.get(key):
+ return proposed.get(key)
+ else:
+ return PARAM_TO_DEFAULT_KEYMAP.get(key)
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, value in proposed_commands.items():
+ if key == "vrf":
+ continue
+ if value is True:
+ commands.append(key)
+
+ elif value is False:
+ if key == "passive-interface default":
+ if existing_commands.get(key):
+ commands.append("no {0}".format(key))
+ else:
+ commands.append("no {0}".format(key))
+
+ elif value == "default" or value == "":
+ if key == "log-adjacency-changes":
+ commands.append("no {0}".format(key))
+ elif existing_commands.get(key):
+ existing_value = existing_commands.get(key)
+ commands.append("no {0} {1}".format(key, existing_value))
+ else:
+ if key == "timers throttle lsa":
+ command = "{0} {1} {2} {3}".format(
+ key,
+ get_timer_prd("timer_throttle_lsa_start", proposed),
+ get_timer_prd("timer_throttle_lsa_hold", proposed),
+ get_timer_prd("timer_throttle_lsa_max", proposed),
+ )
+ elif key == "timers throttle spf":
+ command = "{0} {1} {2} {3}".format(
+ key,
+ get_timer_prd("timer_throttle_spf_start", proposed),
+ get_timer_prd("timer_throttle_spf_hold", proposed),
+ get_timer_prd("timer_throttle_spf_max", proposed),
+ )
+ elif key == "log-adjacency-changes":
+ if value == "log":
+ command = key
+ elif value == "detail":
+ command = "{0} {1}".format(key, value)
+ elif key == "auto-cost reference-bandwidth":
+ if len(value) < 5:
+ command = "{0} {1} Mbps".format(key, value)
+ else:
+ value = str(int(value) // 1000)
+ command = "{0} {1} Gbps".format(key, value)
+ elif key == "bfd":
+ command = "no bfd" if value == "disable" else "bfd"
+ else:
+ command = "{0} {1}".format(key, value.lower())
+
+ if command not in commands:
+ commands.append(command)
+
+ if commands:
+ parents = ["router ospf {0}".format(module.params["ospf"])]
+ if module.params["vrf"] != "default":
+ parents.append("vrf {0}".format(module.params["vrf"]))
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, existing, proposed, candidate):
+ commands = []
+ parents = ["router ospf {0}".format(module.params["ospf"])]
+ if module.params["vrf"] == "default":
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+ for key, value in existing_commands.items():
+ if value and key != "vrf":
+ command = None
+ if key == "passive-interface default":
+ command = "no {0}".format(key)
+ elif key == "timers throttle lsa":
+ if (
+ existing["timer_throttle_lsa_start"]
+ != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timer_throttle_lsa_start"
+ )
+ or existing["timer_throttle_lsa_hold"]
+ != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timer_throttle_lsa_hold"
+ )
+ or existing["timer_throttle_lsa_max"]
+ != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timer_throttle_lsa_max"
+ )
+ ):
+ command = "no {0} {1} {2} {3}".format(
+ key,
+ existing["timer_throttle_lsa_start"],
+ existing["timer_throttle_lsa_hold"],
+ existing["timer_throttle_lsa_max"],
+ )
+ elif key == "timers throttle spf":
+ if (
+ existing["timer_throttle_spf_start"]
+ != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timer_throttle_spf_start"
+ )
+ or existing["timer_throttle_spf_hold"]
+ != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timer_throttle_spf_hold"
+ )
+ or existing["timer_throttle_spf_max"]
+ != PARAM_TO_DEFAULT_KEYMAP.get(
+ "timer_throttle_spf_max"
+ )
+ ):
+ command = "no {0} {1} {2} {3}".format(
+ key,
+ existing["timer_throttle_spf_start"],
+ existing["timer_throttle_spf_hold"],
+ existing["timer_throttle_spf_max"],
+ )
+ elif key == "log-adjacency-changes":
+ command = "no {0}".format(key)
+ elif key == "auto-cost reference-bandwidth":
+ if value != PARAM_TO_DEFAULT_KEYMAP.get("auto_cost"):
+ command = "no {0}".format(key)
+ else:
+ command = None
+ elif key == "bfd":
+ if value == "enable":
+ command = "no bfd"
+ else:
+ existing_value = existing_commands.get(key)
+ command = "no {0} {1}".format(key, existing_value)
+
+ if command:
+ if command not in commands:
+ commands.append(command)
+ else:
+ if existing.get("vrf") and existing.get("vrf") == module.params["vrf"]:
+ commands = ["no vrf {0}".format(module.params["vrf"])]
+
+ if commands:
+ candidate.add(commands, parents=parents)
+
+
+def main():
+ argument_spec = dict(
+ vrf=dict(required=False, type="str", default="default"),
+ ospf=dict(required=True, type="str"),
+ router_id=dict(required=False, type="str"),
+ default_metric=dict(required=False, type="str"),
+ log_adjacency=dict(
+ required=False, type="str", choices=["log", "detail", "default"]
+ ),
+ timer_throttle_lsa_start=dict(required=False, type="str"),
+ timer_throttle_lsa_hold=dict(required=False, type="str"),
+ timer_throttle_lsa_max=dict(required=False, type="str"),
+ timer_throttle_spf_start=dict(required=False, type="str"),
+ timer_throttle_spf_hold=dict(required=False, type="str"),
+ timer_throttle_spf_max=dict(required=False, type="str"),
+ auto_cost=dict(required=False, type="str"),
+ bfd=dict(required=False, type="str", choices=["enable", "disable"]),
+ passive_interface=dict(required=False, type="bool"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ result = dict(changed=False, commands=[], warnings=warnings)
+
+ state = module.params["state"]
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing = get_existing(module, args)
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key != "interface":
+ if str(value).lower() == "true":
+ value = True
+ elif str(value).lower() == "false":
+ value = False
+ elif str(value).lower() == "default":
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key)
+ if value is None:
+ value = "default"
+ if existing.get(key) != value:
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ if state == "absent" and existing:
+ state_absent(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ result["commands"] = candidate
+ if not module.check_mode:
+ load_config(module, candidate)
+ result["changed"] = True
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv2.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv2.py
new file mode 100644
index 00000000..a1a547f1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv2.py
@@ -0,0 +1,1981 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_ospfv2
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_ospfv2
+short_description: OSPFv2 resource module
+description:
+- This module manages OSPFv2 configuration on devices running Cisco NX-OS.
+version_added: 1.0.0
+notes:
+- Tested against NX-OS 7.0(3)I5(1).
+- This module works with connection C(network_cli) and C(httpapi).
+author: Nilashish Chakraborty (@NilashishC)
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section "^router ospf .*").
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A list of OSPF process configuration.
+ type: dict
+ suboptions:
+ processes:
+ description:
+ - A list of OSPF instances' configurations.
+ type: list
+ elements: dict
+ suboptions:
+ areas:
+ description:
+ - Configure properties of OSPF Areas.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description:
+ - The Area ID as an integer or IP Address.
+ type: str
+ required: true
+ authentication:
+ description:
+ - Authentication settings for the Area.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set authentication for the area.
+ type: bool
+ message_digest:
+ description:
+ - Use message-digest authentication.
+ type: bool
+ default_cost:
+ description:
+ - Specify the default cost for default summary LSA.
+ type: int
+ filter_list:
+ description:
+ - Filter prefixes between OSPF areas.
+ type: list
+ elements: dict
+ suboptions:
+ route_map:
+ description:
+ - The Route-map name.
+ type: str
+ required: true
+ direction:
+ description:
+ - The direction to apply the route map.
+ type: str
+ choices: [in, out]
+ required: true
+ nssa:
+ description:
+ - NSSA settings for the area.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure area as NSSA.
+ type: bool
+ default_information_originate:
+ description:
+ - Originate Type-7 default LSA into NSSA area.
+ type: bool
+ no_redistribution:
+ description:
+ - Do not send redistributed LSAs into NSSA area.
+ type: bool
+ no_summary:
+ description:
+ - Do not send summary LSAs into NSSA area.
+ type: bool
+ translate:
+ description:
+ - Translate LSA.
+ type: dict
+ suboptions:
+ type7:
+ description:
+ - Translate from Type 7 to Type 5.
+ type: dict
+ suboptions:
+ always:
+ description:
+ - Always translate LSAs
+ type: bool
+ never:
+ description:
+ - Never translate LSAs
+ type: bool
+ supress_fa:
+ description:
+ - Suppress forwarding address in translated LSAs.
+ type: bool
+ ranges:
+ description:
+ - Configure an address range for the area.
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - IP in Prefix format (x.x.x.x/len)
+ type: str
+ required: true
+ cost:
+ description:
+ - Cost to use for the range.
+ type: int
+ not_advertise:
+ description:
+ - Suppress advertising the specified range.
+ type: bool
+ stub:
+ description:
+ - Settings for configuring the area as a stub.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure the area as a stub.
+ type: bool
+ no_summary:
+ description:
+ - Prevent ABR from sending summary LSAs into stub area.
+ type: bool
+ auto_cost:
+ description:
+ - Calculate OSPF cost according to bandwidth.
+ type: dict
+ suboptions:
+ reference_bandwidth:
+ description:
+ - Reference bandwidth used to assign OSPF cost.
+ type: int
+ required: true
+ unit:
+ description:
+ - Specify in which unit the reference bandwidth is specified.
+ type: str
+ required: true
+ choices: [Gbps, Mbps]
+ bfd:
+ description:
+ - Enable BFD on all OSPF interfaces.
+ type: bool
+ default_information:
+ description:
+ - Control distribution of default routes.
+ type: dict
+ suboptions:
+ originate:
+ description:
+ - Distribute a default route.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable distribution of default route.
+ type: bool
+ always:
+ description:
+ - Always advertise a default route.
+ type: bool
+ route_map:
+ description:
+ - Policy to control distribution of default routes
+ type: str
+ default_metric:
+ description:
+ - Specify default metric for redistributed routes.
+ type: int
+ distance:
+ description:
+ - Configure the OSPF administrative distance.
+ type: int
+ flush_routes:
+ description:
+ - Flush routes on a non-graceful controlled restart.
+ type: bool
+ graceful_restart:
+ description:
+ - Configure graceful restart.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable graceful-restart.
+ type: bool
+ grace_period:
+ description:
+ - Configure maximum interval to restart gracefully.
+ type: int
+ helper_disable:
+ description:
+ - Enable/Disable helper mode.
+ type: bool
+ isolate:
+ description:
+ - Isolate this router from OSPF perspective.
+ type: bool
+ log_adjacency_changes:
+ description:
+ - Log changes in adjacency state.
+ type: dict
+ suboptions:
+ log:
+ description:
+ - Enable/disable logging changes in adjacency state.
+ type: bool
+ detail:
+ description:
+ - Notify all state changes.
+ type: bool
+ max_lsa:
+ description:
+ - Feature to limit the number of non-self-originated LSAs.
+ type: dict
+ suboptions:
+ max_non_self_generated_lsa:
+ description:
+ - Set the maximum number of non self-generated LSAs.
+ type: int
+ required: true
+ threshold:
+ description:
+ - Threshold value (%) at which to generate a warning message.
+ type: int
+ ignore_count:
+ description:
+ - Set count on how many times adjacencies can be suppressed.
+ type: int
+ ignore_time:
+ description:
+ - Set time during which all adjacencies are suppressed.
+ type: int
+ reset_time:
+ description:
+ - Set number of minutes after which ignore-count is reset to zero.
+ type: int
+ warning_only:
+ description:
+ - Log a warning message when limit is exceeded.
+ type: bool
+ max_metric:
+ description:
+ - Maximize the cost metric.
+ type: dict
+ suboptions:
+ router_lsa:
+ description:
+ - Router LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set router-lsa attribute.
+ type: bool
+ external_lsa:
+ description:
+ - External LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set external-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Set max metric value for external LSAs.
+ type: int
+ include_stub:
+ description:
+ - Advertise Max metric for Stub links as well.
+ type: bool
+ on_startup:
+ description:
+ - Effective only at startup.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set on-startup attribute.
+ type: bool
+ wait_period:
+ description:
+ - Wait period in seconds after startup.
+ type: int
+ wait_for_bgp_asn:
+ description:
+ - ASN of BGP to wait for.
+ type: int
+ summary_lsa:
+ description:
+ - Summary LSAs configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set summary-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Max metric value for summary LSAs.
+ type: int
+ maximum_paths:
+ description:
+ - Maximum paths per destination.
+ type: int
+ mpls:
+ description:
+ - OSPF MPLS configuration settings.
+ type: dict
+ suboptions:
+ traffic_eng:
+ description:
+ - OSPF MPLS Traffic Engineering commands.
+ type: dict
+ suboptions:
+ areas:
+ description:
+ - List of Area IDs.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description:
+ - Area Id as an integer or ip address.
+ type: str
+ multicast_intact:
+ description:
+ - MPLS TE multicast support.
+ type: bool
+ router_id:
+ description:
+ - Router ID associated with TE.
+ type: str
+ name_lookup:
+ description:
+ - Display OSPF router ids as DNS names.
+ type: bool
+ passive_interface:
+ description:
+ - Suppress routing updates on the interface.
+ type: dict
+ suboptions:
+ default:
+ description:
+ - Interfaces passive by default.
+ type: bool
+ process_id:
+ description:
+ - The OSPF process tag.
+ type: str
+ required: true
+ redistribute:
+ description:
+ - Redistribute information from another routing protocol.
+ type: list
+ elements: dict
+ suboptions:
+ protocol:
+ description:
+ - The name of the protocol.
+ type: str
+ choices: [bgp, direct, eigrp, isis, lisp, ospf, rip, static]
+ required: true
+ id:
+ description:
+ - The identifier for the protocol specified.
+ type: str
+ route_map:
+ description:
+ - The route map policy to constrain redistribution.
+ type: str
+ required: true
+ rfc1583compatibility:
+ description:
+ - Configure 1583 compatibility for external path preferences.
+ type: bool
+ router_id:
+ description:
+ - Set OSPF process router-id.
+ type: str
+ shutdown:
+ description:
+ - Shutdown the OSPF protocol instance.
+ type: bool
+ summary_address:
+ description:
+ - Configure route summarization for redistribution.
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - IP prefix in format x.x.x.x/ml.
+ type: str
+ required: true
+ not_advertise:
+ description:
+ - Supress advertising the specified summary.
+ type: bool
+ tag:
+ description:
+ - A 32-bit tag value.
+ type: int
+ table_map:
+ description:
+ - Policy for filtering/modifying OSPF routes before sending them to RIB.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - The Route Map name.
+ type: str
+ required: true
+ filter:
+ description:
+ - Block the OSPF routes from being sent to RIB.
+ type: bool
+ timers:
+ description:
+ - Configure timer related constants.
+ type: dict
+ suboptions:
+ lsa_arrival:
+ description:
+ - Mimimum interval between arrival of a LSA.
+ type: int
+ lsa_group_pacing:
+ description:
+ - LSA group refresh/maxage interval.
+ type: int
+ throttle:
+ description:
+ - Configure throttle related constants.
+ type: dict
+ suboptions:
+ lsa:
+ description:
+ - Set rate-limiting for LSA generation.
+ type: dict
+ suboptions:
+ start_interval:
+ description:
+ - The start interval.
+ type: int
+ hold_interval:
+ description:
+ - The hold interval.
+ type: int
+ max_interval:
+ description:
+ - The max interval.
+ type: int
+ spf:
+ description:
+ - Set OSPF SPF timers.
+ type: dict
+ suboptions:
+ initial_spf_delay:
+ description:
+ - Initial SPF schedule delay in milliseconds.
+ type: int
+ min_hold_time:
+ description:
+ - Minimum hold time between SPF calculations.
+ type: int
+ max_wait_time:
+ description:
+ - Maximum wait time between SPF calculations.
+ type: int
+ vrfs:
+ description:
+ - Configure VRF specific OSPF settings.
+ type: list
+ elements: dict
+ suboptions:
+ areas:
+ description:
+ - Configure properties of OSPF Areas.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description:
+ - The Area ID as an integer or IP Address.
+ type: str
+ required: true
+ authentication:
+ description:
+ - Authentication settings for the Area.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set authentication for the area.
+ type: bool
+ message_digest:
+ description:
+ - Use message-digest authentication.
+ type: bool
+ default_cost:
+ description:
+ - Specify the default cost for default summary LSA.
+ type: int
+ filter_list:
+ description:
+ - Filter prefixes between OSPF areas.
+ type: list
+ elements: dict
+ suboptions:
+ route_map:
+ description:
+ - The Route-map name.
+ type: str
+ required: true
+ direction:
+ description:
+ - The direction to apply the route map.
+ type: str
+ choices: [in, out]
+ required: true
+ nssa:
+ description:
+ - NSSA settings for the area.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure area as NSSA.
+ type: bool
+ default_information_originate:
+ description:
+ - Originate Type-7 default LSA into NSSA area.
+ type: bool
+ no_redistribution:
+ description:
+ - Do not send redistributed LSAs into NSSA area.
+ type: bool
+ no_summary:
+ description:
+ - Do not send summary LSAs into NSSA area.
+ type: bool
+ translate:
+ description:
+ - Translate LSA.
+ type: dict
+ suboptions:
+ type7:
+ description:
+ - Translate from Type 7 to Type 5.
+ type: dict
+ suboptions:
+ always:
+ description:
+ - Always translate LSAs
+ type: bool
+ never:
+ description:
+ - Never translate LSAs
+ type: bool
+ supress_fa:
+ description:
+ - Suppress forwarding address in translated LSAs.
+ type: bool
+ ranges:
+ description:
+ - Configure an address range for the area.
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - IP in Prefix format (x.x.x.x/len)
+ type: str
+ required: true
+ cost:
+ description:
+ - Cost to use for the range.
+ type: int
+ not_advertise:
+ description:
+ - Suppress advertising the specified range.
+ type: bool
+ stub:
+ description:
+ - Settings for configuring the area as a stub.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure the area as a stub.
+ type: bool
+ no_summary:
+ description:
+ - Prevent ABR from sending summary LSAs into stub area.
+ type: bool
+ auto_cost:
+ description:
+ - Calculate OSPF cost according to bandwidth.
+ type: dict
+ suboptions:
+ reference_bandwidth:
+ description:
+ - Reference bandwidth used to assign OSPF cost.
+ type: int
+ required: true
+ unit:
+ description:
+ - Specify in which unit the reference bandwidth is specified.
+ type: str
+ required: True
+ choices: [Gbps, Mbps]
+ bfd:
+ description:
+ - Enable BFD on all OSPF interfaces.
+ type: bool
+ default_information:
+ description:
+ - Control distribution of default routes.
+ type: dict
+ suboptions:
+ originate:
+ description:
+ - Distribute a default route.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable distribution of default route.
+ type: bool
+ always:
+ description:
+ - Always advertise a default route.
+ type: bool
+ route_map:
+ description:
+ - Policy to control distribution of default routes
+ type: str
+ default_metric:
+ description:
+ - Specify default metric for redistributed routes.
+ type: int
+ distance:
+ description:
+ - Configure the OSPF administrative distance.
+ type: int
+ down_bit_ignore:
+ description:
+ - Configure a PE router to ignore the DN bit for network summary,
+ external and NSSA external LSA.
+ type: bool
+ capability:
+ description:
+ - OSPF capability settings.
+ type: dict
+ suboptions:
+ vrf_lite:
+ description:
+ - Enable VRF-lite capability settings.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable VRF-lite support.
+ type: bool
+ evpn:
+ description:
+ - Ethernet VPN.
+ type: bool
+ graceful_restart:
+ description:
+ - Configure graceful restart.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable graceful-restart.
+ type: bool
+ grace_period:
+ description:
+ - Configure maximum interval to restart gracefully.
+ type: int
+ helper_disable:
+ description:
+ - Enable/Disable helper mode.
+ type: bool
+ log_adjacency_changes:
+ description:
+ - Log changes in adjacency state.
+ type: dict
+ suboptions:
+ log:
+ description:
+ - Enable/disable logging changes in adjacency state.
+ type: bool
+ detail:
+ description:
+ - Notify all state changes.
+ type: bool
+ max_lsa:
+ description:
+ - Feature to limit the number of non-self-originated LSAs.
+ type: dict
+ suboptions:
+ max_non_self_generated_lsa:
+ description:
+ - Set the maximum number of non self-generated LSAs.
+ type: int
+ required: true
+ threshold:
+ description:
+ - Threshold value (%) at which to generate a warning message.
+ type: int
+ ignore_count:
+ description:
+ - Set count on how many times adjacencies can be suppressed.
+ type: int
+ ignore_time:
+ description:
+ - Set time during which all adjacencies are suppressed.
+ type: int
+ reset_time:
+ description:
+ - Set number of minutes after which ignore-count is reset to zero.
+ type: int
+ warning_only:
+ description:
+ - Log a warning message when limit is exceeded.
+ type: bool
+ max_metric:
+ description:
+ - Maximize the cost metric.
+ type: dict
+ suboptions:
+ router_lsa:
+ description:
+ - Router LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set router-lsa attribute.
+ type: bool
+ external_lsa:
+ description:
+ - External LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set external-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Set max metric value for external LSAs.
+ type: int
+ include_stub:
+ description:
+ - Advertise Max metric for Stub links as well.
+ type: bool
+ on_startup:
+ description:
+ - Effective only at startup.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set on-startup attribute.
+ type: bool
+ wait_period:
+ description:
+ - Wait period in seconds after startup.
+ type: int
+ wait_for_bgp_asn:
+ description:
+ - ASN of BGP to wait for.
+ type: int
+ summary_lsa:
+ description:
+ - Summary LSAs configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set summary-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Max metric value for summary LSAs.
+ type: int
+ maximum_paths:
+ description:
+ - Maximum paths per destination.
+ type: int
+ name_lookup:
+ description:
+ - Display OSPF router ids as DNS names.
+ type: bool
+ passive_interface:
+ description:
+ - Suppress routing updates on the interface.
+ type: dict
+ suboptions:
+ default:
+ description:
+ - Interfaces passive by default.
+ type: bool
+ redistribute:
+ description:
+ - Redistribute information from another routing protocol.
+ type: list
+ elements: dict
+ suboptions:
+ protocol:
+ description:
+ - The name of the protocol.
+ type: str
+ choices: [bgp, direct, eigrp, isis, lisp, ospf, rip, static]
+ required: true
+ id:
+ description:
+ - The identifier for the protocol specified.
+ type: str
+ route_map:
+ description:
+ - The route map policy to constrain redistribution.
+ type: str
+ required: true
+ rfc1583compatibility:
+ description:
+ - Configure 1583 compatibility for external path preferences.
+ type: bool
+ router_id:
+ description:
+ - Set OSPF process router-id.
+ type: str
+ shutdown:
+ description:
+ - Shutdown the OSPF protocol instance.
+ type: bool
+ summary_address:
+ description:
+ - Configure route summarization for redistribution.
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - IP prefix in format x.x.x.x/ml.
+ type: str
+ required: true
+ not_advertise:
+ description:
+ - Supress advertising the specified summary.
+ type: bool
+ tag:
+ description:
+ - A 32-bit tag value.
+ type: int
+ table_map:
+ description:
+ - Policy for filtering/modifying OSPF routes before sending them to
+ RIB.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - The Route Map name.
+ type: str
+ required: true
+ filter:
+ description:
+ - Block the OSPF routes from being sent to RIB.
+ type: bool
+ timers:
+ description:
+ - Configure timer related constants.
+ type: dict
+ suboptions:
+ lsa_arrival:
+ description:
+ - Mimimum interval between arrival of a LSA.
+ type: int
+ lsa_group_pacing:
+ description:
+ - LSA group refresh/maxage interval.
+ type: int
+ throttle:
+ description:
+ - Configure throttle related constants.
+ type: dict
+ suboptions:
+ lsa:
+ description:
+ - Set rate-limiting for LSA generation.
+ type: dict
+ suboptions:
+ start_interval:
+ description:
+ - The start interval.
+ type: int
+ hold_interval:
+ description:
+ - The hold interval.
+ type: int
+ max_interval:
+ description:
+ - The max interval.
+ type: int
+ spf:
+ description:
+ - Set OSPF SPF timers.
+ type: dict
+ suboptions:
+ initial_spf_delay:
+ description:
+ - Initial SPF schedule delay in milliseconds.
+ type: int
+ min_hold_time:
+ description:
+ - Minimum hold time between SPF calculations.
+ type: int
+ max_wait_time:
+ description:
+ - Maximum wait time between SPF calculations.
+ type: int
+ vrf:
+ description:
+ - Name/Identifier of the VRF.
+ type: str
+ required: true
+ state:
+ description:
+ - The state the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - parsed
+ - rendered
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# nxos-9k-rdo#
+
+- name: Merge the provided configuration with the exisiting running configuration
+ cisco.nxos.nxos_ospfv2:
+ config:
+ processes:
+ - process_id: 100
+ router_id: 203.0.113.20
+ - process_id: 102
+ router_id: 198.51.100.1
+ areas:
+ - area_id: 0.0.0.100
+ filter_list:
+ - route_map: rmap_1
+ direction: in
+ - route_map: rmap_2
+ direction: out
+ ranges:
+ - prefix: 198.51.100.64/27
+ not_advertise: true
+ - prefix: 198.51.100.96/27
+ cost: 120
+ - area_id: 0.0.0.101
+ authentication:
+ message_digest: true
+ redistribute:
+ - protocol: eigrp
+ id: 120
+ route_map: rmap_1
+ - protocol: direct
+ route_map: ospf102-direct-connect
+ vrfs:
+ - vrf: zone1
+ router_id: 198.51.100.129
+ redistribute:
+ - protocol: static
+ route_map: zone1-static-connect
+ summary_address:
+ - prefix: 198.51.100.128/27
+ tag: 121
+ - prefix: 198.51.100.160/27
+ areas:
+ - area_id: 0.0.0.102
+ nssa:
+ default_information_originate: true
+ no_summary: true
+ - area_id: 0.0.0.103
+ nssa:
+ no_summary: true
+ translate:
+ type7:
+ always: true
+ - vrf: zone2
+ auto_cost:
+ reference_bandwidth: 45
+ unit: Gbps
+ state: merged
+
+# Task output
+# -------------
+# before: {}
+#
+# commands:
+# - router ospf 102
+# - router-id 198.51.100.1
+# - redistribute eigrp 120 route-map rmap_1
+# - redistribute direct route-map ospf102-direct-connect
+# - area 0.0.0.100 filter-list route-map rmap_1 in
+# - area 0.0.0.100 filter-list route-map rmap_2 out
+# - area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# - area 0.0.0.100 range 198.51.100.96/27 cost 120
+# - area 0.0.0.101 authentication message-digest
+# - vrf zone1
+# - router-id 198.51.100.129
+# - summary-address 198.51.100.128/27 tag 121
+# - summary-address 198.51.100.160/27
+# - redistribute static route-map zone1-static-connect
+# - area 0.0.0.102 nssa no-summary default-information-originate
+# - area 0.0.0.103 nssa no-summary
+# - area 0.0.0.103 nssa translate type7 always
+# - vrf zone2
+# - auto-cost reference-bandwidth 45 Gbps
+# - router ospf 100
+# - router-id 203.0.113.20
+#
+# after:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# - cost: 120
+# prefix: 198.51.100.96/27
+# - area_id: 0.0.0.101
+# authentication:
+# message_digest: true
+# process_id: "102"
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# redistribute:
+# - protocol: static
+# route_map: zone1-static-connect
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+# router ospf 102
+# router-id 198.51.100.1
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# area 0.0.0.100 range 198.51.100.96/27 cost 120
+# area 0.0.0.101 authentication message-digest
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# redistribute static route-map zone1-static-connect
+# summary-address 198.51.100.128/27 tag 121
+# summary-address 198.51.100.160/27
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+# Using replaced
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+# router ospf 102
+# router-id 198.51.100.1
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# area 0.0.0.100 range 198.51.100.96/27 cost 120
+# area 0.0.0.101 authentication message-digest
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# redistribute static route-map zone1-static-connect
+# summary-address 198.51.100.128/27 tag 121
+# summary-address 198.51.100.160/27
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Replace device configurations of listed OSPF processes with provided configurations
+ cisco.nxos.nxos_ospfv2:
+ config:
+ processes:
+ - process_id: 102
+ router_id: 198.51.100.1
+ areas:
+ - area_id: 0.0.0.100
+ filter_list:
+ - route_map: rmap_8
+ direction: in
+ ranges:
+ - prefix: 198.51.100.64/27
+ not_advertise: true
+ - area_id: 0.0.0.101
+ stub:
+ no_summary: true
+ redistribute:
+ - protocol: eigrp
+ id: 130
+ route_map: rmap_1
+ - protocol: direct
+ route_map: ospf102-direct-connect
+ vrfs:
+ - vrf: zone1
+ router_id: 198.51.100.129
+ redistribute:
+ - protocol: bgp
+ id: 65563
+ route_map: zone1-bgp-connect
+ areas:
+ - area_id: 0.0.0.102
+ nssa:
+ default_information_originate: true
+ no_summary: true
+ state: replaced
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# - cost: 120
+# prefix: 198.51.100.96/27
+# - area_id: 0.0.0.101
+# authentication:
+# message_digest: true
+# process_id: "102"
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# redistribute:
+# - protocol: static
+# route_map: zone1-static-connect
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - router ospf 102
+# - redistribute eigrp 130 route-map rmap_1
+# - no redistribute eigrp 120 route-map rmap_1
+# - area 0.0.0.100 filter-list route-map rmap_8 in
+# - no area 0.0.0.100 filter-list route-map rmap_2 out
+# - no area 0.0.0.100 range 198.51.100.96/27
+# - no area 0.0.0.101 authentication
+# - area 0.0.0.101 stub no-summary
+# - vrf zone1
+# - no summary-address 198.51.100.128/27 tag 121
+# - no summary-address 198.51.100.160/27
+# - redistribute bgp 65563 route-map zone1-bgp-connect
+# - no redistribute static route-map zone1-static-connect
+# - no area 0.0.0.103 nssa
+# - no area 0.0.0.103 nssa translate type7 always
+# - no vrf zone2
+#
+# after:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - areas:
+# - area_id: 0.0.0.101
+# stub:
+# no_summary: true
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: in
+# route_map: rmap_8
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# process_id: "102"
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "130"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# redistribute:
+# - id: "65563"
+# protocol: bgp
+# route_map: zone1-bgp-connect
+# router_id: 198.51.100.129
+# vrf: zone1
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+# router ospf 102
+# router-id 198.51.100.1
+# area 0.0.0.101 stub no-summary
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 130 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_8 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# redistribute bgp 65563 route-map zone1-bgp-connect
+
+# Using overridden
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+# router ospf 102
+# router-id 198.51.100.1
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# area 0.0.0.100 range 198.51.100.96/27 cost 120
+# area 0.0.0.101 authentication message-digest
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# redistribute static route-map zone1-static-connect
+# summary-address 198.51.100.128/27 tag 121
+# summary-address 198.51.100.160/27
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Overridde all OSPF configuration with provided configuration
+ cisco.nxos.nxos_ospfv2:
+ config:
+ processes:
+ - process_id: 104
+ router_id: 203.0.113.20
+ - process_id: 102
+ router_id: 198.51.100.1
+ shutdown: true
+ state: overridden
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# - cost: 120
+# prefix: 198.51.100.96/27
+# - area_id: 0.0.0.101
+# authentication:
+# message_digest: true
+# process_id: "102"
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# redistribute:
+# - protocol: static
+# route_map: zone1-static-connect
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - no router ospf 100
+# - router ospf 104
+# - router-id 203.0.113.20
+# - router ospf 102
+# - shutdown
+# - no redistribute direct route-map ospf102-direct-connect
+# - no redistribute eigrp 120 route-map rmap_1
+# - no area 0.0.0.100 filter-list route-map rmap_2 out
+# - no area 0.0.0.100 filter-list route-map rmap_1 in
+# - no area 0.0.0.100 range 198.51.100.64/27
+# - no area 0.0.0.100 range 198.51.100.96/27
+# - no area 0.0.0.101 authentication
+# - no vrf zone1
+# - no vrf zone2
+#
+# after:
+# processes:
+# - process_id: "102"
+# router_id: 198.51.100.1
+# shutdown: true
+# - process_id: "104"
+# router_id: 203.0.113.20
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 102
+# router-id 198.51.100.1
+# shutdown
+# router ospf 104
+# router-id 203.0.113.20
+
+# Using deleted to delete a single OSPF process
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+# router ospf 102
+# router-id 198.51.100.1
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# area 0.0.0.100 range 198.51.100.96/27 cost 120
+# area 0.0.0.101 authentication message-digest
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# redistribute static route-map zone1-static-connect
+# summary-address 198.51.100.128/27 tag 121
+# summary-address 198.51.100.160/27
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Delete a single OSPF process
+ cisco.nxos.nxos_ospfv2:
+ config:
+ processes:
+ - process_id: 102
+ state: deleted
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# - cost: 120
+# prefix: 198.51.100.96/27
+# - area_id: 0.0.0.101
+# authentication:
+# message_digest: true
+# process_id: "102"
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# redistribute:
+# - protocol: static
+# route_map: zone1-static-connect
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - no router ospf 102
+#
+# after:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+
+# Using deleted all OSPF processes from the device
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospf 100
+# router-id 203.0.113.20
+# router ospf 102
+# router-id 198.51.100.1
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# area 0.0.0.100 range 198.51.100.96/27 cost 120
+# area 0.0.0.101 authentication message-digest
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# redistribute static route-map zone1-static-connect
+# summary-address 198.51.100.128/27 tag 121
+# summary-address 198.51.100.160/27
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Delete all OSPF processes from the device
+ cisco.nxos.nxos_ospfv2:
+ state: deleted
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# - cost: 120
+# prefix: 198.51.100.96/27
+# - area_id: 0.0.0.101
+# authentication:
+# message_digest: true
+# process_id: "102"
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# redistribute:
+# - protocol: static
+# route_map: zone1-static-connect
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - no router ospf 100
+# - no router ospf 102
+#
+# after: {}
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# nxos-9k-rdo#
+
+# Using rendered
+
+- name: Render platform specific configuration lines (without connecting to the device)
+ cisco.nxos.nxos_ospfv2:
+ config:
+ processes:
+ - process_id: 100
+ router_id: 203.0.113.20
+ - process_id: 102
+ router_id: 198.51.100.1
+ areas:
+ - area_id: 0.0.0.100
+ filter_list:
+ - route_map: rmap_1
+ direction: in
+ - route_map: rmap_2
+ direction: out
+ ranges:
+ - prefix: 198.51.100.64/27
+ not_advertise: true
+ - prefix: 198.51.100.96/27
+ cost: 120
+ - area_id: 0.0.0.101
+ authentication:
+ message_digest: true
+ redistribute:
+ - protocol: eigrp
+ id: 120
+ route_map: rmap_1
+ - protocol: direct
+ route_map: ospf102-direct-connect
+ vrfs:
+ - vrf: zone1
+ router_id: 198.51.100.129
+ redistribute:
+ - protocol: static
+ route_map: zone1-static-connect
+ summary_address:
+ - prefix: 198.51.100.128/27
+ tag: 121
+ - prefix: 198.51.100.160/27
+ areas:
+ - area_id: 0.0.0.102
+ nssa:
+ default_information_originate: true
+ no_summary: true
+ - area_id: 0.0.0.103
+ nssa:
+ no_summary: true
+ translate:
+ type7:
+ always: true
+ - vrf: zone2
+ auto_cost:
+ reference_bandwidth: 45
+ unit: Gbps
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+# rendered:
+# - router ospf 100
+# - router-id 203.0.113.20
+# - router ospf 102
+# - router-id 198.51.100.1
+# - redistribute eigrp 120 route-map rmap_1
+# - redistribute direct route-map ospf102-direct-connect
+# - area 0.0.0.100 filter-list route-map rmap_1 in
+# - area 0.0.0.100 filter-list route-map rmap_2 out
+# - area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# - area 0.0.0.100 range 198.51.100.96/27 cost 120
+# - area 0.0.0.101 authentication message-digest
+# - vrf zone1
+# - router-id 198.51.100.129
+# - summary-address 198.51.100.128/27 tag 121
+# - summary-address 198.51.100.160/27
+# - redistribute static route-map zone1-static-connect
+# - area 0.0.0.102 nssa no-summary default-information-originate
+# - area 0.0.0.103 nssa no-summary
+# - area 0.0.0.103 nssa translate type7 always
+# - vrf zone2
+# - auto-cost reference-bandwidth 45 Gbps
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# router ospf 100
+# router-id 192.0.100.1
+# area 0.0.0.101 nssa no-summary no-redistribution
+# area 0.0.0.102 stub no-summary
+# redistribute direct route-map ospf-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 192.0.2.0/24 not-advertise
+# area 0.0.0.100 range 192.0.3.0/24 cost 120
+# area 0.0.0.100 authentication message-digest
+# vrf zone1
+# router-id 192.0.100.2
+# area 0.0.100.1 nssa no-summary no-redistribution
+# redistribute static route-map zone1-direct-connect
+# summary-address 10.0.0.0/24 tag 120
+# summary-address 11.0.0.0/24 not-advertise
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+# down-bit-ignore
+# capability vrf-lite evpn
+# shutdown
+# router ospf 102
+# router-id 198.54.100.1
+# shutdown
+# vrf zone2
+# summary-address 192.0.8.0/24 tag 120
+# vrf zone4
+# shutdown
+
+- name: Parse externally provided OSPFv2 config
+ cisco.nxos.nxos_ospfv2:
+ running_config: "{{ lookup('file', 'ospfv2.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# parsed:
+# processes:
+# - process_id: "100"
+# areas:
+# - area_id: 0.0.0.101
+# nssa:
+# no_redistribution: true
+# no_summary: true
+# - area_id: 0.0.0.102
+# stub:
+# no_summary: true
+# - area_id: 0.0.0.100
+# authentication:
+# message_digest: true
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 192.0.2.0/24
+# - cost: 120
+# prefix: 192.0.3.0/24
+# redistribute:
+# - protocol: direct
+# route_map: ospf-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 192.0.100.1
+# vrfs:
+# - vrf: zone1
+# areas:
+# - area_id: 0.0.100.1
+# nssa:
+# no_redistribution: true
+# no_summary: true
+# redistribute:
+# - protocol: static
+# route_map: zone1-direct-connect
+# router_id: 192.0.100.2
+# summary_address:
+# - prefix: 10.0.0.0/24
+# tag: 120
+# - not_advertise: true
+# prefix: 11.0.0.0/24
+# - vrf: zone2
+# auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# capability:
+# vrf_lite:
+# evpn: true
+# down_bit_ignore: true
+# shutdown: true
+# - process_id: "102"
+# router_id: 198.54.100.1
+# shutdown: true
+# vrfs:
+# - vrf: zone2
+# summary_address:
+# - prefix: 192.0.8.0/24
+# tag: 120
+# - vrf: zone4
+# shutdown: true
+
+# Using gathered
+
+- name: Gather OSPFv2 facts using gathered
+ cisco.nxos.nxos_ospfv2:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# processes:
+# - process_id: "102"
+# areas:
+# - area_id: 0.0.0.101
+# stub:
+# no_summary: true
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: in
+# route_map: rmap_8
+# ranges:
+# - not_advertise: true
+# prefix: 198.51.100.64/27
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "130"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 198.51.100.1
+# vrfs:
+# - vrf: zone1
+# areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# redistribute:
+# - id: "65563"
+# protocol: bgp
+# route_map: zone1-bgp-connect
+# router_id: 198.51.100.129
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - "router ospf 102"
+ - "router-id 198.54.100.1"
+ - "router ospf 100"
+ - "router-id 192.0.100.1"
+ - "redistribute eigrp 120 route-map rmap_1"
+ - "redistribute direct route-map ospf-direct-connect"
+ - "area 0.0.0.100 filter-list route-map rmap_1 in"
+ - "area 0.0.0.100 filter-list route-map rmap_2 out"
+ - "area 0.0.0.100 range 192.0.2.0/24 not-advertise"
+ - "area 0.0.0.100 range 192.0.3.0/24 cost 120"
+ - "vrf zone1"
+ - "router-id 192.0.100.2"
+ - "summary-address 10.0.0.0/24 tag 121"
+ - "summary-address 11.0.0.0/24"
+ - "redistribute static route-map zone1-direct-connect"
+ - "vrf zone2"
+ - "auto-cost reference-bandwidth 45 Gbps"
+ - "capability vrf-lite evpn"
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.ospfv2.ospfv2 import (
+ Ospfv2Args,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.ospfv2.ospfv2 import (
+ Ospfv2,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ required_if = [
+ ("state", "merged", ("config",)),
+ ("state", "replaced", ("config",)),
+ ("state", "overridden", ("config",)),
+ ("state", "rendered", ("config",)),
+ ("state", "parsed", ("running_config",)),
+ ]
+ module = AnsibleModule(
+ argument_spec=Ospfv2Args.argument_spec,
+ required_if=required_if,
+ supports_check_mode=True,
+ )
+
+ result = Ospfv2(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv3.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv3.py
new file mode 100644
index 00000000..2783a6cf
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ospfv3.py
@@ -0,0 +1,1699 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_ospfv3
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+module: nxos_ospfv3
+short_description: OSPFv3 resource module
+description:
+- This module manages OSPFv3 configuration on devices running Cisco NX-OS.
+version_added: 1.2.0
+notes:
+- Tested against NX-OS 7.0(3)I5(1).
+- This module works with connection C(network_cli) and C(httpapi).
+author: Nilashish Chakraborty (@NilashishC)
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the command B(show running-config | section "^router ospfv3").
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A list of OSPFv3 process configuration.
+ type: dict
+ suboptions:
+ processes:
+ description:
+ - A list of OSPFv3 instances' configurations.
+ type: list
+ elements: dict
+ suboptions:
+ address_family:
+ description:
+ - IPv6 unicast address-family OSPFv3 settings.
+ type: dict
+ suboptions:
+ afi:
+ description:
+ - Configure OSPFv3 settings under IPv6 address-family.
+ type: str
+ choices: ['ipv6']
+ safi:
+ description:
+ - Configure OSPFv3 settings under IPv6 unicast address-family.
+ type: str
+ choices: ['unicast']
+ areas:
+ description:
+ - Configure properties of OSPF Areas under address-family.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description:
+ - The Area ID as an integer or IP Address.
+ type: str
+ required: True
+ default_cost:
+ description:
+ - Specify the default cost.
+ type: int
+ filter_list:
+ description:
+ - Filter prefixes between OSPF areas.
+ type: list
+ elements: dict
+ suboptions:
+ route_map:
+ description:
+ - The Route-map name.
+ type: str
+ required: True
+ direction:
+ description:
+ - The direction to apply the route map.
+ type: str
+ choices: [in, out]
+ required: True
+ ranges:
+ description:
+ - Configure an address range for the area.
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - IP in Prefix format (x.x.x.x/len)
+ type: str
+ required: True
+ cost:
+ description:
+ - Cost to use for the range.
+ type: int
+ not_advertise:
+ description:
+ - Suppress advertising the specified range.
+ type: bool
+ default_information:
+ description:
+ - Control distribution of default routes.
+ type: dict
+ suboptions:
+ originate:
+ description:
+ - Distribute a default route.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable distribution of default route.
+ type: bool
+ always:
+ description:
+ - Always advertise a default route.
+ type: bool
+ route_map:
+ description:
+ - Policy to control distribution of default routes
+ type: str
+ distance:
+ description:
+ - Configure the OSPF administrative distance.
+ type: int
+ maximum_paths:
+ description:
+ - Maximum paths per destination.
+ type: int
+ redistribute:
+ description:
+ - Redistribute information from another routing protocol.
+ type: list
+ elements: dict
+ suboptions:
+ protocol:
+ description:
+ - The name of the protocol.
+ type: str
+ choices: [bgp, direct, eigrp, isis, lisp, ospfv3, rip, static]
+ required: True
+ id:
+ description:
+ - The identifier for the protocol specified.
+ type: str
+ route_map:
+ description:
+ - The route map policy to constrain redistribution.
+ type: str
+ required: True
+ summary_address:
+ description:
+ - Configure route summarization for redistribution.
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - IPv6 prefix format 'xxxx:xxxx/ml', 'xxxx:xxxx::/ml' or 'xxxx::xx/128'
+ type: str
+ required: True
+ not_advertise:
+ description:
+ - Supress advertising the specified summary.
+ type: bool
+ tag:
+ description:
+ - A 32-bit tag value.
+ type: int
+ table_map:
+ description:
+ - Policy for filtering/modifying OSPF routes before sending them to
+ RIB.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - The Route Map name.
+ type: str
+ required: True
+ filter:
+ description:
+ - Block the OSPF routes from being sent to RIB.
+ type: bool
+ timers:
+ description:
+ - Configure timer related constants.
+ type: dict
+ suboptions:
+ throttle:
+ description:
+ - Configure throttle related constants.
+ type: dict
+ suboptions:
+ spf:
+ description:
+ - Set OSPF SPF timers.
+ type: dict
+ suboptions:
+ initial_spf_delay:
+ description:
+ - Initial SPF schedule delay in milliseconds.
+ type: int
+ min_hold_time:
+ description:
+ - Minimum hold time between SPF calculations.
+ type: int
+ max_wait_time:
+ description:
+ - Maximum wait time between SPF calculations.
+ type: int
+ areas:
+ description:
+ - Configure properties of OSPF Areas.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description:
+ - The Area ID as an integer or IP Address.
+ type: str
+ required: True
+ nssa:
+ description:
+ - NSSA settings for the area.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure area as NSSA.
+ type: bool
+ default_information_originate:
+ description:
+ - Originate Type-7 default LSA into NSSA area.
+ type: bool
+ no_redistribution:
+ description:
+ - Do not send redistributed LSAs into NSSA area.
+ type: bool
+ no_summary:
+ description:
+ - Do not send summary LSAs into NSSA area.
+ type: bool
+ route_map:
+ description:
+ - Policy to control distribution of default route.
+ type: str
+ translate:
+ description:
+ - Translate LSA.
+ type: dict
+ suboptions:
+ type7:
+ description:
+ - Translate from Type 7 to Type 5.
+ type: dict
+ suboptions:
+ always:
+ description:
+ - Always translate LSAs
+ type: bool
+ never:
+ description:
+ - Never translate LSAs
+ type: bool
+ supress_fa:
+ description:
+ - Suppress forwarding address in translated LSAs.
+ type: bool
+ stub:
+ description:
+ - Settings for configuring the area as a stub.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure the area as a stub.
+ type: bool
+ no_summary:
+ description:
+ - Prevent ABR from sending summary LSAs into stub area.
+ type: bool
+ auto_cost:
+ description:
+ - Calculate OSPF cost according to bandwidth.
+ type: dict
+ suboptions:
+ reference_bandwidth:
+ description:
+ - Reference bandwidth used to assign OSPF cost.
+ type: int
+ required: True
+ unit:
+ description:
+ - Specify in which unit the reference bandwidth is specified.
+ type: str
+ required: True
+ choices: [Gbps, Mbps]
+ flush_routes:
+ description:
+ - Flush routes on a non-graceful controlled restart.
+ type: bool
+ graceful_restart:
+ description:
+ - Configure graceful restart.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable graceful-restart.
+ type: bool
+ grace_period:
+ description:
+ - Configure maximum interval to restart gracefully.
+ type: int
+ helper_disable:
+ description:
+ - Enable/Disable helper mode.
+ type: bool
+ planned_only:
+ description:
+ - Enable graceful restart only for a planned restart
+ type: bool
+ isolate:
+ description:
+ - Isolate this router from OSPF perspective.
+ type: bool
+ log_adjacency_changes:
+ description:
+ - Log changes in adjacency state.
+ type: dict
+ suboptions:
+ log:
+ description:
+ - Enable/disable logging changes in adjacency state.
+ type: bool
+ detail:
+ description:
+ - Notify all state changes.
+ type: bool
+ max_lsa:
+ description:
+ - Feature to limit the number of non-self-originated LSAs.
+ type: dict
+ suboptions:
+ max_non_self_generated_lsa:
+ description:
+ - Set the maximum number of non self-generated LSAs.
+ type: int
+ required: True
+ threshold:
+ description:
+ - Threshold value (%) at which to generate a warning message.
+ type: int
+ ignore_count:
+ description:
+ - Set count on how many times adjacencies can be suppressed.
+ type: int
+ ignore_time:
+ description:
+ - Set time during which all adjacencies are suppressed.
+ type: int
+ reset_time:
+ description:
+ - Set number of minutes after which ignore-count is reset to zero.
+ type: int
+ warning_only:
+ description:
+ - Log a warning message when limit is exceeded.
+ type: bool
+ max_metric:
+ description:
+ - Maximize the cost metric.
+ type: dict
+ suboptions:
+ router_lsa:
+ description:
+ - Router LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set router-lsa attribute.
+ type: bool
+ external_lsa:
+ description:
+ - External LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set external-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Set max metric value for external LSAs.
+ type: int
+ stub_prefix_lsa:
+ description:
+ - Advertise Max metric for Stub links as well.
+ type: bool
+ on_startup:
+ description:
+ - Effective only at startup.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set on-startup attribute.
+ type: bool
+ wait_period:
+ description:
+ - Wait period in seconds after startup.
+ type: int
+ wait_for_bgp_asn:
+ description:
+ - ASN of BGP to wait for.
+ type: int
+ inter_area_prefix_lsa:
+ description:
+ - Inter-area-prefix LSAs configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set summary-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Max metric value for summary LSAs.
+ type: int
+ name_lookup:
+ description:
+ - Display OSPF router ids as DNS names.
+ type: bool
+ passive_interface:
+ description:
+ - Suppress routing updates on the interface.
+ type: dict
+ suboptions:
+ default:
+ description:
+ - Interfaces passive by default.
+ type: bool
+ process_id:
+ description:
+ - The OSPF process tag.
+ type: str
+ required: True
+ router_id:
+ description:
+ - Set OSPF process router-id.
+ type: str
+ shutdown:
+ description:
+ - Shutdown the OSPF protocol instance.
+ type: bool
+ timers:
+ description:
+ - Configure timer related constants.
+ type: dict
+ suboptions:
+ lsa_arrival:
+ description:
+ - Mimimum interval between arrival of a LSA.
+ type: int
+ lsa_group_pacing:
+ description:
+ - LSA group refresh/maxage interval.
+ type: int
+ throttle:
+ description:
+ - Configure throttle related constants.
+ type: dict
+ suboptions:
+ lsa:
+ description:
+ - Set rate-limiting for LSA generation.
+ type: dict
+ suboptions:
+ start_interval:
+ description:
+ - The start interval.
+ type: int
+ hold_interval:
+ description:
+ - The hold interval.
+ type: int
+ max_interval:
+ description:
+ - The max interval.
+ type: int
+ vrfs:
+ description:
+ - Configure VRF specific OSPF settings.
+ type: list
+ elements: dict
+ suboptions:
+ areas:
+ description:
+ - Configure properties of OSPF Areas.
+ type: list
+ elements: dict
+ suboptions:
+ area_id:
+ description:
+ - The Area ID as an integer or IP Address.
+ type: str
+ required: True
+ nssa:
+ description:
+ - NSSA settings for the area.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure area as NSSA.
+ type: bool
+ default_information_originate:
+ description:
+ - Originate Type-7 default LSA into NSSA area.
+ type: bool
+ no_redistribution:
+ description:
+ - Do not send redistributed LSAs into NSSA area.
+ type: bool
+ no_summary:
+ description:
+ - Do not send summary LSAs into NSSA area.
+ type: bool
+ route_map:
+ description:
+ - Policy to control distribution of default route.
+ type: str
+ translate:
+ description:
+ - Translate LSA.
+ type: dict
+ suboptions:
+ type7:
+ description:
+ - Translate from Type 7 to Type 5.
+ type: dict
+ suboptions:
+ always:
+ description:
+ - Always translate LSAs
+ type: bool
+ never:
+ description:
+ - Never translate LSAs
+ type: bool
+ supress_fa:
+ description:
+ - Suppress forwarding address in translated LSAs.
+ type: bool
+ stub:
+ description:
+ - Settings for configuring the area as a stub.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Configure the area as a stub.
+ type: bool
+ no_summary:
+ description:
+ - Prevent ABR from sending summary LSAs into stub area.
+ type: bool
+ auto_cost:
+ description:
+ - Calculate OSPF cost according to bandwidth.
+ type: dict
+ suboptions:
+ reference_bandwidth:
+ description:
+ - Reference bandwidth used to assign OSPF cost.
+ type: int
+ required: True
+ unit:
+ description:
+ - Specify in which unit the reference bandwidth is specified.
+ type: str
+ required: True
+ choices: [Gbps, Mbps]
+ graceful_restart:
+ description:
+ - Configure graceful restart.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Enable graceful-restart.
+ type: bool
+ grace_period:
+ description:
+ - Configure maximum interval to restart gracefully.
+ type: int
+ helper_disable:
+ description:
+ - Enable/Disable helper mode.
+ type: bool
+ planned_only:
+ description:
+ - Enable graceful restart only for a planned restart
+ type: bool
+ log_adjacency_changes:
+ description:
+ - Log changes in adjacency state.
+ type: dict
+ suboptions:
+ log:
+ description:
+ - Enable/disable logging changes in adjacency state.
+ type: bool
+ detail:
+ description:
+ - Notify all state changes.
+ type: bool
+ max_lsa:
+ description:
+ - Feature to limit the number of non-self-originated LSAs.
+ type: dict
+ suboptions:
+ max_non_self_generated_lsa:
+ description:
+ - Set the maximum number of non self-generated LSAs.
+ type: int
+ required: True
+ threshold:
+ description:
+ - Threshold value (%) at which to generate a warning message.
+ type: int
+ ignore_count:
+ description:
+ - Set count on how many times adjacencies can be suppressed.
+ type: int
+ ignore_time:
+ description:
+ - Set time during which all adjacencies are suppressed.
+ type: int
+ reset_time:
+ description:
+ - Set number of minutes after which ignore-count is reset to zero.
+ type: int
+ warning_only:
+ description:
+ - Log a warning message when limit is exceeded.
+ type: bool
+ max_metric:
+ description:
+ - Maximize the cost metric.
+ type: dict
+ suboptions:
+ router_lsa:
+ description:
+ - Router LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set router-lsa attribute.
+ type: bool
+ external_lsa:
+ description:
+ - External LSA configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set external-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Set max metric value for external LSAs.
+ type: int
+ stub_prefix_lsa:
+ description:
+ - Advertise Max metric for Stub links as well.
+ type: bool
+ on_startup:
+ description:
+ - Effective only at startup.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set on-startup attribute.
+ type: bool
+ wait_period:
+ description:
+ - Wait period in seconds after startup.
+ type: int
+ wait_for_bgp_asn:
+ description:
+ - ASN of BGP to wait for.
+ type: int
+ inter_area_prefix_lsa:
+ description:
+ - Inter-area-prefix LSAs configuration.
+ type: dict
+ suboptions:
+ set:
+ description:
+ - Set summary-lsa attribute.
+ type: bool
+ max_metric_value:
+ description:
+ - Max metric value for summary LSAs.
+ type: int
+ name_lookup:
+ description:
+ - Display OSPF router ids as DNS names.
+ type: bool
+ passive_interface:
+ description:
+ - Suppress routing updates on the interface.
+ type: dict
+ suboptions:
+ default:
+ description:
+ - Interfaces passive by default.
+ type: bool
+ router_id:
+ description:
+ - Set OSPF process router-id.
+ type: str
+ shutdown:
+ description:
+ - Shutdown the OSPF protocol instance.
+ type: bool
+ timers:
+ description:
+ - Configure timer related constants.
+ type: dict
+ suboptions:
+ lsa_arrival:
+ description:
+ - Mimimum interval between arrival of a LSA.
+ type: int
+ lsa_group_pacing:
+ description:
+ - LSA group refresh/maxage interval.
+ type: int
+ throttle:
+ description:
+ - Configure throttle related constants.
+ type: dict
+ suboptions:
+ lsa:
+ description:
+ - Set rate-limiting for LSA generation.
+ type: dict
+ suboptions:
+ start_interval:
+ description:
+ - The start interval.
+ type: int
+ hold_interval:
+ description:
+ - The hold interval.
+ type: int
+ max_interval:
+ description:
+ - The max interval.
+ type: int
+ vrf:
+ description:
+ - Name/Identifier of the VRF.
+ type: str
+ required: True
+ state:
+ description:
+ - The state the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - parsed
+ - rendered
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# nxos-9k-rdo#
+
+- name: Merge the provided configuration with the exisiting running configuration
+ cisco.nxos.nxos_ospfv3:
+ config:
+ processes:
+ - process_id: 100
+ router_id: 203.0.113.20
+ - process_id: 102
+ router_id: 198.51.100.1
+ address_family:
+ afi: ipv6
+ safi: unicast
+ areas:
+ - area_id: 0.0.0.100
+ filter_list:
+ - route_map: rmap_1
+ direction: in
+ - route_map: rmap_2
+ direction: out
+ ranges:
+ - prefix: 2001:db2::/32
+ not_advertise: true
+ - prefix: 2001:db3::/32
+ cost: 120
+ redistribute:
+ - protocol: eigrp
+ id: 120
+ route_map: rmap_1
+ - protocol: direct
+ route_map: ospf102-direct-connect
+ vrfs:
+ - vrf: zone1
+ router_id: 198.51.100.129
+ areas:
+ - area_id: 0.0.0.102
+ nssa:
+ default_information_originate: true
+ no_summary: true
+ - area_id: 0.0.0.103
+ nssa:
+ no_summary: true
+ translate:
+ type7:
+ always: true
+ - vrf: zone2
+ auto_cost:
+ reference_bandwidth: 45
+ unit: Gbps
+ state: merged
+
+# Task output
+# -------------
+# before: {}
+#
+# commands:
+# - router ospf 102
+# - router-id 198.51.100.1
+# - address-family ipv6 unicast
+# - redistribute eigrp 120 route-map rmap_1
+# - redistribute direct route-map ospf102-direct-connect
+# - area 0.0.0.100 filter-list route-map rmap_1 in
+# - area 0.0.0.100 filter-list route-map rmap_2 out
+# - area 0.0.0.100 range 2001:db2::/32 not-advertise
+# - area 0.0.0.100 range 2001:db3::/32 cost 120
+# - vrf zone1
+# - router-id 198.51.100.129
+# - area 0.0.0.102 nssa no-summary default-information-originate
+# - area 0.0.0.103 nssa no-summary
+# - area 0.0.0.103 nssa translate type7 always
+# - vrf zone2
+# - auto-cost reference-bandwidth 45 Gbps
+# - router ospf 100
+# - router-id 203.0.113.20
+#
+# after:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv4
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# - cost: 120
+# prefix: 2001:db3::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 100
+# router-id 203.0.113.20
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family ipv6 unicast
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 2001:db2::/32 not-advertise
+# area 0.0.0.100 range 2001:db3::/32 cost 120
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+# Using replaced
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 100
+# router-id 203.0.113.20
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family upv6 unicast
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 2001:db2::/32 not-advertise
+# area 0.0.0.100 range 2001:db3::/32 cost 120
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Replace device configurations of listed OSPFv3 processes with provided configurations
+ cisco.nxos.nxos_ospfv3:
+ config:
+ processes:
+ - process_id: 102
+ router_id: 198.51.100.1
+ address_family:
+ afi: ipv6
+ safi: unicast
+ areas:
+ - area_id: 0.0.0.100
+ filter_list:
+ - route_map: rmap_8
+ direction: in
+ ranges:
+ - not_advertise: true
+ prefix: 2001:db2::/32
+ redistribute:
+ - protocol: eigrp
+ id: 130
+ route_map: rmap_1
+ - protocol: direct
+ route_map: ospf102-direct-connect
+ vrfs:
+ - vrf: zone1
+ router_id: 198.51.100.129
+ areas:
+ - area_id: 0.0.0.102
+ nssa:
+ default_information_originate: True
+ no_summary: True
+ state: replaced
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv4
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# - cost: 120
+# prefix: 2001:db3::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - router ospf 102
+# - address-family ipv6 unicast
+# - redistribute eigrp 130 route-map rmap_1
+# - no redistribute eigrp 120 route-map rmap_1
+# - area 0.0.0.100 filter-list route-map rmap_8 in
+# - no area 0.0.0.100 filter-list route-map rmap_2 out
+# - no area 0.0.0.100 range 2001:db3::/32
+# - vrf zone1
+# - no area 0.0.0.103 nssa
+# - no area 0.0.0.103 nssa translate type7 always
+# - no vrf zone2
+#
+# after:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv6
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: in
+# route_map: rmap_8
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "130"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# router_id: 198.51.100.129
+# vrf: zone1
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 100
+# router-id 203.0.113.20
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family ipv6 unicast
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 130 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_8 in
+# area 0.0.0.100 range 198.51.100.64/27 not-advertise
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+
+# Using overridden
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 100
+# router-id 203.0.113.20
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family ipv6 unicast
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 2001:db2::/32 not-advertise
+# area 0.0.0.100 range 2001:db3::/32 cost 120
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Overridde all OSPFv3 configuration with provided configuration
+ cisco.nxos.nxos_ospfv3:
+ config:
+ processes:
+ - process_id: 104
+ router_id: 203.0.113.20
+ - process_id: 102
+ router_id: 198.51.100.1
+ shutdown: true
+ state: overridden
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv4
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# - cost: 120
+# prefix: 2001:db3::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - no router ospfv3 100
+# - router ospfv3 104
+# - router-id 203.0.113.20
+# - router ospfv3 102
+# - shutdown
+# - address-family ipv6 unicast
+# - no redistribute direct route-map ospf102-direct-connect
+# - no redistribute eigrp 120 route-map rmap_1
+# - no area 0.0.0.100 filter-list route-map rmap_2 out
+# - no area 0.0.0.100 filter-list route-map rmap_1 in
+# - no area 0.0.0.100 range 2001:db2::/32
+# - no area 0.0.0.100 range 2001:db3::/32
+# - no vrf zone1
+# - no vrf zone2
+#
+# after:
+# processes:
+# - process_id: "102"
+# router_id: 198.51.100.1
+# shutdown: true
+# address_family:
+# afi: ipv6
+# safi: unicast
+# - process_id: "104"
+# router_id: 203.0.113.20
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family ipv6 unicast
+# shutdown
+# router ospfv3 104
+# router-id 203.0.113.20
+
+# Using deleted to delete a single OSPF process
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospf .*"
+# router ospfv3 100
+# router-id 203.0.113.20
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family ipv6 unicast
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 2001:db2::/32 not-advertise
+# area 0.0.0.100 range 2001:db3::/32 cost 120
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Delete a single OSPFv3 process
+ cisco.nxos.nxos_ospfv3:
+ config:
+ processes:
+ - process_id: 102
+ state: deleted
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv4
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# - cost: 120
+# prefix: 2001:db3::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - no router ospfv3 102
+#
+# after:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 100
+# router-id 203.0.113.20
+
+# Using deleted all OSPFv3 processes from the device
+
+# Before state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# router ospfv3 100
+# router-id 203.0.113.20
+# router ospfv3 102
+# router-id 198.51.100.1
+# address-family ipv6 unicast
+# redistribute direct route-map ospf102-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 2001:db2::/32 not-advertise
+# area 0.0.0.100 range 2001:db3::/32 cost 120
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.0.102 nssa no-summary default-information-originate
+# area 0.0.0.103 nssa no-summary
+# area 0.0.0.103 nssa translate type7 always
+# vrf zone2
+# auto-cost reference-bandwidth 45 Gbps
+
+- name: Delete all OSPFv3 processes from the device
+ cisco.nxos.nxos_ospfv3:
+ state: deleted
+
+# Task output
+# -------------
+# before:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv4
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# - cost: 120
+# prefix: 2001:db3::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+# commands:
+# - no router ospfv3 100
+# - no router ospfv3 102
+#
+# after: {}
+
+# After state:
+# ------------
+# nxos-9k-rdo# sh running-config | section "^router ospfv3"
+# nxos-9k-rdo#
+
+# Using rendered
+
+- name: Merge the provided configuration with the exisiting running configuration
+ cisco.nxos.nxos_ospfv3:
+ config:
+ processes:
+ - process_id: 100
+ router_id: 203.0.113.20
+ - process_id: 102
+ router_id: 198.51.100.1
+ address_family:
+ afi: ipv6
+ safi: unicast
+ areas:
+ - area_id: 0.0.0.100
+ filter_list:
+ - route_map: rmap_1
+ direction: in
+ - route_map: rmap_2
+ direction: out
+ ranges:
+ - prefix: 2001:db2::/32
+ not_advertise: true
+ - prefix: 2001:db3::/32
+ cost: 120
+ redistribute:
+ - protocol: eigrp
+ id: 120
+ route_map: rmap_1
+ - protocol: direct
+ route_map: ospf102-direct-connect
+ vrfs:
+ - vrf: zone1
+ router_id: 198.51.100.129
+ areas:
+ - area_id: 0.0.0.102
+ nssa:
+ default_information_originate: true
+ no_summary: true
+ - area_id: 0.0.0.103
+ nssa:
+ no_summary: true
+ translate:
+ type7:
+ always: true
+ - vrf: zone2
+ auto_cost:
+ reference_bandwidth: 45
+ unit: Gbps
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+# rendered:
+# - router ospfv3 100
+# - router-id 203.0.113.20
+# - router ospfv3 102
+# - router-id 198.51.100.1
+# - address-family ipv6 unicast
+# - redistribute eigrp 120 route-map rmap_1
+# - redistribute direct route-map ospf102-direct-connect
+# - area 0.0.0.100 filter-list route-map rmap_1 in
+# - area 0.0.0.100 filter-list route-map rmap_2 out
+# - area 0.0.0.100 range 2001:db2::/32 not-advertise
+# - area 0.0.0.100 range 2001:db3::/32 cost 120
+# - vrf zone1
+# - router-id 198.51.100.129
+# - area 0.0.0.102 nssa no-summary default-information-originate
+# - area 0.0.0.103 nssa no-summary
+# - area 0.0.0.103 nssa translate type7 always
+# - vrf zone2
+# - auto-cost reference-bandwidth 45 Gbps
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# router ospfv3 100
+# router-id 192.0.100.1
+# address-family ipv6 unicast
+# redistribute direct route-map ospf-direct-connect
+# redistribute eigrp 120 route-map rmap_1
+# area 0.0.0.100 filter-list route-map rmap_2 out
+# area 0.0.0.100 filter-list route-map rmap_1 in
+# area 0.0.0.100 range 2001:db2::/32 not-advertise
+# area 0.0.0.100 range 2001:db3::/32 cost 120
+# vrf zone1
+# router-id 198.51.100.129
+# area 0.0.100.1 nssa no-summary no-redistribution
+# router ospfv3 102
+# router-id 198.54.100.1
+# shutdown
+
+- name: Parse externally provided OSPFv3 config
+ cisco.nxos.nxos_ospfv3:
+ running_config: "{{ lookup('file', 'ospfv2.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+# parsed:
+# processes:
+# - process_id: "100"
+# address_family:
+# afi: ipv6
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.101
+# nssa:
+# no_redistribution: true
+# no_summary: true
+# - area_id: 0.0.0.102
+# stub:
+# no_summary: true
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 192.0.2.0/24
+# - cost: 120
+# prefix: 192.0.3.0/24
+# redistribute:
+# - protocol: direct
+# route_map: ospf-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# router_id: 192.0.100.1
+# vrfs:
+# - vrf: zone1
+# areas:
+# - area_id: 0.0.100.1
+# nssa:
+# no_redistribution: true
+# no_summary: true
+# router_id: 192.0.100.2
+# - process_id: "102"
+# router_id: 198.54.100.1
+# shutdown: True
+
+# Using gathered
+
+- name: Gather OSPFv3 facts using gathered
+ cisco.nxos.nxos_ospfv3:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# processes:
+# - process_id: "100"
+# router_id: 203.0.113.20
+# - address_family:
+# afi: ipv4
+# safi: unicast
+# areas:
+# - area_id: 0.0.0.100
+# filter_list:
+# - direction: out
+# route_map: rmap_2
+# - direction: in
+# route_map: rmap_1
+# ranges:
+# - not_advertise: true
+# prefix: 2001:db2::/32
+# - cost: 120
+# prefix: 2001:db3::/32
+# redistribute:
+# - protocol: direct
+# route_map: ospf102-direct-connect
+# - id: "120"
+# protocol: eigrp
+# route_map: rmap_1
+# process_id: "102"
+# router_id: 198.51.100.1
+# vrfs:
+# - areas:
+# - area_id: 0.0.0.102
+# nssa:
+# default_information_originate: true
+# no_summary: true
+# - area_id: 0.0.0.103
+# nssa:
+# no_summary: true
+# translate:
+# type7:
+# always: true
+# router_id: 198.51.100.129
+# vrf: zone1
+# - auto_cost:
+# reference_bandwidth: 45
+# unit: Gbps
+# vrf: zone2
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - "router ospfv3 102"
+ - "router-id 198.54.100.1"
+ - "router ospfv3 100"
+ - "router-id 192.0.100.1"
+ - "address-family ipv6 unicast"
+ - "redistribute eigrp 120 route-map rmap_1"
+ - "redistribute direct route-map ospf-direct-connect"
+ - "area 0.0.0.100 filter-list route-map rmap_1 in"
+ - "area 0.0.0.100 filter-list route-map rmap_2 out"
+ - "area 0.0.0.100 range 2001:db2::/32 not-advertise"
+ - "area 0.0.0.100 range 2001:db3::/32 cost 120"
+ - "vrf zone1"
+ - "router-id 192.0.100.2"
+ - "vrf zone2"
+ - "auto-cost reference-bandwidth 45 Gbps"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.ospfv3.ospfv3 import (
+ Ospfv3Args,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.ospfv3.ospfv3 import (
+ Ospfv3,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=Ospfv3Args.argument_spec,
+ mutually_exclusive=[["config", "running_config"]],
+ required_if=[
+ ["state", "merged", ["config"]],
+ ["state", "replaced", ["config"]],
+ ["state", "overridden", ["config"]],
+ ["state", "rendered", ["config"]],
+ ["state", "parsed", ["running_config"]],
+ ],
+ supports_check_mode=True,
+ )
+
+ result = Ospfv3(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_overlay_global.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_overlay_global.py
new file mode 100644
index 00000000..1ea560c3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_overlay_global.py
@@ -0,0 +1,204 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_overlay_global
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Configures anycast gateway MAC of the switch.
+description:
+- Configures anycast gateway MAC of the switch.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Default restores params default value
+- Supported MAC address format are "E.E.E", "EE-EE-EE-EE-EE-EE", "EE:EE:EE:EE:EE:EE"
+ and "EEEE.EEEE.EEEE"
+options:
+ anycast_gateway_mac:
+ description:
+ - Anycast gateway mac of the switch.
+ required: true
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_overlay_global:
+ anycast_gateway_mac: b.b.b
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["fabric forwarding anycast-gateway-mac 000B.000B.000B"]
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+PARAM_TO_COMMAND_KEYMAP = {
+ "anycast_gateway_mac": "fabric forwarding anycast-gateway-mac"
+}
+
+
+def get_existing(module, args):
+ existing = {}
+ config = str(get_config(module))
+
+ for arg in args:
+ command = PARAM_TO_COMMAND_KEYMAP[arg]
+ has_command = re.findall(
+ r"(?:{0}\s)(?P<value>.*)$".format(command), config, re.M
+ )
+ value = ""
+ if has_command:
+ value = has_command[0]
+ existing[arg] = value
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if value:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def get_commands(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, proposed in proposed_commands.items():
+ existing_value = existing_commands.get(key)
+ if proposed == "default" and existing_value:
+ commands.append("no {0} {1}".format(key, existing_value))
+ elif "anycast-gateway-mac" in key and proposed != "default":
+ proposed = normalize_mac(proposed, module)
+ existing_value = normalize_mac(existing_value, module)
+ if proposed != existing_value:
+ command = "{0} {1}".format(key, proposed)
+ commands.append(command)
+ if commands:
+ candidate.add(commands, parents=[])
+
+
+def normalize_mac(proposed_mac, module):
+ if proposed_mac is None:
+ return ""
+ try:
+ if "-" in proposed_mac:
+ splitted_mac = proposed_mac.split("-")
+ if len(splitted_mac) != 6:
+ raise ValueError
+
+ for octect in splitted_mac:
+ if len(octect) != 2:
+ raise ValueError
+
+ elif "." in proposed_mac:
+ splitted_mac = []
+ splitted_dot_mac = proposed_mac.split(".")
+ if len(splitted_dot_mac) != 3:
+ raise ValueError
+
+ for octect in splitted_dot_mac:
+ if len(octect) > 4:
+ raise ValueError
+ else:
+ octect_len = len(octect)
+ padding = 4 - octect_len
+ splitted_mac.append(octect.zfill(padding + 1))
+
+ elif ":" in proposed_mac:
+ splitted_mac = proposed_mac.split(":")
+ if len(splitted_mac) != 6:
+ raise ValueError
+
+ for octect in splitted_mac:
+ if len(octect) != 2:
+ raise ValueError
+ else:
+ raise ValueError
+ except ValueError:
+ module.fail_json(
+ msg="Invalid MAC address format", proposed_mac=proposed_mac
+ )
+
+ joined_mac = "".join(splitted_mac)
+ # fmt: off
+ mac = [joined_mac[i: i + 4] for i in range(0, len(joined_mac), 4)]
+ # fmt: on
+ return ".".join(mac).upper()
+
+
+def main():
+ argument_spec = dict(anycast_gateway_mac=dict(required=True, type="str"))
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ result = {"changed": False, "commands": [], "warnings": warnings}
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+
+ existing = get_existing(module, args)
+ proposed = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ candidate = CustomNetworkConfig(indent=3)
+ get_commands(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ result["commands"] = candidate
+
+ if not module.check_mode:
+ load_config(module, candidate)
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim.py
new file mode 100644
index 00000000..1f8aefc6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim.py
@@ -0,0 +1,220 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_pim
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages configuration of a PIM instance.
+description:
+- Manages configuration of a Protocol Independent Multicast (PIM) instance.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+options:
+ bfd:
+ description:
+ - Enables BFD on all PIM interfaces.
+ - "Dependency: ''feature bfd''"
+ type: str
+ choices:
+ - enable
+ - disable
+ ssm_range:
+ description:
+ - Configure group ranges for Source Specific Multicast (SSM). Valid values are
+ multicast addresses or the keyword C(none) or keyword C(default). C(none) removes
+ all SSM group ranges. C(default) will set ssm_range to the default multicast
+ address. If you set multicast address, please ensure that it is not the same
+ as the C(default), otherwise use the C(default) option.
+ type: list
+ elements: str
+"""
+EXAMPLES = """
+- name: Configure ssm_range, enable bfd
+ cisco.nxos.nxos_pim:
+ bfd: enable
+ ssm_range: 224.0.0.0/8
+
+- name: Set to default
+ cisco.nxos.nxos_pim:
+ ssm_range: default
+
+- name: Remove all ssm group ranges
+ cisco.nxos.nxos_pim:
+ ssm_range: none
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - ip pim bfd
+ - ip pim ssm range 224.0.0.0/8
+"""
+
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+PARAM_TO_COMMAND_KEYMAP = {
+ "bfd": "ip pim bfd",
+ "ssm_range": "ip pim ssm range",
+}
+
+
+def get_existing(module, args):
+ existing = {}
+ config = str(get_config(module))
+
+ for arg in args:
+ if "ssm_range" in arg:
+ # <value> may be 'n.n.n.n/s', 'none', or 'default'
+ m = re.search(
+ r"ssm range (?P<value>(?:[\s\d.\/]+|none|default))?$",
+ config,
+ re.M,
+ )
+ if m:
+ # Remove rsvd SSM range
+ value = m.group("value").replace("232.0.0.0/8", "")
+ existing[arg] = value.split()
+
+ elif "bfd" in arg and "ip pim bfd" in config:
+ existing[arg] = "enable"
+
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if value is not None:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def get_commands(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+
+ for key, value in proposed_commands.items():
+ command = ""
+ if key == "ip pim ssm range":
+ if value == "default":
+ # no cmd needs a value but the actual value does not matter
+ command = "no ip pim ssm range none"
+ elif value == "none":
+ command = "ip pim ssm range none"
+ elif value:
+ command = "ip pim ssm range {0}".format(value)
+ elif key == "ip pim bfd":
+ no_cmd = "no " if value == "disable" else ""
+ command = no_cmd + key
+
+ if command:
+ commands.append(command)
+
+ if commands:
+ candidate.add(commands, parents=[])
+
+
+def main():
+ argument_spec = dict(
+ bfd=dict(required=False, type="str", choices=["enable", "disable"]),
+ ssm_range=dict(
+ required=False, type="list", default=[], elements="str"
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+ warnings = list()
+ result = {"changed": False, "commands": [], "warnings": warnings}
+
+ params = module.params
+ args = [k for k in PARAM_TO_COMMAND_KEYMAP.keys() if params[k] is not None]
+
+ # SSM syntax check
+ if "ssm_range" in args:
+ for item in params["ssm_range"]:
+ if re.search("none|default", item):
+ break
+ if len(item.split(".")) != 4:
+ module.fail_json(
+ msg="Valid ssm_range values are multicast addresses "
+ "or the keyword 'none' or the keyword 'default'."
+ )
+
+ existing = get_existing(module, args)
+ proposed_args = dict((k, v) for k, v in params.items() if k in args)
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key == "ssm_range":
+ if value and value[0] == "default":
+ if existing.get(key):
+ proposed[key] = "default"
+ else:
+ v = sorted(set([str(i) for i in value]))
+ ex = sorted(set([str(i) for i in existing.get(key, [])]))
+ if v != ex:
+ proposed[key] = " ".join(str(s) for s in v)
+
+ elif key == "bfd":
+ if value != existing.get("bfd", "disable"):
+ proposed[key] = value
+
+ elif value != existing.get(key):
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ get_commands(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ result["commands"] = candidate
+ result["changed"] = True
+ load_config(module, candidate)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_interface.py
new file mode 100644
index 00000000..5c0a9601
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_interface.py
@@ -0,0 +1,621 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+DOCUMENTATION = """
+module: nxos_pim_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages PIM interface configuration.
+description:
+- Manages PIM interface configuration settings.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- When C(state=default), supported params will be reset to a default state. These
+ include C(dr_prio), C(hello_auth_key), C(hello_interval), C(jp_policy_out), C(jp_policy_in),
+ C(jp_type_in), C(jp_type_out), C(border), C(neighbor_policy), C(neighbor_type).
+- The C(hello_auth_key) param is not idempotent.
+- C(hello_auth_key) only supports clear text passwords.
+- When C(state=absent), pim interface configuration will be set to defaults and pim-sm
+ will be disabled on the interface.
+- PIM must be enabled on the device to use this module.
+- This module is for Layer 3 interfaces.
+options:
+ interface:
+ description:
+ - Full name of the interface such as Ethernet1/33.
+ type: str
+ required: true
+ sparse:
+ description:
+ - Enable/disable sparse-mode on the interface.
+ type: bool
+ default: false
+ bfd:
+ description:
+ - Enables BFD for PIM at the interface level. This overrides the bfd variable
+ set at the pim global level.
+ - Valid values are 'enable', 'disable' or 'default'.
+ - "Dependency: ''feature bfd''"
+ type: str
+ choices:
+ - enable
+ - disable
+ - default
+ dr_prio:
+ description:
+ - Configures priority for PIM DR election on interface.
+ type: str
+ hello_auth_key:
+ description:
+ - Authentication for hellos on this interface.
+ type: str
+ hello_interval:
+ description:
+ - Hello interval in milliseconds for this interface.
+ type: int
+ jp_policy_out:
+ description:
+ - Policy for join-prune messages (outbound).
+ type: str
+ jp_policy_in:
+ description:
+ - Policy for join-prune messages (inbound).
+ type: str
+ jp_type_out:
+ description:
+ - Type of policy mapped to C(jp_policy_out).
+ type: str
+ choices:
+ - prefix
+ - routemap
+ jp_type_in:
+ description:
+ - Type of policy mapped to C(jp_policy_in).
+ type: str
+ choices:
+ - prefix
+ - routemap
+ border:
+ description:
+ - Configures interface to be a boundary of a PIM domain.
+ type: bool
+ default: false
+ neighbor_policy:
+ description:
+ - Configures a neighbor policy for filtering adjacencies.
+ type: str
+ neighbor_type:
+ description:
+ - Type of policy mapped to neighbor_policy.
+ type: str
+ choices:
+ - prefix
+ - routemap
+ state:
+ description:
+ - Manages desired state of the resource.
+ type: str
+ choices:
+ - present
+ - absent
+ - default
+ default: present
+"""
+EXAMPLES = """
+- name: Ensure PIM is not running on the interface
+ cisco.nxos.nxos_pim_interface:
+ interface: eth1/33
+ state: absent
+
+- name: Ensure the interface has pim-sm enabled with the appropriate priority and
+ hello interval
+ cisco.nxos.nxos_pim_interface:
+ interface: eth1/33
+ dr_prio: 10
+ hello_interval: 40
+ state: present
+
+- name: Ensure join-prune policies exist
+ cisco.nxos.nxos_pim_interface:
+ interface: eth1/33
+ jp_policy_in: JPIN
+ jp_policy_out: JPOUT
+ jp_type_in: routemap
+ jp_type_out: routemap
+
+- name: disable bfd on the interface
+ cisco.nxos.nxos_pim_interface:
+ interface: eth1/33
+ bfd: disable
+
+- name: Ensure defaults are in place
+ cisco.nxos.nxos_pim_interface:
+ interface: eth1/33
+ state: default
+"""
+
+RETURN = r"""
+commands:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["interface eth1/33",
+ "ip pim neighbor-policy test",
+ "ip pim bfd-instance disable",
+ "ip pim neighbor-policy test"
+ ]
+"""
+
+import re
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_interface_type,
+)
+
+
+PARAM_TO_COMMAND_KEYMAP = {
+ "interface": "",
+ "bfd": "ip pim bfd-instance",
+ "sparse": "ip pim sparse-mode",
+ "dr_prio": "ip pim dr-priority {0}",
+ "hello_interval": "ip pim hello-interval {0}",
+ "hello_auth_key": "ip pim hello-authentication ah-md5 {0}",
+ "border": "ip pim border",
+ "jp_policy_out": "ip pim jp-policy prefix-list {0} out",
+ "jp_policy_in": "ip pim jp-policy prefix-list {0} in",
+ "jp_type_in": "",
+ "jp_type_out": "",
+ "neighbor_policy": "ip pim neighbor-policy prefix-list {0}",
+ "neighbor_type": "",
+}
+
+PARAM_TO_DEFAULT_KEYMAP = {
+ "bfd": "default",
+ "dr_prio": "1",
+ "hello_interval": "30000",
+ "sparse": False,
+ "border": False,
+ "hello_auth_key": False,
+}
+
+BFD_KEYMAP = {
+ None: None,
+ "default": "no ip pim bfd-instance",
+ "disable": "ip pim bfd-instance disable",
+ "enable": "ip pim bfd-instance",
+}
+
+
+def execute_show_command(command, module, text=False):
+ if text:
+ cmds = [{"command": command, "output": "text"}]
+ else:
+ cmds = [{"command": command, "output": "json"}]
+
+ return run_commands(module, cmds)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def local_existing(gexisting):
+ jp_bidir = False
+ isauth = False
+ if gexisting:
+ jp_bidir = gexisting.get("jp_bidir")
+ isauth = gexisting.get("isauth")
+ if jp_bidir and isauth:
+ gexisting.pop("jp_bidir")
+ gexisting.pop("isauth")
+
+ return gexisting, jp_bidir, isauth
+
+
+def get_interface_mode(interface, intf_type, module):
+ mode = "unknown"
+ command = "show interface {0}".format(interface)
+ body = execute_show_command(command, module)
+
+ try:
+ interface_table = body[0]["TABLE_interface"]["ROW_interface"]
+ except (KeyError, AttributeError, IndexError):
+ return mode
+
+ if intf_type in ["ethernet", "portchannel"]:
+ mode = str(interface_table.get("eth_mode", "layer3"))
+ if mode in ["access", "trunk"]:
+ mode = "layer2"
+ elif mode == "routed":
+ mode = "layer3"
+ elif intf_type in ["loopback", "svi"]:
+ mode = "layer3"
+ return mode
+
+
+def get_pim_interface(module, interface):
+ pim_interface = {}
+ body = get_config(module, flags=["interface {0}".format(interface)])
+
+ pim_interface["bfd"] = "default"
+ pim_interface["neighbor_type"] = None
+ pim_interface["neighbor_policy"] = None
+ pim_interface["jp_policy_in"] = None
+ pim_interface["jp_policy_out"] = None
+ pim_interface["jp_type_in"] = None
+ pim_interface["jp_type_out"] = None
+ pim_interface["jp_bidir"] = False
+ pim_interface["isauth"] = False
+
+ if body:
+ all_lines = body.splitlines()
+
+ for each in all_lines:
+ if "jp-policy" in each:
+ policy_name = re.search(
+ r"ip pim jp-policy(?: prefix-list)? (\S+)(?: \S+)?", each
+ ).group(1)
+ if "prefix-list" in each:
+ ptype = "prefix"
+ else:
+ ptype = "routemap"
+ if "out" in each:
+ pim_interface["jp_policy_out"] = policy_name
+ pim_interface["jp_type_out"] = ptype
+ elif "in" in each:
+ pim_interface["jp_policy_in"] = policy_name
+ pim_interface["jp_type_in"] = ptype
+ else:
+ pim_interface["jp_policy_in"] = policy_name
+ pim_interface["jp_policy_out"] = policy_name
+ pim_interface["jp_bidir"] = True
+ elif "neighbor-policy" in each:
+ pim_interface["neighbor_policy"] = re.search(
+ r"ip pim neighbor-policy(?: prefix-list)? (\S+)", each
+ ).group(1)
+ if "prefix-list" in each:
+ pim_interface["neighbor_type"] = "prefix"
+ else:
+ pim_interface["neighbor_type"] = "routemap"
+ elif "ah-md5" in each:
+ pim_interface["isauth"] = True
+ elif "sparse-mode" in each:
+ pim_interface["sparse"] = True
+ elif "bfd-instance" in each:
+ m = re.search(
+ r"ip pim bfd-instance(?P<disable> disable)?", each
+ )
+ if m:
+ pim_interface["bfd"] = (
+ "disable" if m.group("disable") else "enable"
+ )
+ elif "border" in each:
+ pim_interface["border"] = True
+ elif "hello-interval" in each:
+ pim_interface["hello_interval"] = re.search(
+ r"ip pim hello-interval (\d+)", body
+ ).group(1)
+ elif "dr-priority" in each:
+ pim_interface["dr_prio"] = re.search(
+ r"ip pim dr-priority (\d+)", body
+ ).group(1)
+
+ return pim_interface
+
+
+def fix_delta(delta, existing):
+ for key in list(delta):
+ if key in ["dr_prio", "hello_interval", "sparse", "border"]:
+ if (
+ delta.get(key) == PARAM_TO_DEFAULT_KEYMAP.get(key)
+ and existing.get(key) is None
+ ):
+ delta.pop(key)
+ return delta
+
+
+def config_pim_interface(delta, existing, jp_bidir, isauth):
+ command = None
+ commands = []
+
+ delta = fix_delta(delta, existing)
+
+ if jp_bidir:
+ if delta.get("jp_policy_in") or delta.get("jp_policy_out"):
+ if existing.get("jp_type_in") == "prefix":
+ command = "no ip pim jp-policy prefix-list {0}".format(
+ existing.get("jp_policy_in")
+ )
+ else:
+ command = "no ip pim jp-policy {0}".format(
+ existing.get("jp_policy_in")
+ )
+ if command:
+ commands.append(command)
+
+ for k, v in delta.items():
+ if k in [
+ "bfd",
+ "dr_prio",
+ "hello_interval",
+ "hello_auth_key",
+ "border",
+ "sparse",
+ ]:
+ if k == "bfd":
+ command = BFD_KEYMAP[v]
+ elif v:
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(v)
+ elif k == "hello_auth_key":
+ if isauth:
+ command = "no ip pim hello-authentication ah-md5"
+ else:
+ command = "no " + PARAM_TO_COMMAND_KEYMAP.get(k).format(v)
+
+ if command:
+ commands.append(command)
+ elif k in [
+ "neighbor_policy",
+ "jp_policy_in",
+ "jp_policy_out",
+ "neighbor_type",
+ ]:
+ if k in ["neighbor_policy", "neighbor_type"]:
+ temp = delta.get("neighbor_policy") or existing.get(
+ "neighbor_policy"
+ )
+ if delta.get("neighbor_type") == "prefix":
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(temp)
+ elif delta.get("neighbor_type") == "routemap":
+ command = "ip pim neighbor-policy {0}".format(temp)
+ elif existing.get("neighbor_type") == "prefix":
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(temp)
+ elif existing.get("neighbor_type") == "routemap":
+ command = "ip pim neighbor-policy {0}".format(temp)
+ elif k in ["jp_policy_in", "jp_type_in"]:
+ temp = delta.get("jp_policy_in") or existing.get(
+ "jp_policy_in"
+ )
+ if delta.get("jp_type_in") == "prefix":
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(temp)
+ elif delta.get("jp_type_in") == "routemap":
+ command = "ip pim jp-policy {0} in".format(temp)
+ elif existing.get("jp_type_in") == "prefix":
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(temp)
+ elif existing.get("jp_type_in") == "routemap":
+ command = "ip pim jp-policy {0} in".format(temp)
+ elif k in ["jp_policy_out", "jp_type_out"]:
+ temp = delta.get("jp_policy_out") or existing.get(
+ "jp_policy_out"
+ )
+ if delta.get("jp_type_out") == "prefix":
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(temp)
+ elif delta.get("jp_type_out") == "routemap":
+ command = "ip pim jp-policy {0} out".format(temp)
+ elif existing.get("jp_type_out") == "prefix":
+ command = PARAM_TO_COMMAND_KEYMAP.get(k).format(temp)
+ elif existing.get("jp_type_out") == "routemap":
+ command = "ip pim jp-policy {0} out".format(temp)
+ if command:
+ commands.append(command)
+ command = None
+
+ if "no ip pim sparse-mode" in commands:
+ # sparse is long-running on some platforms, process it last
+ commands.remove("no ip pim sparse-mode")
+ commands.append("no ip pim sparse-mode")
+ return commands
+
+
+def get_pim_interface_defaults():
+
+ args = dict(
+ dr_prio=PARAM_TO_DEFAULT_KEYMAP.get("dr_prio"),
+ bfd=PARAM_TO_DEFAULT_KEYMAP.get("bfd"),
+ border=PARAM_TO_DEFAULT_KEYMAP.get("border"),
+ sparse=PARAM_TO_DEFAULT_KEYMAP.get("sparse"),
+ hello_interval=PARAM_TO_DEFAULT_KEYMAP.get("hello_interval"),
+ hello_auth_key=PARAM_TO_DEFAULT_KEYMAP.get("hello_auth_key"),
+ )
+
+ default = dict(
+ (param, value) for (param, value) in args.items() if value is not None
+ )
+
+ return default
+
+
+def default_pim_interface_policies(existing, jp_bidir):
+ commands = []
+
+ if jp_bidir:
+ if existing.get("jp_policy_in") or existing.get("jp_policy_out"):
+ if existing.get("jp_type_in") == "prefix":
+ command = "no ip pim jp-policy prefix-list {0}".format(
+ existing.get("jp_policy_in")
+ )
+ if command:
+ commands.append(command)
+
+ elif not jp_bidir:
+ command = None
+ for k in existing:
+ if k == "jp_policy_in":
+ if existing.get("jp_policy_in"):
+ if existing.get("jp_type_in") == "prefix":
+ command = "no ip pim jp-policy prefix-list {0} in".format(
+ existing.get("jp_policy_in")
+ )
+ else:
+ command = "no ip pim jp-policy {0} in".format(
+ existing.get("jp_policy_in")
+ )
+ elif k == "jp_policy_out":
+ if existing.get("jp_policy_out"):
+ if existing.get("jp_type_out") == "prefix":
+ command = "no ip pim jp-policy prefix-list {0} out".format(
+ existing.get("jp_policy_out")
+ )
+ else:
+ command = "no ip pim jp-policy {0} out".format(
+ existing.get("jp_policy_out")
+ )
+ if command:
+ commands.append(command)
+ command = None
+
+ if existing.get("neighbor_policy"):
+ command = "no ip pim neighbor-policy"
+ commands.append(command)
+
+ return commands
+
+
+def config_pim_interface_defaults(existing, jp_bidir, isauth):
+ command = []
+
+ # returns a dict
+ defaults = get_pim_interface_defaults()
+ delta = dict(set(defaults.items()).difference(existing.items()))
+ if delta:
+ # returns a list
+ command = config_pim_interface(delta, existing, jp_bidir, isauth)
+ comm = default_pim_interface_policies(existing, jp_bidir)
+ if comm:
+ for each in comm:
+ command.append(each)
+
+ return command
+
+
+def normalize_proposed_values(proposed):
+ keys = proposed.keys()
+ if "bfd" in keys:
+ # bfd is a tri-state string: enable, disable, default
+ proposed["bfd"] = proposed["bfd"].lower()
+ if "hello_interval" in keys:
+ proposed["hello_interval"] = str(proposed["hello_interval"] * 1000)
+
+
+def main():
+ argument_spec = dict(
+ interface=dict(type="str", required=True),
+ sparse=dict(type="bool", default=False),
+ dr_prio=dict(type="str"),
+ hello_auth_key=dict(type="str", no_log=True),
+ hello_interval=dict(type="int"),
+ jp_policy_out=dict(type="str"),
+ jp_policy_in=dict(type="str"),
+ jp_type_out=dict(type="str", choices=["prefix", "routemap"]),
+ jp_type_in=dict(type="str", choices=["prefix", "routemap"]),
+ bfd=dict(type="str", choices=["enable", "disable", "default"]),
+ border=dict(type="bool", default=False),
+ neighbor_policy=dict(type="str"),
+ neighbor_type=dict(type="str", choices=["prefix", "routemap"]),
+ state=dict(
+ type="str",
+ default="present",
+ choices=["absent", "default", "present"],
+ ),
+ )
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ state = module.params["state"]
+ interface = module.params["interface"]
+ jp_type_in = module.params["jp_type_in"]
+ jp_type_out = module.params["jp_type_out"]
+ jp_policy_in = module.params["jp_policy_in"]
+ jp_policy_out = module.params["jp_policy_out"]
+ neighbor_policy = module.params["neighbor_policy"]
+ neighbor_type = module.params["neighbor_type"]
+
+ intf_type = get_interface_type(interface)
+ if get_interface_mode(interface, intf_type, module) == "layer2":
+ module.fail_json(msg="this module only works on Layer 3 interfaces.")
+
+ if jp_policy_in:
+ if not jp_type_in:
+ module.fail_json(
+ msg="jp_type_in required when using jp_policy_in."
+ )
+ if jp_policy_out:
+ if not jp_type_out:
+ module.fail_json(
+ msg="jp_type_out required when using jp_policy_out."
+ )
+ if neighbor_policy:
+ if not neighbor_type:
+ module.fail_json(
+ msg="neighbor_type required when using neighbor_policy."
+ )
+
+ get_existing = get_pim_interface(module, interface)
+ existing, jp_bidir, isauth = local_existing(get_existing)
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ proposed = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+ normalize_proposed_values(proposed)
+
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ commands = []
+ if state == "present":
+ if delta:
+ command = config_pim_interface(delta, existing, jp_bidir, isauth)
+ if command:
+ commands.append(command)
+ elif state == "default" or state == "absent":
+ defaults = config_pim_interface_defaults(existing, jp_bidir, isauth)
+ if defaults:
+ commands.append(defaults)
+
+ if commands:
+ commands.insert(0, ["interface {0}".format(interface)])
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_rp_address.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_rp_address.py
new file mode 100644
index 00000000..70c955c8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_pim_rp_address.py
@@ -0,0 +1,256 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_pim_rp_address
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages configuration of an PIM static RP address instance.
+description:
+- Manages configuration of an Protocol Independent Multicast (PIM) static rendezvous
+ point (RP) address instance.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) is currently not supported on all platforms.
+options:
+ rp_address:
+ description:
+ - Configures a Protocol Independent Multicast (PIM) static rendezvous point (RP)
+ address. Valid values are unicast addresses.
+ required: true
+ type: str
+ group_list:
+ description:
+ - Group range for static RP. Valid values are multicast addresses.
+ type: str
+ prefix_list:
+ description:
+ - Prefix list policy for static RP. Valid values are prefix-list policy names.
+ type: str
+ route_map:
+ description:
+ - Route map policy for static RP. Valid values are route-map policy names.
+ type: str
+ bidir:
+ description:
+ - Group range is treated in PIM bidirectional mode.
+ type: bool
+ state:
+ description:
+ - Specify desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+EXAMPLES = """
+- cisco.nxos.nxos_pim_rp_address:
+ rp_address: 10.1.1.20
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["router bgp 65535", "vrf test", "router-id 192.0.2.1"]
+"""
+
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+
+def get_existing(module, args, gl):
+ existing = {}
+ config = str(get_config(module))
+ address = module.params["rp_address"]
+
+ pim_address_re = r"ip pim rp-address (?P<value>.*)$"
+ for line in re.findall(pim_address_re, config, re.M):
+
+ values = line.split()
+ if values[0] != address:
+ continue
+ if gl and "group-list" not in line:
+ continue
+ elif not gl and "group-list" in line:
+ if "224.0.0.0/4" not in line: # ignore default group-list
+ continue
+
+ existing["bidir"] = existing.get("bidir") or "bidir" in line
+ if len(values) > 2:
+ value = values[2]
+ if values[1] == "route-map":
+ existing["route_map"] = value
+ elif values[1] == "prefix-list":
+ existing["prefix_list"] = value
+ elif values[1] == "group-list":
+ if value != "224.0.0.0/4": # ignore default group-list
+ existing["group_list"] = value
+
+ return existing
+
+
+def state_present(module, existing, proposed, candidate):
+ address = module.params["rp_address"]
+ command = "ip pim rp-address {0}".format(address)
+ if module.params["group_list"] and not proposed.get("group_list"):
+ command += " group-list " + module.params["group_list"]
+ if module.params["prefix_list"]:
+ if not proposed.get("prefix_list"):
+ command += " prefix-list " + module.params["prefix_list"]
+ if module.params["route_map"]:
+ if not proposed.get("route_map"):
+ command += " route-map " + module.params["route_map"]
+ commands = build_command(proposed, command)
+ if commands:
+ candidate.add(commands, parents=[])
+
+
+def build_command(param_dict, command):
+ for param in ["group_list", "prefix_list", "route_map"]:
+ if param_dict.get(param):
+ command += " {0} {1}".format(
+ param.replace("_", "-"), param_dict.get(param)
+ )
+ if param_dict.get("bidir"):
+ command += " bidir"
+ return [command]
+
+
+def state_absent(module, existing, candidate):
+ address = module.params["rp_address"]
+
+ commands = []
+ command = "no ip pim rp-address {0}".format(address)
+ if module.params["group_list"] == existing.get("group_list"):
+ commands = build_command(existing, command)
+ elif not module.params["group_list"]:
+ commands = [command]
+
+ if commands:
+ candidate.add(commands, parents=[])
+
+
+def get_proposed(pargs, existing):
+ proposed = {}
+
+ for key, value in pargs.items():
+ if key != "rp_address":
+ if str(value).lower() == "true":
+ value = True
+ elif str(value).lower() == "false":
+ value = False
+
+ if existing.get(key) != value:
+ proposed[key] = value
+
+ return proposed
+
+
+def main():
+ argument_spec = dict(
+ rp_address=dict(required=True, type="str"),
+ group_list=dict(required=False, type="str"),
+ prefix_list=dict(required=False, type="str"),
+ route_map=dict(required=False, type="str"),
+ bidir=dict(required=False, type="bool"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ )
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[
+ ["group_list", "route_map"],
+ ["group_list", "prefix_list"],
+ ["route_map", "prefix_list"],
+ ],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False, "commands": [], "warnings": warnings}
+
+ state = module.params["state"]
+
+ args = ["rp_address", "group_list", "prefix_list", "route_map", "bidir"]
+
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ if module.params["group_list"]:
+ existing = get_existing(module, args, True)
+ proposed = get_proposed(proposed_args, existing)
+
+ else:
+ existing = get_existing(module, args, False)
+ proposed = get_proposed(proposed_args, existing)
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present" and (proposed or not existing):
+ state_present(module, existing, proposed, candidate)
+ elif state == "absent" and existing:
+ state_absent(module, existing, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ result["commands"] = candidate
+ result["changed"] = True
+ msgs = load_config(module, candidate, True)
+ if msgs:
+ for item in msgs:
+ if item:
+ if isinstance(item, dict):
+ err_str = item["clierror"]
+ else:
+ err_str = item
+ if "No policy was configured" in err_str:
+ if state == "absent":
+ addr = module.params["rp_address"]
+ new_cmd = "no ip pim rp-address {0}".format(addr)
+ load_config(module, new_cmd)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ping.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ping.py
new file mode 100644
index 00000000..11f08a93
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_ping.py
@@ -0,0 +1,237 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_ping
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Tests reachability using ping from Nexus switch.
+description:
+- Tests reachability using ping from switch to a remote destination.
+- For a general purpose network module, see the M(ansible.netcommon.net_ping) module.
+- For Windows targets, use the M(ansible.windows.win_ping) module instead.
+- For targets running Python, use the M(ansible.builtin.ping) module instead.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+options:
+ dest:
+ description:
+ - IP address or hostname (resolvable by switch) of remote node.
+ required: true
+ type: str
+ count:
+ description:
+ - Number of packets to send.
+ default: 5
+ type: int
+ source:
+ description:
+ - Source IP Address or hostname (resolvable by switch)
+ type: str
+ vrf:
+ description:
+ - Outgoing VRF.
+ type: str
+ state:
+ description:
+ - Determines if the expected result is success or fail.
+ choices:
+ - absent
+ - present
+ default: present
+ type: str
+notes:
+- For a general purpose network module, see the M(ansible.netcommon.net_ping) module.
+- For Windows targets, use the M(ansible.windows.win_ping) module instead.
+- For targets running Python, use the M(ansible.builtin.ping) module instead.
+"""
+
+EXAMPLES = """
+- name: Test reachability to 8.8.8.8 using mgmt vrf
+ cisco.nxos.nxos_ping:
+ dest: 8.8.8.8
+ vrf: management
+ host: 68.170.147.165
+
+- name: Test reachability to a few different public IPs using mgmt vrf
+ cisco.nxos.nxos_ping:
+ dest: nxos_ping
+ vrf: management
+ host: 68.170.147.165
+ with_items:
+ - 8.8.8.8
+ - 4.4.4.4
+ - 198.6.1.4
+"""
+
+RETURN = """
+commands:
+ description: Show the command sent
+ returned: always
+ type: list
+ sample: ["ping 8.8.8.8 count 2 vrf management"]
+rtt:
+ description: Show RTT stats
+ returned: always
+ type: dict
+ sample: {"avg": 6.264, "max": 6.564, "min": 5.978}
+packets_rx:
+ description: Packets successfully received
+ returned: always
+ type: int
+ sample: 2
+packets_tx:
+ description: Packets successfully transmitted
+ returned: always
+ type: int
+ sample: 2
+packet_loss:
+ description: Percentage of packets lost
+ returned: always
+ type: str
+ sample: "0.00%"
+"""
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_summary(results_list, reference_point):
+ summary_string = results_list[reference_point + 1]
+ summary_list = summary_string.split(",")
+
+ summary = dict(
+ packets_tx=int(summary_list[0].split("packets")[0].strip()),
+ packets_rx=int(summary_list[1].split("packets")[0].strip()),
+ packet_loss=summary_list[2].split("packet")[0].strip(),
+ )
+
+ if "bytes from" not in results_list[reference_point - 2]:
+ ping_pass = False
+ else:
+ ping_pass = True
+
+ return summary, ping_pass
+
+
+def get_rtt(results_list, packet_loss, location):
+ rtt = dict(min=None, avg=None, max=None)
+
+ if packet_loss != "100.00%":
+ rtt_string = results_list[location]
+ base = rtt_string.split("=")[1]
+ rtt_list = base.split("/")
+
+ rtt["min"] = float(rtt_list[0].lstrip())
+ rtt["avg"] = float(rtt_list[1])
+ rtt["max"] = float(rtt_list[2][:-3])
+
+ return rtt
+
+
+def get_statistics_summary_line(response_as_list):
+ for each in response_as_list:
+ if "---" in each:
+ index = response_as_list.index(each)
+ return index
+
+
+def get_ping_results(command, module):
+ cmd = {"command": command, "output": "text"}
+ ping = run_commands(module, [cmd])[0]
+
+ if not ping:
+ module.fail_json(
+ msg="An unexpected error occurred. Check all params.",
+ command=command,
+ destination=module.params["dest"],
+ vrf=module.params["vrf"],
+ source=module.params["source"],
+ )
+
+ elif "can't bind to address" in ping:
+ module.fail_json(msg="Can't bind to source address.", command=command)
+ elif "bad context" in ping:
+ module.fail_json(
+ msg="Wrong VRF name inserted.",
+ command=command,
+ vrf=module.params["vrf"],
+ )
+ else:
+ splitted_ping = ping.split("\n")
+ reference_point = get_statistics_summary_line(splitted_ping)
+ summary, ping_pass = get_summary(splitted_ping, reference_point)
+ rtt = get_rtt(
+ splitted_ping, summary["packet_loss"], reference_point + 2
+ )
+
+ return (summary, rtt, ping_pass)
+
+
+def main():
+ argument_spec = dict(
+ dest=dict(required=True),
+ count=dict(required=False, default=5, type="int"),
+ vrf=dict(required=False),
+ source=dict(required=False),
+ state=dict(
+ required=False, choices=["present", "absent"], default="present"
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ destination = module.params["dest"]
+ state = module.params["state"]
+
+ ping_command = "ping {0}".format(destination)
+ for command in ["count", "source", "vrf"]:
+ arg = module.params[command]
+ if arg:
+ ping_command += " {0} {1}".format(command, arg)
+
+ summary, rtt, ping_pass = get_ping_results(ping_command, module)
+
+ results = summary
+ results["rtt"] = rtt
+ results["commands"] = [ping_command]
+
+ if ping_pass and state == "absent":
+ module.fail_json(msg="Ping succeeded unexpectedly")
+ elif not ping_pass and state == "present":
+ module.fail_json(msg="Ping failed unexpectedly")
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_reboot.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_reboot.py
new file mode 100644
index 00000000..89fcb897
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_reboot.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_reboot
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Reboot a network device.
+description:
+- Reboot a network device.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- The module will fail due to timeout issues, but the reboot will be performed anyway.
+options:
+ confirm:
+ description:
+ - Safeguard boolean. Set to true if you're sure you want to reboot.
+ required: false
+ default: false
+ type: bool
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_reboot:
+ confirm: true
+"""
+
+RETURN = """
+rebooted:
+ description: Whether the device was instructed to reboot.
+ returned: success
+ type: bool
+ sample: true
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def reboot(module):
+ cmds = "terminal dont-ask ; reload"
+ opts = {"ignore_timeout": True}
+ load_config(module, cmds, False, opts)
+
+
+def main():
+ argument_spec = dict(confirm=dict(default=False, type="bool"))
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = dict(changed=False, warnings=warnings)
+
+ if module.params["confirm"]:
+ if not module.check_mode:
+ reboot(module)
+ results["changed"] = True
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rollback.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rollback.py
new file mode 100644
index 00000000..fae27263
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rollback.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_rollback
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Set a checkpoint or rollback to a checkpoint.
+description:
+- This module offers the ability to set a configuration checkpoint file or rollback
+ to a configuration checkpoint file on Cisco NXOS switches.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Sometimes C(transport=nxapi) may cause a timeout error.
+options:
+ checkpoint_file:
+ description:
+ - Name of checkpoint file to create. Mutually exclusive with rollback_to.
+ type: str
+ rollback_to:
+ description:
+ - Name of checkpoint file to rollback to. Mutually exclusive with checkpoint_file.
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_rollback:
+ checkpoint_file: backup.cfg
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+ host: '{{ inventory_hostname }}'
+- cisco.nxos.nxos_rollback:
+ rollback_to: backup.cfg
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+ host: '{{ inventory_hostname }}'
+"""
+
+RETURN = """
+filename:
+ description: The filename of the checkpoint/rollback file.
+ returned: success
+ type: str
+ sample: 'backup.cfg'
+status:
+ description: Which operation took place and whether it was successful.
+ returned: success
+ type: str
+ sample: 'rollback executed'
+"""
+
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ run_commands,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def checkpoint(filename, module):
+ commands = [
+ {"command": "terminal dont-ask", "output": "text"},
+ {"command": "checkpoint file %s" % filename, "output": "text"},
+ ]
+ run_commands(module, commands)
+
+
+def rollback(filename, module):
+ commands = [
+ {
+ "command": "rollback running-config file %s" % filename,
+ "output": "text",
+ }
+ ]
+ run_commands(module, commands)
+
+
+def main():
+ argument_spec = dict(
+ checkpoint_file=dict(required=False), rollback_to=dict(required=False)
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[["checkpoint_file", "rollback_to"]],
+ supports_check_mode=False,
+ )
+
+ checkpoint_file = module.params["checkpoint_file"]
+ rollback_to = module.params["rollback_to"]
+
+ status = None
+ filename = None
+ changed = False
+
+ if checkpoint_file:
+ checkpoint(checkpoint_file, module)
+ status = "checkpoint file created"
+ elif rollback_to:
+ rollback(rollback_to, module)
+ status = "rollback executed"
+ changed = True
+ filename = rollback_to or checkpoint_file
+
+ module.exit_json(changed=changed, status=status, filename=filename)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rpm.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rpm.py
new file mode 100644
index 00000000..ac813c9a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_rpm.py
@@ -0,0 +1,421 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_rpm
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Install patch or feature rpms on Cisco NX-OS devices.
+description:
+- Install software maintenance upgrade (smu) RPMS and 3rd party RPMS on Cisco NX-OS
+ devices.
+version_added: 1.0.0
+author: Sai Chintalapudi (@saichint)
+notes:
+- Tested against NXOSv 7.0(3)I2(5), 7.0(3)I4(6), 7.0(3)I5(3), 7.0(3)I6(1), 7.0(3)I7(3)
+- For patches, the minimum platform version needed is 7.0(3)I2(5)
+- For feature rpms, the minimum platform version needed is 7.0(3)I6(1)
+- The module manages the entire RPM lifecycle (Add, activate, commit, deactivate,
+ remove)
+- For reload patches, this module is NOT idempotent until the patch is committed.
+options:
+ pkg:
+ description:
+ - Name of the RPM package.
+ type: str
+ file_system:
+ description:
+ - The remote file system of the device. If omitted, devices that support a file_system
+ parameter will use their default values.
+ default: bootflash
+ type: str
+ aggregate:
+ description:
+ - List of RPM/patch definitions.
+ type: list
+ elements: dict
+ suboptions:
+ pkg:
+ description:
+ - Name of the RPM package.
+ required: True
+ type: str
+ file_system:
+ description:
+ - The remote file system of the device. If omitted, devices that support a file_system
+ parameter will use their default values.
+ type: str
+ state:
+ description:
+ - If the state is present, the rpm will be installed, If the state is absent,
+ it will be removed.
+ choices:
+ - present
+ - absent
+ type: str
+ state:
+ description:
+ - If the state is present, the rpm will be installed, If the state is absent,
+ it will be removed.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_rpm:
+ pkg: nxos.sample-n9k_ALL-1.0.0-7.0.3.I7.3.lib32_n9000.rpm
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["install add bootflash:nxos.sample-n9k_ALL-1.0.0-7.0.3.I7.3.lib32_n9000.rpm forced",
+ "install activate nxos.sample-n9k_ALL-1.0.0-7.0.3.I7.3.lib32_n9000 forced",
+ "install commit nxos.sample-n9k_ALL-1.0.0-7.0.3.I7.3.lib32_n9000"]
+"""
+
+
+import time
+
+from copy import deepcopy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+
+
+def execute_show_command(command, module):
+ iteration = 0
+ cmds = [{"command": command, "output": "text"}]
+
+ while iteration < 10:
+ body = run_commands(module, cmds)[0]
+ if body:
+ return body
+ else:
+ time.sleep(2)
+ iteration += 1
+
+
+def remote_file_exists(module, dst, file_system):
+ command = "dir {0}:/{1}".format(file_system, dst)
+ body = execute_show_command(command, module)
+ if "No such file" in body:
+ return False
+ return True
+
+
+def config_cmd_operation(module, cmd):
+ iteration = 0
+ while iteration < 10:
+ msg = load_config(module, [cmd], True)
+ if msg:
+ if (
+ "another install operation is in progress" in msg[0].lower()
+ or "failed" in msg[0].lower()
+ ):
+ time.sleep(2)
+ iteration += 1
+ else:
+ return
+ else:
+ return
+
+
+def validate_operation(module, show_cmd, cfg_cmd, pkg, pkg_not_present):
+ iteration = 0
+ while iteration < 10:
+ body = execute_show_command(show_cmd, module)
+ if pkg_not_present:
+ if pkg not in body:
+ return
+ else:
+ if pkg in body:
+ return
+ time.sleep(2)
+ iteration += 1
+
+ err = 'Operation "{0}" Failed'.format(cfg_cmd)
+ module.fail_json(msg=err)
+
+
+def add_operation(module, show_cmd, file_system, full_pkg, pkg):
+ cmd = "install add {0}:{1}".format(file_system, full_pkg)
+ config_cmd_operation(module, cmd)
+ validate_operation(module, show_cmd, cmd, pkg, False)
+ return cmd
+
+
+def activate_operation(module, show_cmd, pkg):
+ cmd = "install activate {0} forced".format(pkg)
+ config_cmd_operation(module, cmd)
+ validate_operation(module, show_cmd, cmd, pkg, False)
+ return cmd
+
+
+def activate_reload(module, pkg, flag):
+ iteration = 0
+ if flag:
+ cmd = "install activate {0} forced".format(pkg)
+ else:
+ cmd = "install deactivate {0} forced".format(pkg)
+ opts = {"ignore_timeout": True}
+ while iteration < 10:
+ msg = load_config(module, [cmd], True, opts)
+ if msg:
+ if isinstance(msg[0], int):
+ if msg[0] == -32603:
+ return cmd
+ elif isinstance(msg[0], str):
+ if "socket is closed" in msg[0].lower():
+ return cmd
+ if (
+ "another install operation is in progress"
+ in msg[0].lower()
+ or "failed" in msg[0].lower()
+ ):
+ time.sleep(2)
+ iteration += 1
+
+
+def commit_operation(module, show_cmd, pkg, flag):
+ cmd = "install commit {0}".format(pkg)
+ config_cmd_operation(module, cmd)
+ validate_operation(module, show_cmd, cmd, pkg, flag)
+ return cmd
+
+
+def deactivate_operation(module, show_cmd, pkg, flag):
+ cmd = "install deactivate {0} forced".format(pkg)
+ config_cmd_operation(module, cmd)
+ validate_operation(module, show_cmd, cmd, pkg, flag)
+ return cmd
+
+
+def terminal_operation(module, config):
+ if config:
+ cmd = "terminal dont-ask"
+ else:
+ cmd = "no terminal dont-ask"
+ config_cmd_operation(module, cmd)
+ return cmd
+
+
+def remove_operation(module, show_cmd, pkg):
+ commands = []
+ commands.append(terminal_operation(module, True))
+ cmd = "install remove {0} forced".format(pkg)
+ config_cmd_operation(module, cmd)
+ validate_operation(module, show_cmd, cmd, pkg, True)
+ commands.append(cmd)
+ commands.append(terminal_operation(module, False))
+ return commands
+
+
+def install_remove_rpm(module, full_pkg, file_system, state):
+ commands = []
+ reload_patch = False
+
+ splitted_pkg = full_pkg.split(".")
+ pkg = ".".join(splitted_pkg[0:-1])
+
+ show_inactive = "show install inactive"
+ show_active = "show install active"
+ show_commit = "show install committed"
+ show_patches = "show install patches"
+ show_pkg_info = "show install pkg-info {0}".format(pkg)
+
+ if state == "present":
+ inactive_body = execute_show_command(show_inactive, module)
+ active_body = execute_show_command(show_active, module)
+
+ if pkg not in inactive_body and pkg not in active_body:
+ commands.append(
+ add_operation(
+ module, show_inactive, file_system, full_pkg, pkg
+ )
+ )
+
+ patch_type_body = execute_show_command(show_pkg_info, module)
+ if patch_type_body and "Patch Type : reload" in patch_type_body:
+ # This is reload smu/patch rpm
+ reload_patch = True
+
+ if pkg not in active_body:
+ if reload_patch:
+ commands.append(activate_reload(module, pkg, True))
+ return commands
+ else:
+ commands.append(activate_operation(module, show_active, pkg))
+
+ commit_body = execute_show_command(show_commit, module)
+ if pkg not in commit_body:
+ patch_body = execute_show_command(show_patches, module)
+ if pkg in patch_body:
+ # This is smu/patch rpm
+ commands.append(
+ commit_operation(module, show_commit, pkg, False)
+ )
+ else:
+ err = 'Operation "install activate {0} forced" Failed'.format(
+ pkg
+ )
+ module.fail_json(msg=err)
+
+ else:
+ commit_body = execute_show_command(show_commit, module)
+ active_body = execute_show_command(show_active, module)
+
+ patch_type_body = execute_show_command(show_pkg_info, module)
+ if patch_type_body and "Patch Type : reload" in patch_type_body:
+ # This is reload smu/patch rpm
+ reload_patch = True
+
+ if pkg in commit_body and pkg in active_body:
+ if reload_patch:
+ commands.append(activate_reload(module, pkg, False))
+ return commands
+ else:
+ commands.append(
+ deactivate_operation(module, show_active, pkg, True)
+ )
+ commit_body = execute_show_command(show_commit, module)
+ if pkg in commit_body:
+ # This is smu/patch rpm
+ commands.append(
+ commit_operation(module, show_commit, pkg, True)
+ )
+ commands.extend(remove_operation(module, show_inactive, pkg))
+
+ elif pkg in commit_body:
+ # This is smu/patch rpm
+ commands.append(commit_operation(module, show_commit, pkg, True))
+ commands.extend(remove_operation(module, show_inactive, pkg))
+
+ elif pkg in active_body:
+ # This is smu/patch rpm
+ if reload_patch:
+ commands.append(activate_reload(module, pkg, False))
+ return commands
+ else:
+ commands.append(
+ deactivate_operation(module, show_inactive, pkg, False)
+ )
+ commands.extend(remove_operation(module, show_inactive, pkg))
+
+ else:
+ inactive_body = execute_show_command(show_inactive, module)
+ if pkg in inactive_body:
+ commands.extend(remove_operation(module, show_inactive, pkg))
+
+ return commands
+
+
+def main():
+ element_spec = dict(
+ pkg=dict(type="str"),
+ file_system=dict(type="str", default="bootflash"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+ aggregate_spec["pkg"] = dict(required=True)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec)
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ required_one_of = [["pkg", "aggregate"]]
+ mutually_exclusive = [["pkg", "aggregate"]]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=required_one_of,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=False,
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ aggregate = module.params.get("aggregate")
+ objects = []
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ objects.append(d)
+ else:
+ objects.append(
+ {
+ "pkg": module.params["pkg"],
+ "file_system": module.params["file_system"],
+ "state": module.params["state"],
+ }
+ )
+
+ for obj in objects:
+ if obj["state"] == "present":
+ remote_exists = remote_file_exists(
+ module, obj["pkg"], file_system=obj["file_system"]
+ )
+
+ if not remote_exists:
+ module.fail_json(
+ msg="The requested package doesn't exist on the device"
+ )
+
+ cmds = install_remove_rpm(
+ module, obj["pkg"], obj["file_system"], obj["state"]
+ )
+
+ if cmds:
+ results["changed"] = True
+ results["commands"].extend(cmds)
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_smu.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_smu.py
new file mode 100644
index 00000000..4f674a6d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_smu.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_smu
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-10-01) Perform SMUs on Cisco NX-OS devices.
+description:
+- Perform software maintenance upgrades (SMUs) on Cisco NX-OS devices.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+deprecated:
+ alternative: nxos_rpm
+ why: Updated modules released with more functionality.
+ removed_at_date: '2022-10-01'
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- The module can only activate and commit a package, not remove or deactivate it.
+- Use C(transport=nxapi) to avoid connection timeout
+options:
+ pkg:
+ description:
+ - Name of the remote package.
+ required: true
+ type: str
+ file_system:
+ description:
+ - The remote file system of the device. If omitted, devices that support a file_system
+ parameter will use their default values.
+ default: 'bootflash:'
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_smu:
+ pkg: nxos.CSCuz65185-n9k_EOR-1.0.0-7.0.3.I2.2d.lib32_n9000.rpm
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["install add bootflash:nxos.CSCuz65185-n9k_EOR-1.0.0-7.0.3.I2.2d.lib32_n9000.rpm",
+ "install activate bootflash:nxos.CSCuz65185-n9k_EOR-1.0.0-7.0.3.I2.2d.lib32_n9000.rpm force",
+ "install commit bootflash:nxos.CSCuz65185-n9k_EOR-1.0.0-7.0.3.I2.2d.lib32_n9000.rpm"]
+"""
+
+
+import time
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ cmds = [{"command": command, "output": "text"}]
+
+ return run_commands(module, cmds)
+
+
+def remote_file_exists(module, dst, file_system="bootflash:"):
+ command = "dir {0}/{1}".format(file_system, dst)
+ body = execute_show_command(command, module)
+ if "No such file" in body[0]:
+ return False
+ return True
+
+
+def apply_patch(module, commands):
+ for command in commands:
+ load_config(module, [command])
+ time.sleep(5)
+
+
+def get_commands(module, pkg, file_system):
+ commands = []
+ splitted_pkg = pkg.split(".")
+ fixed_pkg = ".".join(splitted_pkg[0:-1])
+
+ command = "show install inactive"
+ inactive_body = execute_show_command(command, module)
+
+ command = "show install active"
+ active_body = execute_show_command(command, module)
+
+ if fixed_pkg not in inactive_body[0] and fixed_pkg not in active_body[0]:
+ commands.append("install add {0}{1}".format(file_system, pkg))
+
+ if fixed_pkg not in active_body[0]:
+ commands.append("install activate {0} force".format(pkg))
+ command = "show install committed"
+ install_body = execute_show_command(command, module)
+ if fixed_pkg not in install_body[0]:
+ commands.append("install commit {0}".format(pkg))
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ pkg=dict(required=True),
+ file_system=dict(required=False, default="bootflash:"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ pkg = module.params["pkg"]
+ file_system = module.params["file_system"]
+ remote_exists = remote_file_exists(module, pkg, file_system=file_system)
+
+ if not remote_exists:
+ module.fail_json(
+ msg="The requested package doesn't exist on the device"
+ )
+
+ commands = get_commands(module, pkg, file_system)
+ if commands:
+ results["changed"] = True
+ if not module.check_mode:
+ apply_patch(module, commands)
+ if "configure" in commands:
+ commands.pop(0)
+ results["commands"] = commands
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snapshot.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snapshot.py
new file mode 100644
index 00000000..864977f6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snapshot.py
@@ -0,0 +1,425 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+
+DOCUMENTATION = """
+module: nxos_snapshot
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manage snapshots of the running states of selected features.
+description:
+- Create snapshots of the running states of selected features, add new show commands
+ for snapshot creation, delete and compare existing snapshots.
+version_added: 1.0.0
+author:
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(transport=cli) may cause timeout errors.
+- The C(element_key1) and C(element_key2) parameter specify the tags used to distinguish
+ among row entries. In most cases, only the element_key1 parameter needs to specified
+ to be able to distinguish among row entries.
+- C(action=compare) will always store a comparison report on a local file.
+options:
+ action:
+ description:
+ - Define what snapshot action the module would perform.
+ required: true
+ choices:
+ - add
+ - compare
+ - create
+ - delete
+ - delete_all
+ type: str
+ snapshot_name:
+ description:
+ - Snapshot name, to be used when C(action=create) or C(action=delete).
+ type: str
+ description:
+ description:
+ - Snapshot description to be used when C(action=create).
+ type: str
+ snapshot1:
+ description:
+ - First snapshot to be used when C(action=compare).
+ type: str
+ snapshot2:
+ description:
+ - Second snapshot to be used when C(action=compare).
+ type: str
+ comparison_results_file:
+ description:
+ - Name of the file where snapshots comparison will be stored when C(action=compare).
+ type: str
+ compare_option:
+ description:
+ - Snapshot options to be used when C(action=compare).
+ choices:
+ - summary
+ - ipv4routes
+ - ipv6routes
+ type: str
+ section:
+ description:
+ - Used to name the show command output, to be used when C(action=add).
+ type: str
+ show_command:
+ description:
+ - Specify a new show command, to be used when C(action=add).
+ type: str
+ row_id:
+ description:
+ - Specifies the tag of each row entry of the show command's XML output, to be
+ used when C(action=add).
+ type: str
+ element_key1:
+ description:
+ - Specify the tags used to distinguish among row entries, to be used when C(action=add).
+ type: str
+ element_key2:
+ description:
+ - Specify the tags used to distinguish among row entries, to be used when C(action=add).
+ type: str
+ save_snapshot_locally:
+ description:
+ - Specify to locally store a new created snapshot, to be used when C(action=create).
+ type: bool
+ default: no
+ path:
+ description:
+ - Specify the path of the file where new created snapshot or snapshots comparison
+ will be stored, to be used when C(action=create) and C(save_snapshot_locally=true)
+ or C(action=compare).
+ default: ./
+ type: str
+"""
+
+EXAMPLES = """
+# Create a snapshot and store it locally
+- cisco.nxos.nxos_snapshot:
+ action: create
+ snapshot_name: test_snapshot
+ description: Done with Ansible
+ save_snapshot_locally: true
+ path: /home/user/snapshots/
+
+# Delete a snapshot
+- cisco.nxos.nxos_snapshot:
+ action: delete
+ snapshot_name: test_snapshot
+
+# Delete all existing snapshots
+- cisco.nxos.nxos_snapshot:
+ action: delete_all
+
+# Add a show command for snapshots creation
+- cisco.nxos.nxos_snapshot:
+ section: myshow
+ show_command: show ip interface brief
+ row_id: ROW_intf
+ element_key1: intf-name
+
+# Compare two snapshots
+- cisco.nxos.nxos_snapshot:
+ action: compare
+ snapshot1: pre_snapshot
+ snapshot2: post_snapshot
+ comparison_results_file: compare_snapshots.txt
+ compare_option: summary
+ path: ../snapshot_reports/
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: verbose mode
+ type: list
+ sample: ["snapshot create post_snapshot Post-snapshot"]
+"""
+
+import os
+import re
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+
+
+def execute_show_command(command, module):
+ command = [{"command": command, "output": "text"}]
+
+ return run_commands(module, command)
+
+
+def get_existing(module):
+ existing = []
+ command = "show snapshots"
+
+ body = execute_show_command(command, module)[0]
+ if body:
+ split_body = body.splitlines()
+ snapshot_regex = (
+ r"(?P<name>\S+)\s+(?P<date>\w+\s+\w+\s+\d+\s+\d+"
+ r":\d+:\d+\s+\d+)\s+(?P<description>.*)"
+ )
+ for snapshot in split_body:
+ temp = {}
+ try:
+ match_snapshot = re.match(snapshot_regex, snapshot, re.DOTALL)
+ snapshot_group = match_snapshot.groupdict()
+ temp["name"] = snapshot_group["name"]
+ temp["date"] = snapshot_group["date"]
+ temp["description"] = snapshot_group["description"]
+ existing.append(temp)
+ except AttributeError:
+ pass
+
+ return existing
+
+
+def action_create(module, existing_snapshots):
+ commands = list()
+ exist = False
+ for snapshot in existing_snapshots:
+ if module.params["snapshot_name"] == snapshot["name"]:
+ exist = True
+
+ if exist is False:
+ commands.append(
+ "snapshot create {0} {1}".format(
+ module.params["snapshot_name"], module.params["description"]
+ )
+ )
+
+ return commands
+
+
+def action_add(module, existing_snapshots):
+ commands = list()
+ command = "show snapshot sections"
+ sections = []
+ body = execute_show_command(command, module)[0]
+
+ if body:
+ section_regex = r".*\[(?P<section>\S+)\].*"
+ split_body = body.split("\n\n")
+ for section in split_body:
+ temp = {}
+ for line in section.splitlines():
+ try:
+ match_section = re.match(section_regex, section, re.DOTALL)
+ temp["section"] = match_section.groupdict()["section"]
+ except (AttributeError, KeyError):
+ pass
+
+ if "show command" in line:
+ temp["show_command"] = line.split("show command: ")[1]
+ elif "row id" in line:
+ temp["row_id"] = line.split("row id: ")[1]
+ elif "key1" in line:
+ temp["element_key1"] = line.split("key1: ")[1]
+ elif "key2" in line:
+ temp["element_key2"] = line.split("key2: ")[1]
+
+ if temp:
+ sections.append(temp)
+
+ proposed = {
+ "section": module.params["section"],
+ "show_command": module.params["show_command"],
+ "row_id": module.params["row_id"],
+ "element_key1": module.params["element_key1"],
+ "element_key2": module.params["element_key2"] or "-",
+ }
+
+ if proposed not in sections:
+ if module.params["element_key2"]:
+ commands.append(
+ 'snapshot section add {0} "{1}" {2} {3} {4}'.format(
+ module.params["section"],
+ module.params["show_command"],
+ module.params["row_id"],
+ module.params["element_key1"],
+ module.params["element_key2"],
+ )
+ )
+ else:
+ commands.append(
+ 'snapshot section add {0} "{1}" {2} {3}'.format(
+ module.params["section"],
+ module.params["show_command"],
+ module.params["row_id"],
+ module.params["element_key1"],
+ )
+ )
+
+ return commands
+
+
+def action_compare(module, existing_snapshots):
+ command = "show snapshot compare {0} {1}".format(
+ module.params["snapshot1"], module.params["snapshot2"]
+ )
+
+ if module.params["compare_option"]:
+ command += " {0}".format(module.params["compare_option"])
+
+ body = execute_show_command(command, module)[0]
+ return body
+
+
+def action_delete(module, existing_snapshots):
+ commands = list()
+
+ exist = False
+ for snapshot in existing_snapshots:
+ if module.params["snapshot_name"] == snapshot["name"]:
+ exist = True
+
+ if exist:
+ commands.append(
+ "snapshot delete {0}".format(module.params["snapshot_name"])
+ )
+
+ return commands
+
+
+def action_delete_all(module, existing_snapshots):
+ commands = list()
+ if existing_snapshots:
+ commands.append("snapshot delete all")
+ return commands
+
+
+def invoke(name, *args, **kwargs):
+ func = globals().get(name)
+ if func:
+ return func(*args, **kwargs)
+
+
+def write_on_file(content, filename, module):
+ path = module.params["path"]
+ if path[-1] != "/":
+ path += "/"
+ filepath = "{0}{1}".format(path, filename)
+ try:
+ report = open(filepath, "w")
+ report.write(content)
+ report.close()
+ except Exception:
+ module.fail_json(msg="Error while writing on file.")
+
+ return filepath
+
+
+def main():
+ argument_spec = dict(
+ action=dict(
+ required=True,
+ choices=["create", "add", "compare", "delete", "delete_all"],
+ ),
+ snapshot_name=dict(type="str"),
+ description=dict(type="str"),
+ snapshot1=dict(type="str"),
+ snapshot2=dict(type="str"),
+ compare_option=dict(choices=["summary", "ipv4routes", "ipv6routes"]),
+ comparison_results_file=dict(type="str"),
+ section=dict(type="str"),
+ show_command=dict(type="str"),
+ row_id=dict(type="str"),
+ element_key1=dict(type="str"),
+ element_key2=dict(type="str"),
+ save_snapshot_locally=dict(type="bool", default=False),
+ path=dict(type="str", default="./"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ required_if = [
+ (
+ "action",
+ "compare",
+ ["snapshot1", "snapshot2", "comparison_results_file"],
+ ),
+ ("action", "create", ["snapshot_name", "description"]),
+ (
+ "action",
+ "add",
+ ["section", "show_command", "row_id", "element_key1"],
+ ),
+ ("action", "delete", ["snapshot_name"]),
+ ]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=required_if,
+ supports_check_mode=True,
+ )
+
+ action = module.params["action"]
+ comparison_results_file = module.params["comparison_results_file"]
+
+ if not os.path.isdir(module.params["path"]):
+ module.fail_json(
+ msg="{0} is not a valid directory name.".format(
+ module.params["path"]
+ )
+ )
+
+ existing_snapshots = invoke("get_existing", module)
+ action_results = invoke("action_%s" % action, module, existing_snapshots)
+
+ result = {"changed": False, "commands": []}
+
+ if not module.check_mode:
+ if action == "compare":
+ result["commands"] = []
+
+ if module.params["path"] and comparison_results_file:
+ snapshot1 = module.params["snapshot1"]
+ snapshot2 = module.params["snapshot2"]
+ compare_option = module.params["compare_option"]
+ command = "show snapshot compare {0} {1}".format(
+ snapshot1, snapshot2
+ )
+ if compare_option:
+ command += " {0}".format(compare_option)
+ content = execute_show_command(command, module)[0]
+ if content:
+ write_on_file(content, comparison_results_file, module)
+ else:
+ if action_results:
+ load_config(module, action_results)
+ result["commands"] = action_results
+ result["changed"] = True
+
+ if (
+ action == "create"
+ and module.params["path"]
+ and module.params["save_snapshot_locally"]
+ ):
+ command = "show snapshot dump {0} | json".format(
+ module.params["snapshot_name"]
+ )
+ content = execute_show_command(command, module)[0]
+ if content:
+ write_on_file(
+ str(content), module.params["snapshot_name"], module
+ )
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_community.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_community.py
new file mode 100644
index 00000000..bb3d2512
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_community.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_snmp_community
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages SNMP community configs.
+description:
+- Manages SNMP community configuration.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+options:
+ community:
+ description:
+ - Case-sensitive community string.
+ required: true
+ type: str
+ access:
+ description:
+ - Access type for community.
+ choices:
+ - ro
+ - rw
+ type: str
+ group:
+ description:
+ - Group to which the community belongs.
+ type: str
+ acl:
+ description:
+ - ACL name to filter snmp requests or keyword 'default'.
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# ensure snmp community is configured
+- cisco.nxos.nxos_snmp_community:
+ community: TESTING7
+ group: network-operator
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["snmp-server community TESTING7 group network-operator"]
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ if "show run" not in command:
+ output = "json"
+ else:
+ output = "text"
+ cmds = [{"command": command, "output": output}]
+
+ body = run_commands(module, cmds)
+ return body
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_snmp_groups(module):
+ data = execute_show_command("show snmp group", module)[0]
+ group_list = []
+
+ try:
+ group_table = data["TABLE_role"]["ROW_role"]
+ for group in group_table:
+ group_list.append(group["role_name"])
+ except (KeyError, AttributeError):
+ pass
+
+ return group_list
+
+
+def get_snmp_community(module, name):
+ command = "show run snmp all | grep word-exp {0}".format(name)
+ data = execute_show_command(command, module)[0]
+ community_dict = {}
+
+ if not data:
+ return community_dict
+
+ community_re = r"snmp-server community (\S+)"
+ mo = re.search(community_re, data)
+ if mo:
+ community_name = mo.group(1)
+ else:
+ return community_dict
+
+ community_dict["group"] = None
+ group_re = r"snmp-server community {0} group (\S+)".format(community_name)
+ mo = re.search(group_re, data)
+ if mo:
+ community_dict["group"] = mo.group(1)
+
+ community_dict["acl"] = None
+ acl_re = r"snmp-server community {0} use-acl (\S+)".format(community_name)
+ mo = re.search(acl_re, data)
+ if mo:
+ community_dict["acl"] = mo.group(1)
+
+ return community_dict
+
+
+def config_snmp_community(delta, community):
+ CMDS = {
+ "group": "snmp-server community {0} group {group}",
+ "acl": "snmp-server community {0} use-acl {acl}",
+ "no_acl": "no snmp-server community {0} use-acl {no_acl}",
+ }
+ commands = []
+ for k in delta.keys():
+ cmd = CMDS.get(k).format(community, **delta)
+ if cmd:
+ if "group" in cmd:
+ commands.insert(0, cmd)
+ else:
+ commands.append(cmd)
+ cmd = None
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ community=dict(required=True, type="str"),
+ access=dict(choices=["ro", "rw"]),
+ group=dict(type="str"),
+ acl=dict(type="str"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=[["access", "group"]],
+ mutually_exclusive=[["access", "group"]],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ access = module.params["access"]
+ group = module.params["group"]
+ community = module.params["community"]
+ acl = module.params["acl"]
+ state = module.params["state"]
+
+ if access:
+ if access == "ro":
+ group = "network-operator"
+ elif access == "rw":
+ group = "network-admin"
+
+ # group check - ensure group being configured exists on the device
+ configured_groups = get_snmp_groups(module)
+
+ if group not in configured_groups:
+ module.fail_json(
+ msg="Group not on switch. Please add before moving forward"
+ )
+
+ existing = get_snmp_community(module, community)
+ args = dict(group=group, acl=acl)
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta.get("acl") == "default":
+ delta.pop("acl")
+ if existing.get("acl"):
+ delta["no_acl"] = existing.get("acl")
+
+ commands = []
+
+ if state == "absent":
+ if existing:
+ command = "no snmp-server community {0}".format(community)
+ commands.append(command)
+ elif state == "present":
+ if delta:
+ command = config_snmp_community(dict(delta), community)
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_contact.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_contact.py
new file mode 100644
index 00000000..eacba94c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_contact.py
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_snmp_contact
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages SNMP contact info.
+description:
+- Manages SNMP contact information.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the contact configuration if it is configured.
+options:
+ contact:
+ description:
+ - Contact information.
+ required: true
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# ensure snmp contact is configured
+- cisco.nxos.nxos_snmp_contact:
+ contact: Test
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["snmp-server contact New_Test"]
+"""
+
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ command = {"command": command, "output": "text"}
+
+ return run_commands(module, command)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_snmp_contact(module):
+ contact = {}
+ contact_regex = r"^\s*snmp-server\scontact\s(?P<contact>.+)$"
+
+ body = execute_show_command("show run snmp", module)[0]
+ match_contact = re.search(contact_regex, body, re.M)
+ if match_contact:
+ contact["contact"] = match_contact.group("contact")
+
+ return contact
+
+
+def main():
+ argument_spec = dict(
+ contact=dict(required=True, type="str"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ contact = module.params["contact"]
+ state = module.params["state"]
+
+ existing = get_snmp_contact(module)
+ commands = []
+
+ if state == "absent":
+ if existing and existing["contact"] == contact:
+ commands.append("no snmp-server contact")
+ elif state == "present":
+ if not existing or existing["contact"] != contact:
+ commands.append("snmp-server contact {0}".format(contact))
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_host.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_host.py
new file mode 100644
index 00000000..15541103
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_host.py
@@ -0,0 +1,521 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_snmp_host
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages SNMP host configuration.
+description:
+- Manages SNMP host configuration parameters.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- C(state=absent) removes the host configuration if it is configured.
+options:
+ snmp_host:
+ description:
+ - IP address of hostname of target host.
+ required: true
+ type: str
+ version:
+ description:
+ - SNMP version. If this is not specified, v1 is used.
+ choices:
+ - v1
+ - v2c
+ - v3
+ type: str
+ v3:
+ description:
+ - Use this when verion is v3. SNMPv3 Security level.
+ choices:
+ - noauth
+ - auth
+ - priv
+ type: str
+ community:
+ description:
+ - Community string or v3 username.
+ type: str
+ udp:
+ description:
+ - UDP port number (0-65535).
+ default: 162
+ type: str
+ snmp_type:
+ description:
+ - type of message to send to host. If this is not specified, trap type is used.
+ choices:
+ - trap
+ - inform
+ type: str
+ vrf:
+ description:
+ - VRF to use to source traffic to source. If state = absent, the vrf is removed.
+ type: str
+ vrf_filter:
+ description:
+ - Name of VRF to filter. If state = absent, the vrf is removed from the filter.
+ type: str
+ src_intf:
+ description:
+ - Source interface. Must be fully qualified interface name. If state = absent,
+ the interface is removed.
+ type: str
+ state:
+ description:
+ - Manage the state of the resource. If state = present, the host is added to the
+ configuration. If only vrf and/or vrf_filter and/or src_intf are given, they
+ will be added to the existing host configuration. If state = absent, the host
+ is removed if community parameter is given. It is possible to remove only vrf
+ and/or src_int and/or vrf_filter by providing only those parameters and no community
+ parameter.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# ensure snmp host is configured
+- cisco.nxos.nxos_snmp_host:
+ snmp_host: 192.0.2.3
+ community: TESTING
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["snmp-server host 192.0.2.3 filter-vrf another_test_vrf"]
+"""
+
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ command = {"command": command, "output": "json"}
+
+ return run_commands(module, command)
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ value = table.get(key)
+ if value:
+ new_dict[new_key] = str(value)
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_snmp_host(host, udp, module):
+ body = execute_show_command("show snmp host", module)
+
+ host_map = {
+ "port": "udp",
+ "version": "version",
+ "level": "v3",
+ "type": "snmp_type",
+ "secname": "community",
+ }
+
+ host_map_5k = {
+ "port": "udp",
+ "version": "version",
+ "sec_level": "v3",
+ "notif_type": "snmp_type",
+ "commun_or_user": "community",
+ }
+
+ resource = {}
+
+ if body:
+ try:
+ resource_table = body[0]["TABLE_host"]["ROW_host"]
+
+ if isinstance(resource_table, dict):
+ resource_table = [resource_table]
+
+ for each in resource_table:
+ key = str(each["host"]) + "_" + str(each["port"]).strip()
+ src = each.get("src_intf")
+ host_resource = apply_key_map(host_map, each)
+
+ if src:
+ host_resource["src_intf"] = src
+ if re.search(r"interface:", src):
+ host_resource["src_intf"] = src.split(":")[1].strip()
+
+ vrf_filt = each.get("TABLE_vrf_filters")
+ if vrf_filt:
+ vrf_filter = (
+ vrf_filt["ROW_vrf_filters"]["vrf_filter"]
+ .split(":")[1]
+ .split(",")
+ )
+ filters = [vrf.strip() for vrf in vrf_filter]
+ host_resource["vrf_filter"] = filters
+
+ vrf = each.get("vrf")
+ if vrf:
+ host_resource["vrf"] = vrf.split(":")[1].strip()
+ resource[key] = host_resource
+ except KeyError:
+ # Handle the 5K case
+ try:
+ resource_table = body[0]["TABLE_hosts"]["ROW_hosts"]
+
+ if isinstance(resource_table, dict):
+ resource_table = [resource_table]
+
+ for each in resource_table:
+ key = (
+ str(each["address"]) + "_" + str(each["port"]).strip()
+ )
+ src = each.get("src_intf")
+ host_resource = apply_key_map(host_map_5k, each)
+
+ if src:
+ host_resource["src_intf"] = src
+ if re.search(r"interface:", src):
+ host_resource["src_intf"] = src.split(":")[
+ 1
+ ].strip()
+
+ vrf = each.get("use_vrf_name")
+ if vrf:
+ host_resource["vrf"] = vrf.strip()
+
+ vrf_filt = each.get("TABLE_filter_vrf")
+ if vrf_filt:
+ vrf_filter = vrf_filt["ROW_filter_vrf"][
+ "filter_vrf_name"
+ ].split(",")
+ filters = [vrf.strip() for vrf in vrf_filter]
+ host_resource["vrf_filter"] = filters
+
+ resource[key] = host_resource
+ except (KeyError, AttributeError, TypeError):
+ return resource
+ except (AttributeError, TypeError):
+ return resource
+
+ find = resource.get(host + "_" + udp)
+
+ if find:
+ fix_find = {}
+ for (key, value) in find.items():
+ if isinstance(value, str):
+ fix_find[key] = value.strip()
+ else:
+ fix_find[key] = value
+ return fix_find
+
+ return {}
+
+
+def remove_snmp_host(host, udp, existing):
+ commands = []
+ if existing["version"] == "v3":
+ existing["version"] = "3"
+ command = "no snmp-server host {0} {snmp_type} version \
+ {version} {v3} {community} udp-port {1}".format(
+ host, udp, **existing
+ )
+
+ elif existing["version"] == "v2c":
+ existing["version"] = "2c"
+ command = "no snmp-server host {0} {snmp_type} version \
+ {version} {community} udp-port {1}".format(
+ host, udp, **existing
+ )
+
+ elif existing["version"] == "v1":
+ existing["version"] = "1"
+ command = "no snmp-server host {0} {snmp_type} version \
+ {version} {community} udp-port {1}".format(
+ host, udp, **existing
+ )
+
+ if command:
+ commands.append(command)
+ return commands
+
+
+def remove_vrf(host, udp, proposed, existing):
+ commands = []
+ if existing.get("vrf"):
+ commands.append(
+ "no snmp-server host {0} use-vrf \
+ {1} udp-port {2}".format(
+ host, proposed.get("vrf"), udp
+ )
+ )
+ return commands
+
+
+def remove_filter(host, udp, proposed, existing):
+ commands = []
+ if existing.get("vrf_filter"):
+ if proposed.get("vrf_filter") in existing.get("vrf_filter"):
+ commands.append(
+ "no snmp-server host {0} filter-vrf \
+ {1} udp-port {2}".format(
+ host, proposed.get("vrf_filter"), udp
+ )
+ )
+ return commands
+
+
+def remove_src(host, udp, proposed, existing):
+ commands = []
+ if existing.get("src_intf"):
+ commands.append(
+ "no snmp-server host {0} source-interface \
+ {1} udp-port {2}".format(
+ host, proposed.get("src_intf"), udp
+ )
+ )
+ return commands
+
+
+def config_snmp_host(delta, udp, proposed, existing, module):
+ commands = []
+ command_builder = []
+ host = proposed["snmp_host"]
+ cmd = "snmp-server host {0}".format(proposed["snmp_host"])
+
+ snmp_type = delta.get("snmp_type")
+ version = delta.get("version")
+ ver = delta.get("v3")
+ community = delta.get("community")
+
+ command_builder.append(cmd)
+ if any([snmp_type, version, ver, community]):
+ type_string = snmp_type or existing.get("type")
+ if type_string:
+ command_builder.append(type_string)
+
+ version = version or existing.get("version")
+ if version:
+ if version == "v1":
+ vn = "1"
+ elif version == "v2c":
+ vn = "2c"
+ elif version == "v3":
+ vn = "3"
+
+ version_string = "version {0}".format(vn)
+ command_builder.append(version_string)
+
+ if ver:
+ ver_string = ver or existing.get("v3")
+ command_builder.append(ver_string)
+
+ if community:
+ community_string = community or existing.get("community")
+ command_builder.append(community_string)
+
+ udp_string = " udp-port {0}".format(udp)
+ command_builder.append(udp_string)
+
+ cmd = " ".join(command_builder)
+
+ commands.append(cmd)
+
+ CMDS = {
+ "vrf_filter": "snmp-server host {0} filter-vrf {vrf_filter} udp-port {1}",
+ "vrf": "snmp-server host {0} use-vrf {vrf} udp-port {1}",
+ "src_intf": "snmp-server host {0} source-interface {src_intf} udp-port {1}",
+ }
+
+ for key in delta:
+ command = CMDS.get(key)
+ if command:
+ cmd = command.format(host, udp, **delta)
+ commands.append(cmd)
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ snmp_host=dict(required=True, type="str"),
+ community=dict(type="str"),
+ udp=dict(type="str", default="162"),
+ version=dict(choices=["v1", "v2c", "v3"]),
+ src_intf=dict(type="str"),
+ v3=dict(choices=["noauth", "auth", "priv"]),
+ vrf_filter=dict(type="str"),
+ vrf=dict(type="str"),
+ snmp_type=dict(choices=["trap", "inform"]),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ snmp_host = module.params["snmp_host"]
+ community = module.params["community"]
+ udp = module.params["udp"]
+ version = module.params["version"]
+ src_intf = module.params["src_intf"]
+ v3 = module.params["v3"]
+ vrf_filter = module.params["vrf_filter"]
+ vrf = module.params["vrf"]
+ snmp_type = module.params["snmp_type"]
+ state = module.params["state"]
+
+ existing = get_snmp_host(snmp_host, udp, module)
+
+ if version is None:
+ if existing:
+ version = existing.get("version")
+ else:
+ version = "v1"
+
+ if snmp_type is None:
+ if existing:
+ snmp_type = existing.get("snmp_type")
+ else:
+ snmp_type = "trap"
+
+ if v3 is None:
+ if version == "v3" and existing:
+ v3 = existing.get("v3")
+
+ if snmp_type == "inform" and version == "v1":
+ module.fail_json(msg="inform requires snmp v2c or v3")
+
+ if (version == "v1" or version == "v2c") and v3:
+ module.fail_json(
+ msg='param: "v3" should not be used when '
+ "using version v1 or v2c"
+ )
+
+ if not any([vrf_filter, vrf, src_intf]):
+ if not all([snmp_type, version, community, udp]):
+ module.fail_json(
+ msg="when not configuring options like "
+ "vrf_filter, vrf, and src_intf,"
+ "the following params are required: "
+ "type, version, community"
+ )
+
+ if version == "v3" and v3 is None:
+ module.fail_json(
+ msg="when using version=v3, the param v3 "
+ "(options: auth, noauth, priv) is also required"
+ )
+
+ # existing returns the list of vrfs configured for a given host
+ # checking to see if the proposed is in the list
+ store = existing.get("vrf_filter")
+ if existing and store:
+ if vrf_filter not in existing["vrf_filter"]:
+ existing["vrf_filter"] = None
+ else:
+ existing["vrf_filter"] = vrf_filter
+ commands = []
+
+ args = dict(
+ community=community,
+ snmp_host=snmp_host,
+ udp=udp,
+ version=version,
+ src_intf=src_intf,
+ vrf_filter=vrf_filter,
+ v3=v3,
+ vrf=vrf,
+ snmp_type=snmp_type,
+ )
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ if state == "absent" and existing:
+ if proposed.get("community"):
+ commands.append(remove_snmp_host(snmp_host, udp, existing))
+ else:
+ if proposed.get("src_intf"):
+ commands.append(remove_src(snmp_host, udp, proposed, existing))
+ if proposed.get("vrf"):
+ commands.append(remove_vrf(snmp_host, udp, proposed, existing))
+ if proposed.get("vrf_filter"):
+ commands.append(
+ remove_filter(snmp_host, udp, proposed, existing)
+ )
+
+ elif state == "present":
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = config_snmp_host(delta, udp, proposed, existing, module)
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_location.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_location.py
new file mode 100644
index 00000000..ed742cbd
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_location.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_snmp_location
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages SNMP location information.
+description:
+- Manages SNMP location configuration.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+options:
+ location:
+ description:
+ - Location information.
+ required: true
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ required: false
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# ensure snmp location is configured
+- cisco.nxos.nxos_snmp_location:
+ location: Test
+ state: present
+
+# ensure snmp location is not configured
+- cisco.nxos.nxos_snmp_location:
+ location: Test
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["snmp-server location New_Test"]
+"""
+
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ command = {"command": command, "output": "text"}
+
+ return run_commands(module, command)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_snmp_location(module):
+ location = {}
+ location_regex = r"^\s*snmp-server\s+location\s+(?P<location>.+)$"
+
+ body = execute_show_command("show run snmp", module)[0]
+ match_location = re.search(location_regex, body, re.M)
+ if match_location:
+ location["location"] = match_location.group("location")
+
+ return location
+
+
+def main():
+ argument_spec = dict(
+ location=dict(required=True, type="str"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ location = module.params["location"]
+ state = module.params["state"]
+
+ existing = get_snmp_location(module)
+ commands = []
+
+ if state == "absent":
+ if existing and existing["location"] == location:
+ commands.append("no snmp-server location")
+ elif state == "present":
+ if not existing or existing["location"] != location:
+ commands.append("snmp-server location {0}".format(location))
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_traps.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_traps.py
new file mode 100644
index 00000000..c48f6a27
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_traps.py
@@ -0,0 +1,331 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_snmp_traps
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages SNMP traps.
+description:
+- Manages SNMP traps configurations.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- This module works at the group level for traps. If you need to only enable/disable
+ 1 specific trap within a group, use the M(cisco.nxos.nxos_command) module.
+- Be aware that you can set a trap only for an enabled feature.
+options:
+ group:
+ description:
+ - Case sensitive group.
+ required: true
+ choices:
+ - aaa
+ - bfd
+ - bgp
+ - bridge
+ - callhome
+ - cfs
+ - config
+ - eigrp
+ - entity
+ - feature-control
+ - generic
+ - hsrp
+ - license
+ - link
+ - lldp
+ - mmode
+ - ospf
+ - pim
+ - rf
+ - rmon
+ - snmp
+ - storm-control
+ - stpx
+ - switchfabric
+ - syslog
+ - sysmgr
+ - system
+ - upgrade
+ - vtp
+ - all
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ required: false
+ default: enabled
+ choices:
+ - enabled
+ - disabled
+ type: str
+"""
+
+EXAMPLES = """
+# ensure lldp trap configured
+- cisco.nxos.nxos_snmp_traps:
+ group: lldp
+ state: enabled
+
+# ensure lldp trap is not configured
+- cisco.nxos.nxos_snmp_traps:
+ group: lldp
+ state: disabled
+"""
+
+RETURN = """
+commands:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: "snmp-server enable traps lldp ;"
+"""
+
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def get_platform_id(module):
+ info = get_capabilities(module).get("device_info", {})
+ return info.get("network_os_platform", "")
+
+
+def execute_show_command(command, module):
+ command = {"command": command, "output": "text"}
+
+ return run_commands(module, command)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_snmp_traps(group, module):
+ body = execute_show_command("show run snmp all", module)[0].split("\n")
+
+ resource = {}
+ feature_list = [
+ "aaa",
+ "bfd",
+ "bgp",
+ "bridge",
+ "callhome",
+ "cfs",
+ "config",
+ "eigrp",
+ "entity",
+ "feature-control",
+ "generic",
+ "hsrp",
+ "license",
+ "link",
+ "lldp",
+ "mmode",
+ "ospf",
+ "pim",
+ "rf",
+ "rmon",
+ "snmp",
+ "storm-control",
+ "stpx",
+ "switchfabric",
+ "syslog",
+ "sysmgr",
+ "system",
+ "upgrade",
+ "vtp",
+ ]
+
+ if "all" in group and "N3K-C35" in get_platform_id(module):
+ module.warn(
+ "Platform does not support bfd traps; bfd ignored for 'group: all' request"
+ )
+ feature_list.remove("bfd")
+
+ for each in feature_list:
+ for line in body:
+ if each == "ospf":
+ # ospf behaves differently when routers are present
+ if "snmp-server enable traps ospf" == line:
+ resource[each] = True
+ break
+ else:
+ if "enable traps {0}".format(each) in line:
+ if "no " in line:
+ resource[each] = False
+ break
+ else:
+ resource[each] = True
+
+ for each in feature_list:
+ if resource.get(each) is None:
+ # on some platforms, the 'no' cmd does not
+ # show up and so check if the feature is enabled
+ body = execute_show_command("show run | inc feature", module)[0]
+ if "feature {0}".format(each) in body:
+ resource[each] = False
+
+ find = resource.get(group, None)
+
+ if group == "all".lower():
+ return resource
+ elif find is not None:
+ trap_resource = {group: find}
+ return trap_resource
+ else:
+ # if 'find' is None, it means that 'group' is a
+ # currently disabled feature.
+ return {}
+
+
+def get_trap_commands(group, state, existing, module):
+ commands = []
+ enabled = False
+ disabled = False
+
+ if group == "all":
+ if state == "disabled":
+ for feature in existing:
+ if existing[feature]:
+ trap_command = "no snmp-server enable traps {0}".format(
+ feature
+ )
+ commands.append(trap_command)
+
+ elif state == "enabled":
+ for feature in existing:
+ if existing[feature] is False:
+ trap_command = "snmp-server enable traps {0}".format(
+ feature
+ )
+ commands.append(trap_command)
+
+ else:
+ if group in existing:
+ if existing[group]:
+ enabled = True
+ else:
+ disabled = True
+
+ if state == "disabled" and enabled:
+ commands.append(
+ ["no snmp-server enable traps {0}".format(group)]
+ )
+ elif state == "enabled" and disabled:
+ commands.append(["snmp-server enable traps {0}".format(group)])
+ else:
+ module.fail_json(
+ msg="{0} is not a currently " "enabled feature.".format(group)
+ )
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ state=dict(choices=["enabled", "disabled"], default="enabled"),
+ group=dict(
+ choices=[
+ "aaa",
+ "bfd",
+ "bgp",
+ "bridge",
+ "callhome",
+ "cfs",
+ "config",
+ "eigrp",
+ "entity",
+ "feature-control",
+ "generic",
+ "hsrp",
+ "license",
+ "link",
+ "lldp",
+ "mmode",
+ "ospf",
+ "pim",
+ "rf",
+ "rmon",
+ "snmp",
+ "storm-control",
+ "stpx",
+ "switchfabric",
+ "syslog",
+ "sysmgr",
+ "system",
+ "upgrade",
+ "vtp",
+ "all",
+ ],
+ required=True,
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ group = module.params["group"].lower()
+ state = module.params["state"]
+
+ existing = get_snmp_traps(group, module)
+
+ commands = get_trap_commands(group, state, existing, module)
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_user.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_user.py
new file mode 100644
index 00000000..32235eb7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_snmp_user.py
@@ -0,0 +1,413 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_snmp_user
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages SNMP users for monitoring.
+description:
+- Manages SNMP user configuration.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Authentication parameters not idempotent.
+options:
+ user:
+ description:
+ - Name of the user.
+ required: true
+ type: str
+ group:
+ description:
+ - Group to which the user will belong to. If state = present, and the user is
+ existing, the group is added to the user. If the user is not existing, user
+ entry is created with this group argument. If state = absent, only the group
+ is removed from the user entry. However, to maintain backward compatibility,
+ if the existing user belongs to only one group, and if group argument is same
+ as the existing user's group, then the user entry also is deleted.
+ type: str
+ authentication:
+ description:
+ - Authentication parameters for the user.
+ choices:
+ - md5
+ - sha
+ type: str
+ pwd:
+ description:
+ - Authentication password when using md5 or sha. This is not idempotent
+ type: str
+ privacy:
+ description:
+ - Privacy password for the user. This is not idempotent
+ type: str
+ encrypt:
+ description:
+ - Enables AES-128 bit encryption when using privacy password.
+ type: bool
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_snmp_user:
+ user: ntc
+ group: network-operator
+ authentication: md5
+ pwd: test_password
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["snmp-server user ntc network-operator auth md5 test_password"]
+"""
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module, text=False):
+ command = {"command": command, "output": "json"}
+ if text:
+ command["output"] = "text"
+
+ return run_commands(module, command)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_snmp_groups(module):
+ data = execute_show_command("show snmp group", module)[0]
+ group_list = []
+
+ try:
+ group_table = data["TABLE_role"]["ROW_role"]
+ for group in group_table:
+ group_list.append(group["role_name"])
+ except (KeyError, AttributeError):
+ return group_list
+
+ return group_list
+
+
+def get_snmp_user(user, module):
+ command = "show snmp user {0}".format(user)
+ body = execute_show_command(command, module, text=True)
+ body_text = body[0]
+
+ if "No such entry" not in body[0]:
+ body = execute_show_command(command, module)
+
+ resource = {}
+ try:
+ # The TABLE and ROW keys differ between NXOS platforms.
+ if body[0].get("TABLE_snmp_user"):
+ tablekey = "TABLE_snmp_user"
+ rowkey = "ROW_snmp_user"
+ tablegrpkey = "TABLE_snmp_group_names"
+ rowgrpkey = "ROW_snmp_group_names"
+ authkey = "auth_protocol"
+ privkey = "priv_protocol"
+ grpkey = "group_names"
+ elif body[0].get("TABLE_snmp_users"):
+ tablekey = "TABLE_snmp_users"
+ rowkey = "ROW_snmp_users"
+ tablegrpkey = "TABLE_groups"
+ rowgrpkey = "ROW_groups"
+ authkey = "auth"
+ privkey = "priv"
+ grpkey = "group"
+
+ rt = body[0][tablekey][rowkey]
+ # on some older platforms, all groups except the 1st one
+ # are in list elements by themselves and they are
+ # indexed by 'user'. This is due to a platform bug.
+ # Get first element if rt is a list due to the bug
+ # or if there is no bug, parse rt directly
+ if isinstance(rt, list):
+ resource_table = rt[0]
+ else:
+ resource_table = rt
+
+ resource["user"] = user
+ resource["authentication"] = str(resource_table[authkey]).strip()
+ encrypt = str(resource_table[privkey]).strip()
+ if encrypt.startswith("aes"):
+ resource["encrypt"] = "aes-128"
+ else:
+ resource["encrypt"] = "none"
+
+ groups = []
+ if tablegrpkey in resource_table:
+ group_table = resource_table[tablegrpkey][rowgrpkey]
+ try:
+ for group in group_table:
+ groups.append(str(group[grpkey]).strip())
+ except TypeError:
+ groups.append(str(group_table[grpkey]).strip())
+
+ # Now for the platform bug case, get the groups
+ if isinstance(rt, list):
+ # remove 1st element from the list as this is parsed already
+ rt.pop(0)
+ # iterate through other elements indexed by
+ # 'user' and add it to groups.
+ for each in rt:
+ groups.append(each["user"].strip())
+
+ # Some 'F' platforms use 'group' key instead
+ elif "group" in resource_table:
+ # single group is a string, multiple groups in a list
+ groups = resource_table["group"]
+ if isinstance(groups, str):
+ groups = [groups]
+
+ resource["group"] = groups
+
+ except (KeyError, AttributeError, IndexError, TypeError):
+ if not resource and body_text and "No such entry" not in body_text:
+ # 6K and other platforms may not return structured output;
+ # attempt to get state from text output
+ resource = get_non_structured_snmp_user(body_text)
+
+ return resource
+
+
+def get_non_structured_snmp_user(body_text):
+ # This method is a workaround for platforms that don't support structured
+ # output for 'show snmp user <foo>'. This workaround may not work on all
+ # platforms. Sample non-struct output:
+ #
+ # User Auth Priv(enforce) Groups acl_filter
+ # ____ ____ _____________ ______ __________
+ # sample1 no no network-admin ipv4:my_acl
+ # network-operator
+ # priv-11
+ # -OR-
+ # sample2 md5 des(no) priv-15
+ # -OR-
+ # sample3 md5 aes-128(no) network-admin
+ resource = {}
+ output = body_text.rsplit("__________")[-1]
+ pat = re.compile(
+ r"^(?P<user>\S+)\s+"
+ r"(?P<auth>\S+)\s+"
+ r"(?P<priv>[\w\d-]+)(?P<enforce>\([\w\d-]+\))*\s+"
+ r"(?P<group>\S+)",
+ re.M,
+ )
+ m = re.search(pat, output)
+ if not m:
+ return resource
+ resource["user"] = m.group("user")
+ resource["auth"] = m.group("auth")
+ resource["encrypt"] = (
+ "aes-128" if "aes" in str(m.group("priv")) else "none"
+ )
+
+ resource["group"] = [m.group("group")]
+ more_groups = re.findall(r"^\s+([\w\d-]+)\s*$", output, re.M)
+ if more_groups:
+ resource["group"] += more_groups
+
+ return resource
+
+
+def remove_snmp_user(user, group=None):
+ if group:
+ return ["no snmp-server user {0} {1}".format(user, group)]
+ else:
+ return ["no snmp-server user {0}".format(user)]
+
+
+def config_snmp_user(proposed, user, reset):
+ if reset:
+ commands = remove_snmp_user(user)
+ else:
+ commands = []
+
+ if proposed.get("group"):
+ cmd = "snmp-server user {0} {group}".format(user, **proposed)
+ else:
+ cmd = "snmp-server user {0}".format(user)
+
+ auth = proposed.get("authentication", None)
+ pwd = proposed.get("pwd", None)
+
+ if auth and pwd:
+ cmd += " auth {authentication} {pwd}".format(**proposed)
+
+ encrypt = proposed.get("encrypt", None)
+ privacy = proposed.get("privacy", None)
+
+ if encrypt and privacy:
+ cmd += " priv {encrypt} {privacy}".format(**proposed)
+ elif privacy:
+ cmd += " priv {privacy}".format(**proposed)
+
+ if cmd:
+ commands.append(cmd)
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ user=dict(required=True, type="str"),
+ group=dict(type="str"),
+ pwd=dict(type="str", no_log=True),
+ privacy=dict(type="str"),
+ authentication=dict(choices=["md5", "sha"]),
+ encrypt=dict(type="bool"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_together=[["authentication", "pwd"], ["encrypt", "privacy"]],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ user = module.params["user"]
+ group = module.params["group"]
+ pwd = module.params["pwd"]
+ privacy = module.params["privacy"]
+ encrypt = module.params["encrypt"]
+ authentication = module.params["authentication"]
+ state = module.params["state"]
+
+ if privacy and encrypt:
+ if not pwd and authentication:
+ module.fail_json(
+ msg="pwd and authentication must be provided "
+ "when using privacy and encrypt"
+ )
+
+ if group and group not in get_snmp_groups(module):
+ module.fail_json(msg="group not configured yet on switch.")
+
+ existing = get_snmp_user(user, module)
+
+ if state == "present" and existing:
+ if group:
+ if group not in existing["group"]:
+ existing["group"] = None
+ else:
+ existing["group"] = group
+ else:
+ existing["group"] = None
+
+ commands = []
+
+ if state == "absent" and existing:
+ if group:
+ if group in existing["group"]:
+ if len(existing["group"]) == 1:
+ commands.append(remove_snmp_user(user))
+ else:
+ commands.append(remove_snmp_user(user, group))
+ else:
+ commands.append(remove_snmp_user(user))
+
+ elif state == "present":
+ reset = False
+
+ args = dict(
+ user=user,
+ pwd=pwd,
+ group=group,
+ privacy=privacy,
+ encrypt=encrypt,
+ authentication=authentication,
+ )
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ if not existing:
+ if encrypt:
+ proposed["encrypt"] = "aes-128"
+ commands.append(config_snmp_user(proposed, user, reset))
+
+ elif existing:
+ if encrypt and not existing["encrypt"].startswith("aes"):
+ reset = True
+ proposed["encrypt"] = "aes-128"
+
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ if delta.get("pwd"):
+ delta["authentication"] = authentication
+
+ if delta and encrypt:
+ delta["encrypt"] = "aes-128"
+
+ if delta:
+ command = config_snmp_user(delta, user, reset)
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+
+ if "configure" in cmds:
+ cmds.pop(0)
+ results["commands"] = cmds
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_route.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_route.py
new file mode 100644
index 00000000..595a22b3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_route.py
@@ -0,0 +1,383 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_static_route
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manages static
+ route configuration
+description:
+- Manages static route configuration
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_static_routes
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- If no vrf is supplied, vrf is set to default.
+- If C(state=absent), the route will be removed, regardless of the non-required parameters.
+options:
+ prefix:
+ description:
+ - Destination prefix of static route.
+ aliases:
+ - address
+ type: str
+ next_hop:
+ description:
+ - Next hop address or interface of static route. If interface, it must be the
+ fully-qualified interface name.
+ type: str
+ vrf:
+ description:
+ - VRF for static route.
+ default: default
+ type: str
+ tag:
+ description:
+ - Route tag value (numeric) or keyword 'default'.
+ type: str
+ route_name:
+ description:
+ - Name of the route or keyword 'default'. Used with the name parameter on the
+ CLI.
+ type: str
+ pref:
+ description:
+ - Preference or administrative difference of route (range 1-255) or keyword 'default'.
+ aliases:
+ - admin_distance
+ type: str
+ aggregate:
+ description: List of static route definitions
+ type: list
+ elements: dict
+ suboptions:
+ prefix:
+ description:
+ - Destination prefix of static route.
+ required: True
+ type: str
+ next_hop:
+ description:
+ - Next hop address or interface of static route. If interface, it must be the
+ fully-qualified interface name.
+ required: true
+ type: str
+ vrf:
+ description:
+ - VRF for static route.
+ type: str
+ tag:
+ description:
+ - Route tag value (numeric) or keyword 'default'.
+ type: str
+ route_name:
+ description:
+ - Name of the route or keyword 'default'. Used with the name parameter on the
+ CLI.
+ type: str
+ pref:
+ description:
+ - Preference or administrative difference of route (range 1-255) or keyword 'default'.
+ aliases:
+ - admin_distance
+ type: str
+ track:
+ description:
+ - Track value (range 1 - 512). Track must already be configured on the device
+ before adding the route.
+ type: int
+ state:
+ description:
+ - Manage the state of the resource.
+ choices:
+ - present
+ - absent
+ type: str
+ track:
+ description:
+ - Track value (range 1 - 512). Track must already be configured on the device
+ before adding the route.
+ type: int
+ state:
+ description:
+ - Manage the state of the resource.
+ choices:
+ - present
+ - absent
+ default: present
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_static_route:
+ prefix: 192.168.20.64/24
+ next_hop: 192.0.2.3
+ route_name: testing
+ pref: 100
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["ip route 192.168.20.0/24 192.0.2.3 name testing 100"]
+"""
+import re
+from copy import deepcopy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+
+
+def reconcile_candidate(module, candidate, prefix, want):
+ state, vrf = want["state"], want["vrf"]
+ if vrf == "default":
+ parents = []
+ flags = " | include '^ip route'"
+ else:
+ parents = ["vrf context {0}".format(vrf)]
+ flags = " | section '{0}' | include '^ ip route'".format(parents[0])
+
+ # Find existing routes in this vrf/default
+ netcfg = CustomNetworkConfig(
+ indent=2, contents=get_config(module, flags=[flags])
+ )
+ routes = str(netcfg).split("\n")
+ # strip whitespace from route strings
+ routes = [i.strip() for i in routes]
+
+ prefix_and_nh = "ip route {0} {1}".format(prefix, want["next_hop"])
+ existing = [i for i in routes if i.startswith(prefix_and_nh)]
+ proposed = set_route_command(prefix, want, module)
+
+ commands = []
+ if state == "absent" and existing:
+ commands = ["no " + existing[0]]
+ elif state == "present" and proposed not in routes:
+ if existing:
+ commands = ["no " + existing[0]]
+ commands.append(proposed)
+
+ if commands:
+ candidate.add(commands, parents=parents)
+
+
+def get_configured_track(module, ctrack):
+ check_track = "{0}".format(ctrack)
+ track_exists = False
+ command = "show track"
+ try:
+ body = run_commands(module, {"command": command, "output": "text"})
+ match = re.findall(r"Track\s+(\d+)", body[0])
+ except IndexError:
+ return None
+ if check_track in match:
+ track_exists = True
+ return track_exists
+
+
+def set_route_command(prefix, w, module):
+ route_cmd = "ip route {0} {1}".format(prefix, w["next_hop"])
+
+ if w["track"]:
+ if w["track"] in range(1, 512):
+ if get_configured_track(module, w["track"]):
+ route_cmd += " track {0}".format(w["track"])
+ else:
+ module.fail_json(
+ msg="Track {0} not configured on device".format(w["track"])
+ )
+ else:
+ module.fail_json(msg="Invalid track number, valid range is 1-512.")
+ if w["route_name"] and w["route_name"] != "default":
+ route_cmd += " name {0}".format(w["route_name"])
+ if w["tag"]:
+ if w["tag"] != "default" and w["tag"] != "0":
+ route_cmd += " tag {0}".format(w["tag"])
+ if w["pref"] and w["pref"] != "default":
+ route_cmd += " {0}".format(w["pref"])
+
+ return route_cmd
+
+
+def get_dotted_mask(mask):
+ bits = 0
+ for i in range(32 - mask, 32):
+ bits |= 1 << i
+ mask = "%d.%d.%d.%d" % (
+ (bits & 0xFF000000) >> 24,
+ (bits & 0xFF0000) >> 16,
+ (bits & 0xFF00) >> 8,
+ (bits & 0xFF),
+ )
+ return mask
+
+
+def get_network_start(address, netmask):
+ address = address.split(".")
+ netmask = netmask.split(".")
+ return [str(int(address[x]) & int(netmask[x])) for x in range(0, 4)]
+
+
+def network_from_string(address, mask, module):
+ octects = address.split(".")
+
+ if len(octects) > 4:
+ module.fail_json(msg="Incorrect address format.", address=address)
+
+ for octect in octects:
+ try:
+ if int(octect) < 0 or int(octect) > 255:
+ module.fail_json(
+ msg="Address may contain invalid values.", address=address
+ )
+ except ValueError:
+ module.fail_json(
+ msg="Address may contain non-integer values.", address=address
+ )
+
+ try:
+ if int(mask) < 0 or int(mask) > 32:
+ module.fail_json(msg="Incorrect mask value.", mask=mask)
+ except ValueError:
+ module.fail_json(msg="Mask may contain non-integer values.", mask=mask)
+
+ netmask = get_dotted_mask(int(mask))
+ return ".".join(get_network_start(address, netmask))
+
+
+def normalize_prefix(module, prefix):
+ splitted_prefix = prefix.split("/")
+
+ address = splitted_prefix[0]
+ if len(splitted_prefix) > 2:
+ module.fail_json(msg="Incorrect address format.", address=address)
+ elif len(splitted_prefix) == 2:
+ mask = splitted_prefix[1]
+ network = network_from_string(address, mask, module)
+
+ normalized_prefix = str(network) + "/" + str(mask)
+ else:
+ normalized_prefix = prefix + "/" + str(32)
+
+ return normalized_prefix
+
+
+def map_params_to_obj(module):
+ obj = []
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ obj.append(d)
+ else:
+ obj.append(
+ {
+ "prefix": module.params["prefix"],
+ "next_hop": module.params["next_hop"],
+ "vrf": module.params["vrf"],
+ "tag": module.params["tag"],
+ "route_name": module.params["route_name"],
+ "pref": module.params["pref"],
+ "state": module.params["state"],
+ "track": module.params["track"],
+ }
+ )
+
+ return obj
+
+
+def main():
+ element_spec = dict(
+ prefix=dict(type="str", aliases=["address"]),
+ next_hop=dict(type="str"),
+ vrf=dict(type="str", default="default"),
+ tag=dict(type="str"),
+ route_name=dict(type="str"),
+ pref=dict(type="str", aliases=["admin_distance"]),
+ state=dict(choices=["absent", "present"], default="present"),
+ track=dict(type="int"),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+ aggregate_spec["prefix"] = dict(required=True)
+ aggregate_spec["next_hop"] = dict(required=True)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec)
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ result = {"changed": False, "commands": []}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ for w in want:
+ prefix = normalize_prefix(module, w["prefix"])
+ candidate = CustomNetworkConfig(indent=3)
+ reconcile_candidate(module, candidate, prefix, w)
+
+ if not module.check_mode and candidate:
+ candidate = candidate.items_text()
+ load_config(module, candidate)
+ result["commands"].extend(candidate)
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_routes.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_routes.py
new file mode 100644
index 00000000..8117eb39
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_static_routes.py
@@ -0,0 +1,476 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+"""
+The module file for nxos_static_routes
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_static_routes
+short_description: Static routes resource module
+description: This module configures and manages the attributes of static routes on
+ Cisco NX-OS platforms.
+version_added: 1.0.0
+author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
+notes:
+- Tested against NX-OS 7.3.(0)D1(1) on VIRL
+- When a route is configured for a non-existent VRF, the VRF is created and the route
+ is added to it.
+- When deleting routes for a VRF, all routes inside the VRF are deleted, but the VRF
+ is not deleted.
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the following commands in order B(show running-config | include
+ '^ip(v6)* route') and B(show running-config | section '^vrf context').
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description:
+ - A list of configurations for static routes
+ type: list
+ elements: dict
+ suboptions:
+ vrf:
+ description:
+ - The VRF to which the static route(s) belong
+ type: str
+ address_families:
+ description: A dictionary specifying the address family to which the static
+ route(s) belong.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Specifies the top level address family indicator.
+ type: str
+ choices: [ipv4, ipv6]
+ required: true
+ routes:
+ description: A dictionary that specifies the static route configurations
+ elements: dict
+ type: list
+ suboptions:
+ dest:
+ description:
+ - Destination prefix of static route
+ - The address format is <ipv4/v6 address>/<mask>
+ - The mask is number in range 0-32 for IPv4 and in range 0-128 for
+ IPv6
+ type: str
+ required: true
+ next_hops:
+ description:
+ - Details of route to be taken
+ type: list
+ elements: dict
+ suboptions:
+ forward_router_address:
+ description:
+ - IP address of the next hop router
+ type: str
+ # required: True
+ interface:
+ description:
+ - Outgoing interface to take. For anything except 'Null0', then
+ next hop IP address should also be configured.
+ type: str
+ admin_distance:
+ description:
+ - Preference or administrative distance of route (range 1-255)
+ type: int
+ route_name:
+ description:
+ - Name of the static route
+ type: str
+ tag:
+ description:
+ - Route tag value (numeric)
+ type: int
+ track:
+ description:
+ - Track value (range 1 - 512). Track must already be configured
+ on the device before adding the route.
+ type: int
+ dest_vrf:
+ description:
+ - VRF of the destination
+ type: str
+ state:
+ description:
+ - The state the configuration should be left in
+ type: str
+ choices:
+ - deleted
+ - merged
+ - overridden
+ - replaced
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using deleted:
+
+# Before state:
+# -------------
+#
+# ip route 192.0.2.32/28 192.0.2.12 name new_route
+# ip route 192.0.2.26/24 192.0.2.13 tag 12
+
+- name: Delete all routes
+ cisco.nxos.nxos_static_routes:
+ state: deleted
+
+# After state:
+# ------------
+#
+
+
+# Before state:
+# ------------
+#
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+# ip route 192.0.2.64/28 192.0.2.22 tag 4
+# ip route 192.0.2.64/28 192.0.2.23 name merged_route 1
+# ipv6 route 2200:10::/36 2048:ae12::1 vrf dest 5
+
+- name: Delete routes based on VRF
+ cisco.nxos.nxos_static_routes:
+ config:
+ - vrf: trial_vrf
+ state: deleted
+
+# After state:
+# -----------
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+
+
+# Before state:
+# ------------
+#
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+# ip route 192.0.2.64/28 192.0.2.22 tag 4
+# ip route 192.0.2.64/28 192.0.2.23 name merged_route 1
+# ipv6 route 2200:10::/36 2048:ae12::1 vrf dest 5
+
+- name: Delete routes based on AFI in a VRF
+ cisco.nxos.nxos_static_routes:
+ config:
+ - vrf: trial_vrf
+ address_families:
+ - afi: ipv4
+ state: deleted
+
+# After state:
+# -----------
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+# ipv6 route 2200:10::/36 2048:ae12::1 vrf dest 5
+
+
+# Before state:
+# -----------
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# vrf context trial_vrf
+# ipv6 route 2200:10::/36 2048:ae12::1 vrf dest 5
+
+
+# Using merged
+
+# Before state:
+# -------------
+#
+
+- name: Merge new static route configuration
+ cisco.nxos.nxos_static_routes:
+ config:
+ - vrf: trial_vrf
+ address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.64/24
+ next_hops:
+ - forward_router_address: 192.0.2.22
+ tag: 4
+ admin_distance: 2
+
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.16/24
+ next_hops:
+ - forward_router_address: 192.0.2.24
+ route_name: new_route
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8::/64
+ next_hops:
+ - interface: eth1/3
+ forward_router_address: 2001:db8::12
+ state: merged
+
+# After state:
+# ------------
+#
+# ip route 192.0.2.16/24 192.0.2.24 name new_route
+# ipv6 route 2001:db8::/64 Ethernet1/3 2001:db8::12
+# vrf context trial_vrf
+# ip route 192.0.2.0/24 192.0.2.22 tag 4 2
+
+
+# Using overridden:
+
+# Before state:
+# -------------
+#
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+# ip route 192.0.2.64/28 192.0.2.22 tag 4
+# ip route 192.0.2.64/28 192.0.2.23 name merged_route 1
+
+- name: Overriden existing static route configuration with new configuration
+ cisco.nxos.nxos_static_routes:
+ config:
+ - vrf: trial_vrf
+ address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.16/28
+ next_hops:
+ - forward_router_address: 192.0.2.23
+ route_name: overridden_route1
+ admin_distance: 3
+
+ - forward_router_address: 192.0.2.45
+ route_name: overridden_route2
+ dest_vrf: destinationVRF
+ interface: Ethernet1/2
+ state: overridden
+
+# After state:
+# ------------
+#
+# ip route 192.0.2.16/28 192.0.2.23 name replaced_route1 3
+# ip route 192.0.2.16/28 Ethernet1/2 192.0.2.45 vrf destinationVRF name replaced_route2
+
+
+# Using replaced:
+
+# Before state:
+# ------------
+# ip route 192.0.2.16/28 192.0.2.24 name new_route
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+# ip route 192.0.2.64/28 192.0.2.22 tag 4
+# ip route 192.0.2.64/28 192.0.2.23 name merged_route 1
+
+- name: Replaced the existing static configuration of a prefix with new configuration
+ cisco.nxos.nxos_static_routes:
+ config:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.16/28
+ next_hops:
+ - forward_router_address: 192.0.2.23
+ route_name: replaced_route1
+ admin_distance: 3
+
+ - forward_router_address: 192.0.2.45
+ route_name: replaced_route2
+ dest_vrf: destinationVRF
+ interface: Ethernet1/2
+ state: replaced
+
+# After state:
+# -----------
+# ip route 192.0.2.16/28 192.0.2.23 name replaced_route1 3
+# ip route 192.0.2.16/28 Ethernet1/2 192.0.2.45 vrf destinationVRF name replaced_route2
+# ip route 192.0.2.80/28 192.0.2.26 tag 12
+# vrf context trial_vrf
+# ip route 192.0.2.64/28 192.0.2.22 tag 4
+# ip route 192.0.2.64/28 192.0.2.23 name merged_route 1
+
+
+# Using gathered:
+
+# Before state:
+# -------------
+# ipv6 route 2001:db8:12::/32 2001:db8::12
+# vrf context Test
+# ip route 192.0.2.48/28 192.0.2.13
+# ip route 192.0.2.48/28 192.0.2.14 5
+
+- name: Gather the exisitng condiguration
+ cisco.nxos.nxos_static_routes:
+ state: gathered
+
+# returns:
+# gathered:
+# - vrf: Test
+# address_families:
+# - afi: ipv4
+# routes:
+# - dest: 192.0.2.48/28
+# next_hops:
+# - forward_router_address: 192.0.2.13
+#
+# - forward_router_address: 192.0.2.14
+# admin_distance: 5
+#
+# - address_families:
+# - afi: ipv6
+# routes:
+# - dest: 2001:db8:12::/32
+# next_hops:
+# - forward_router_address: 2001:db8::12
+
+
+# Using rendered:
+
+- name: Render required configuration to be pushed to the device
+ cisco.nxos.nxos_static_routes:
+ config:
+ - address_families:
+ - afi: ipv4
+ routes:
+ - dest: 192.0.2.48/28
+ next_hops:
+ - forward_router_address: 192.0.2.13
+
+ - afi: ipv6
+ routes:
+ - dest: 2001:db8::/64
+ next_hops:
+ - interface: eth1/3
+ forward_router_address: 2001:db8::12
+ state: rendered
+
+# returns
+# rendered:
+# vrf context default
+# ip route 192.0.2.48/28 192.0.2.13
+# ipv6 route 2001:db8::/64 Ethernet1/3 2001:db8::12
+
+
+# Using parsed
+
+- name: Parse the config to structured data
+ cisco.nxos.nxos_static_routes:
+ running_config: |
+ ipv6 route 2002:db8:12::/32 2002:db8:12::1
+ vrf context Test
+ ip route 192.0.2.48/28 192.0.2.13
+ ip route 192.0.2.48/28 192.0.2.14 5
+
+# returns:
+# parsed:
+# - vrf: Test
+# address_families:
+# - afi: ipv4
+# routes:
+# - dest: 192.0.2.48/28
+# next_hops:
+# - forward_router_address: 192.0.2.13
+#
+# - forward_router_address: 192.0.2.14
+# admin_distance: 5
+#
+# - address_families:
+# - afi: ipv6
+# routes:
+# - dest: 2002:db8:12::/32
+# next_hops:
+# - forward_router_address: 2002:db8:12::1
+
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['ip route 192.0.2.48/28 192.0.2.12 Ethernet1/2 name sample_route',
+ 'ipv6 route 2001:db8:3000::/36 2001:db8:200:2::2', 'vrf context test','ip route 192.0.2.48/28 192.0.2.121']
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.static_routes.static_routes import (
+ Static_routesArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.static_routes.static_routes import (
+ Static_routes,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=Static_routesArgs.argument_spec, supports_check_mode=True
+ )
+
+ result = Static_routes(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_system.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_system.py
new file mode 100644
index 00000000..0ab78d3e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_system.py
@@ -0,0 +1,402 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_system
+extends_documentation_fragment:
+- cisco.nxos.nxos
+author: Peter Sprygada (@privateip)
+short_description: Manage the system attributes on Cisco NXOS devices
+description:
+- This module provides declarative management of node system attributes on Cisco NXOS
+ devices. It provides an option to configure host system parameters or remove those
+ parameters from the device active configuration.
+version_added: 1.0.0
+options:
+ hostname:
+ description:
+ - Configure the device hostname parameter. This option takes an ASCII string value
+ or keyword 'default'
+ type: str
+ domain_name:
+ description:
+ - Configures the default domain name suffix to be used when referencing this node
+ by its FQDN. This argument accepts either a list of domain names or a list
+ of dicts that configure the domain name and VRF name or keyword 'default'. See
+ examples.
+ type: list
+ elements: raw
+ domain_lookup:
+ description:
+ - Enables or disables the DNS lookup feature in Cisco NXOS. This argument accepts
+ boolean values. When enabled, the system will try to resolve hostnames using
+ DNS and when disabled, hostnames will not be resolved.
+ type: bool
+ domain_search:
+ description:
+ - Configures a list of domain name suffixes to search when performing DNS name
+ resolution. This argument accepts either a list of domain names or a list of
+ dicts that configure the domain name and VRF name or keyword 'default'. See
+ examples.
+ type: list
+ elements: raw
+ name_servers:
+ description:
+ - List of DNS name servers by IP address to use to perform name resolution lookups. This
+ argument accepts either a list of DNS servers or a list of hashes that configure
+ the name server and VRF name or keyword 'default'. See examples.
+ type: list
+ elements: raw
+ system_mtu:
+ description:
+ - Specifies the mtu, must be an integer or keyword 'default'.
+ type: str
+ state:
+ description:
+ - State of the configuration values in the device's current active configuration. When
+ set to I(present), the values should be configured in the device active configuration
+ and when set to I(absent) the values should not be in the device active configuration
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- name: configure hostname and domain-name
+ cisco.nxos.nxos_system:
+ hostname: nxos01
+ domain_name: test.example.com
+
+- name: remove configuration
+ cisco.nxos.nxos_system:
+ state: absent
+
+- name: configure name servers
+ cisco.nxos.nxos_system:
+ name_servers:
+ - 8.8.8.8
+ - 8.8.4.4
+
+- name: configure name servers with VRF support
+ cisco.nxos.nxos_system:
+ name_servers:
+ - {server: 8.8.8.8, vrf: mgmt}
+ - {server: 8.8.4.4, vrf: mgmt}
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always
+ type: list
+ sample:
+ - hostname nxos01
+ - ip domain-name test.example.com
+"""
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ NetworkConfig,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ ComplexList,
+)
+
+_CONFIGURED_VRFS = None
+
+
+def has_vrf(module, vrf):
+ global _CONFIGURED_VRFS
+ if _CONFIGURED_VRFS is not None:
+ return vrf in _CONFIGURED_VRFS
+ config = get_config(module)
+ _CONFIGURED_VRFS = re.findall(r"vrf context (\S+)", config)
+ return vrf in _CONFIGURED_VRFS
+
+
+def map_obj_to_commands(want, have, module):
+ commands = list()
+ state = module.params["state"]
+
+ def needs_update(x):
+ return want.get(x) and (want.get(x) != have.get(x))
+
+ def difference(x, y, z):
+ return [item for item in x[z] if item not in y[z]]
+
+ def remove(cmd, commands, vrf=None):
+ if vrf:
+ commands.append("vrf context %s" % vrf)
+ commands.append(cmd)
+ if vrf:
+ commands.append("exit")
+
+ def add(cmd, commands, vrf=None):
+ if vrf:
+ if not has_vrf(module, vrf):
+ module.fail_json(msg="invalid vrf name %s" % vrf)
+ return remove(cmd, commands, vrf)
+
+ if state == "absent":
+ if have["hostname"]:
+ commands.append("no hostname")
+
+ for item in have["domain_name"]:
+ cmd = "no ip domain-name %s" % item["name"]
+ remove(cmd, commands, item["vrf"])
+
+ for item in have["domain_search"]:
+ cmd = "no ip domain-list %s" % item["name"]
+ remove(cmd, commands, item["vrf"])
+
+ for item in have["name_servers"]:
+ cmd = "no ip name-server %s" % item["server"]
+ remove(cmd, commands, item["vrf"])
+
+ if have["system_mtu"]:
+ commands.append("no system jumbomtu")
+
+ if state == "present":
+ if needs_update("hostname"):
+ if want["hostname"] == "default":
+ if have["hostname"]:
+ commands.append("no hostname")
+ else:
+ commands.append("hostname %s" % want["hostname"])
+
+ if want.get("domain_lookup") is not None:
+ if have.get("domain_lookup") != want.get("domain_lookup"):
+ cmd = "ip domain-lookup"
+ if want["domain_lookup"] is False:
+ cmd = "no %s" % cmd
+ commands.append(cmd)
+
+ if want["domain_name"]:
+ if want.get("domain_name")[0]["name"] == "default":
+ if have["domain_name"]:
+ for item in have["domain_name"]:
+ cmd = "no ip domain-name %s" % item["name"]
+ remove(cmd, commands, item["vrf"])
+ else:
+ for item in difference(have, want, "domain_name"):
+ cmd = "no ip domain-name %s" % item["name"]
+ remove(cmd, commands, item["vrf"])
+ for item in difference(want, have, "domain_name"):
+ cmd = "ip domain-name %s" % item["name"]
+ add(cmd, commands, item["vrf"])
+
+ if want["domain_search"]:
+ if want.get("domain_search")[0]["name"] == "default":
+ if have["domain_search"]:
+ for item in have["domain_search"]:
+ cmd = "no ip domain-list %s" % item["name"]
+ remove(cmd, commands, item["vrf"])
+ else:
+ for item in difference(have, want, "domain_search"):
+ cmd = "no ip domain-list %s" % item["name"]
+ remove(cmd, commands, item["vrf"])
+ for item in difference(want, have, "domain_search"):
+ cmd = "ip domain-list %s" % item["name"]
+ add(cmd, commands, item["vrf"])
+
+ if want["name_servers"]:
+ if want.get("name_servers")[0]["server"] == "default":
+ if have["name_servers"]:
+ for item in have["name_servers"]:
+ cmd = "no ip name-server %s" % item["server"]
+ remove(cmd, commands, item["vrf"])
+ else:
+ for item in difference(have, want, "name_servers"):
+ cmd = "no ip name-server %s" % item["server"]
+ remove(cmd, commands, item["vrf"])
+ for item in difference(want, have, "name_servers"):
+ cmd = "ip name-server %s" % item["server"]
+ add(cmd, commands, item["vrf"])
+
+ if needs_update("system_mtu"):
+ if want["system_mtu"] == "default":
+ if have["system_mtu"]:
+ commands.append("no system jumbomtu")
+ else:
+ commands.append("system jumbomtu %s" % want["system_mtu"])
+
+ return commands
+
+
+def parse_hostname(config):
+ match = re.search(r"^hostname (\S+)", config, re.M)
+ if match:
+ return match.group(1)
+
+
+def parse_domain_name(config, vrf_config):
+ objects = list()
+ match = re.search(r"^ip domain-name (\S+)", config, re.M)
+ if match:
+ objects.append({"name": match.group(1), "vrf": None})
+
+ for vrf, cfg in iteritems(vrf_config):
+ match = re.search(r"ip domain-name (\S+)", cfg, re.M)
+ if match:
+ objects.append({"name": match.group(1), "vrf": vrf})
+
+ return objects
+
+
+def parse_domain_search(config, vrf_config):
+ objects = list()
+
+ for item in re.findall(r"^ip domain-list (\S+)", config, re.M):
+ objects.append({"name": item, "vrf": None})
+
+ for vrf, cfg in iteritems(vrf_config):
+ for item in re.findall(r"ip domain-list (\S+)", cfg, re.M):
+ objects.append({"name": item, "vrf": vrf})
+
+ return objects
+
+
+def parse_name_servers(config, vrf_config, vrfs):
+ objects = list()
+
+ match = re.search("^ip name-server (.+)$", config, re.M)
+ if match and "use-vrf" not in match.group(1):
+ for addr in match.group(1).split(" "):
+ objects.append({"server": addr, "vrf": None})
+
+ for vrf, cfg in iteritems(vrf_config):
+ vrf_match = re.search("ip name-server (.+)", cfg, re.M)
+ if vrf_match:
+ for addr in vrf_match.group(1).split(" "):
+ objects.append({"server": addr, "vrf": vrf})
+
+ return objects
+
+
+def parse_system_mtu(config):
+ match = re.search(r"^system jumbomtu (\d+)", config, re.M)
+ if match:
+ return match.group(1)
+
+
+def map_config_to_obj(module):
+ config = get_config(module)
+ configobj = NetworkConfig(indent=2, contents=config)
+
+ vrf_config = {}
+
+ vrfs = re.findall(r"^vrf context (\S+)$", config, re.M)
+ for vrf in vrfs:
+ config_data = configobj.get_block_config(path=["vrf context %s" % vrf])
+ vrf_config[vrf] = config_data
+
+ return {
+ "hostname": parse_hostname(config),
+ "domain_lookup": "no ip domain-lookup" not in config,
+ "domain_name": parse_domain_name(config, vrf_config),
+ "domain_search": parse_domain_search(config, vrf_config),
+ "name_servers": parse_name_servers(config, vrf_config, vrfs),
+ "system_mtu": parse_system_mtu(config),
+ }
+
+
+def map_params_to_obj(module):
+ obj = {
+ "hostname": module.params["hostname"],
+ "domain_lookup": module.params["domain_lookup"],
+ "system_mtu": module.params["system_mtu"],
+ }
+
+ domain_name = ComplexList(dict(name=dict(key=True), vrf=dict()), module)
+
+ domain_search = ComplexList(dict(name=dict(key=True), vrf=dict()), module)
+
+ name_servers = ComplexList(dict(server=dict(key=True), vrf=dict()), module)
+
+ for arg, cast in [
+ ("domain_name", domain_name),
+ ("domain_search", domain_search),
+ ("name_servers", name_servers),
+ ]:
+ if module.params[arg] is not None:
+ obj[arg] = cast(module.params[arg])
+ else:
+ obj[arg] = None
+
+ return obj
+
+
+def main():
+ """ main entry point for module execution
+ """
+ argument_spec = dict(
+ hostname=dict(),
+ domain_lookup=dict(type="bool"),
+ # { name: <str>, vrf: <str> }
+ domain_name=dict(type="list", elements="raw"),
+ # {name: <str>, vrf: <str> }
+ domain_search=dict(type="list", elements="raw"),
+ # { server: <str>; vrf: <str> }
+ name_servers=dict(type="list", elements="raw"),
+ system_mtu=dict(type="str"),
+ state=dict(default="present", choices=["present", "absent"]),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(module)
+
+ commands = map_obj_to_commands(want, have, module)
+ result["commands"] = commands
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_telemetry.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_telemetry.py
new file mode 100644
index 00000000..af619a69
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_telemetry.py
@@ -0,0 +1,337 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Cisco and/or its affiliates.
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_telemetry
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_telemetry
+short_description: TELEMETRY resource module
+description: Manages Telemetry Monitoring Service (TMS) configuration
+version_added: 1.0.0
+author: Mike Wiebe (@mikewiebe)
+notes:
+- Supported on N9k Version 7.0(3)I7(5) and later.
+options:
+ config:
+ description: The provided configuration
+ type: dict
+ suboptions:
+ certificate:
+ type: dict
+ description:
+ - Certificate SSL/TLS and hostname values.
+ - Value must be a dict defining values for keys (key and hostname).
+ suboptions:
+ key:
+ description:
+ - Certificate key
+ type: str
+ hostname:
+ description:
+ - Certificate hostname
+ type: str
+ compression:
+ type: str
+ description:
+ - Destination profile compression method.
+ choices:
+ - gzip
+ source_interface:
+ type: str
+ description:
+ - Destination profile source interface.
+ - Valid value is a str representing the source interface name.
+ vrf:
+ type: str
+ description:
+ - Destination profile vrf.
+ - Valid value is a str representing the vrf name.
+ destination_groups:
+ type: list
+ description:
+ - List of telemetry destination groups.
+ elements: raw
+ suboptions:
+ id:
+ type: int
+ description:
+ - Destination group identifier.
+ - Value must be a int representing the destination group identifier.
+ destination:
+ type: dict
+ description:
+ - Group destination ipv4, port, protocol and encoding values.
+ - Value must be a dict defining values for keys (ip, port, protocol, encoding).
+ suboptions:
+ ip:
+ type: str
+ description:
+ - Destination group IP address.
+ port:
+ type: int
+ description:
+ - Destination group port number.
+ protocol:
+ type: str
+ description:
+ - Destination group protocol.
+ choices:
+ - HTTP
+ - TCP
+ - UDP
+ - gRPC
+ encoding:
+ type: str
+ description:
+ - Destination group encoding.
+ choices:
+ - GPB
+ - JSON
+ sensor_groups:
+ type: list
+ description:
+ - List of telemetry sensor groups.
+ elements: raw
+ suboptions:
+ id:
+ type: int
+ description:
+ - Sensor group identifier.
+ - Value must be a int representing the sensor group identifier.
+ data_source:
+ type: str
+ description:
+ - Telemetry data source.
+ choices:
+ - NX-API
+ - DME
+ - YANG
+ path:
+ type: dict
+ description:
+ - Telemetry sensor path.
+ - Value must be a dict defining values for keys (name, depth, filter_condition,
+ query_condition).
+ - Mandatory Keys (name)
+ - Optional Keys (depth, filter_condition, query_condition)
+ suboptions:
+ name:
+ type: str
+ description:
+ - Sensor group path name.
+ depth:
+ type: str
+ description:
+ - Sensor group depth.
+ filter_condition:
+ type: str
+ description:
+ - Sensor group filter condition.
+ query_condition:
+ type: str
+ description:
+ - Sensor group query condition.
+ subscriptions:
+ type: list
+ description:
+ - List of telemetry subscriptions.
+ elements: raw
+ suboptions:
+ id:
+ type: int
+ description:
+ - Subscription identifier.
+ - Value must be a int representing the subscription identifier.
+ destination_group:
+ type: int
+ description:
+ - Associated destination group.
+ sensor_group:
+ type: dict
+ description:
+ - Associated sensor group.
+ - Value must be a dict defining values for keys (id, sample_interval).
+ suboptions:
+ id:
+ type: int
+ description:
+ - Associated sensor group id.
+ sample_interval:
+ type: int
+ description:
+ - Associated sensor group id sample interval.
+ state:
+ description:
+ - Final configuration state
+ type: str
+ choices:
+ - merged
+ - replaced
+ - deleted
+ default: merged
+
+"""
+EXAMPLES = """
+# Using deleted
+# This action will delete all telemetry configuration on the device
+
+- name: Delete Telemetry Configuration
+ cisco.nxos.nxos_telemetry:
+ state: deleted
+
+
+# Using merged
+# This action will merge telemetry configuration defined in the playbook with
+# telemetry configuration that is already on the device.
+
+- name: Merge Telemetry Configuration
+ cisco.nxos.nxos_telemetry:
+ config:
+ certificate:
+ key: /bootflash/server.key
+ hostname: localhost
+ compression: gzip
+ source_interface: Ethernet1/1
+ vrf: management
+ destination_groups:
+ - id: 2
+ destination:
+ ip: 192.168.0.2
+ port: 50001
+ protocol: gPRC
+ encoding: GPB
+ - id: 55
+ destination:
+ ip: 192.168.0.55
+ port: 60001
+ protocol: gPRC
+ encoding: GPB
+ sensor_groups:
+ - id: 1
+ data_source: NX-API
+ path:
+ name: '"show lldp neighbors detail"'
+ depth: 0
+ - id: 55
+ data_source: DME
+ path:
+ name: sys/ch
+ depth: unbounded
+ filter_condition: ne(eqptFt.operSt,"ok")
+ subscriptions:
+ - id: 5
+ destination_group: 55
+ sensor_group:
+ id: 1
+ sample_interval: 1000
+ - id: 6
+ destination_group: 2
+ sensor_group:
+ id: 55
+ sample_interval: 2000
+ state: merged
+
+
+# Using replaced
+# This action will replace telemetry configuration on the device with the
+# telemetry configuration defined in the playbook.
+
+- name: Override Telemetry Configuration
+ cisco.nxos.nxos_telemetry:
+ config:
+ certificate:
+ key: /bootflash/server.key
+ hostname: localhost
+ compression: gzip
+ source_interface: Ethernet1/1
+ vrf: management
+ destination_groups:
+ - id: 2
+ destination:
+ ip: 192.168.0.2
+ port: 50001
+ protocol: gPRC
+ encoding: GPB
+ subscriptions:
+ - id: 5
+ destination_group: 55
+ state: replaced
+
+
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: dict
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.telemetry.telemetry import (
+ TelemetryArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.telemetry.telemetry import (
+ Telemetry,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=TelemetryArgs.argument_spec, supports_check_mode=True
+ )
+
+ result = Telemetry(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld.py
new file mode 100644
index 00000000..b509c7ed
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld.py
@@ -0,0 +1,266 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_udld
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages UDLD global configuration params.
+description:
+- Manages UDLD global configuration params.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Module will fail if the udld feature has not been previously enabled.
+options:
+ aggressive:
+ description:
+ - Toggles aggressive mode.
+ choices:
+ - enabled
+ - disabled
+ type: str
+ msg_time:
+ description:
+ - Message time in seconds for UDLD packets or keyword 'default'.
+ type: str
+ reset:
+ description:
+ - Ability to reset all ports shut down by UDLD. 'state' parameter cannot be 'absent'
+ when this is present.
+ type: bool
+ state:
+ description:
+ - Manage the state of the resource. When set to 'absent', aggressive and msg_time
+ are set to their default values.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+EXAMPLES = """
+# ensure udld aggressive mode is globally disabled and se global message interval is 20
+- cisco.nxos.nxos_udld:
+ aggressive: disabled
+ msg_time: 20
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+
+# Ensure agg mode is globally enabled and msg time is 15
+- cisco.nxos.nxos_udld:
+ aggressive: enabled
+ msg_time: 15
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"aggressive": "enabled", "msg_time": "40"}
+existing:
+ description:
+ - k/v pairs of existing udld configuration
+ returned: always
+ type: dict
+ sample: {"aggressive": "disabled", "msg_time": "15"}
+end_state:
+ description: k/v pairs of udld configuration after module execution
+ returned: always
+ type: dict
+ sample: {"aggressive": "enabled", "msg_time": "40"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["udld message-time 40", "udld aggressive"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+PARAM_TO_DEFAULT_KEYMAP = {"msg_time": "15"}
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ value = table.get(key)
+ if value:
+ new_dict[new_key] = str(value)
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def get_commands_config_udld_global(delta, reset, existing):
+ commands = []
+ for param, value in delta.items():
+ if param == "aggressive":
+ command = (
+ "udld aggressive"
+ if value == "enabled"
+ else "no udld aggressive"
+ )
+ commands.append(command)
+ elif param == "msg_time":
+ if value == "default":
+ if existing.get("msg_time") != PARAM_TO_DEFAULT_KEYMAP.get(
+ "msg_time"
+ ):
+ commands.append("no udld message-time")
+ else:
+ commands.append("udld message-time " + value)
+ if reset:
+ command = "udld reset"
+ commands.append(command)
+ return commands
+
+
+def get_commands_remove_udld_global(existing):
+ commands = []
+ if existing.get("aggressive") == "enabled":
+ command = "no udld aggressive"
+ commands.append(command)
+ if existing.get("msg_time") != PARAM_TO_DEFAULT_KEYMAP.get("msg_time"):
+ command = "no udld message-time"
+ commands.append(command)
+ return commands
+
+
+def get_udld_global(module):
+ command = "show udld global | json"
+ udld_table = run_commands(module, [command])[0]
+
+ status = str(udld_table.get("udld-global-mode", None))
+ if status == "enabled-aggressive":
+ aggressive = "enabled"
+ else:
+ aggressive = "disabled"
+
+ interval = str(udld_table.get("message-interval", None))
+ udld = dict(msg_time=interval, aggressive=aggressive)
+
+ return udld
+
+
+def main():
+ argument_spec = dict(
+ aggressive=dict(required=False, choices=["enabled", "disabled"]),
+ msg_time=dict(required=False, type="str"),
+ reset=dict(required=False, type="bool"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ aggressive = module.params["aggressive"]
+ msg_time = module.params["msg_time"]
+ reset = module.params["reset"]
+ state = module.params["state"]
+
+ if reset and state == "absent":
+ module.fail_json(msg="state must be present when using reset flag.")
+
+ args = dict(aggressive=aggressive, msg_time=msg_time, reset=reset)
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+
+ existing = get_udld_global(module)
+ end_state = existing
+
+ delta = set(proposed.items()).difference(existing.items())
+ changed = False
+
+ commands = []
+ if state == "present":
+ if delta:
+ command = get_commands_config_udld_global(
+ dict(delta), reset, existing
+ )
+ commands.append(command)
+
+ elif state == "absent":
+ command = get_commands_remove_udld_global(existing)
+ if command:
+ commands.append(command)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+ end_state = get_udld_global(module)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["end_state"] = end_state
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld_interface.py
new file mode 100644
index 00000000..d37530b3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_udld_interface.py
@@ -0,0 +1,315 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_udld_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages UDLD interface configuration params.
+description:
+- Manages UDLD interface configuration params.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Feature UDLD must be enabled on the device to use this module.
+options:
+ mode:
+ description:
+ - Manages UDLD mode for an interface.
+ required: true
+ choices:
+ - enabled
+ - disabled
+ - aggressive
+ type: str
+ interface:
+ description:
+ - FULL name of the interface, i.e. Ethernet1/1-
+ required: true
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ required: false
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+EXAMPLES = """
+# ensure Ethernet1/1 is configured to be in aggressive mode
+- cisco.nxos.nxos_udld_interface:
+ interface: Ethernet1/1
+ mode: aggressive
+ state: present
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+
+# Remove the aggressive config only if it's currently in aggressive mode and then disable udld (switch default)
+- cisco.nxos.nxos_udld_interface:
+ interface: Ethernet1/1
+ mode: aggressive
+ state: absent
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+
+# ensure Ethernet1/1 has aggressive mode enabled
+- cisco.nxos.nxos_udld_interface:
+ interface: Ethernet1/1
+ mode: enabled
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"mode": "enabled"}
+existing:
+ description:
+ - k/v pairs of existing configuration
+ returned: always
+ type: dict
+ sample: {"mode": "aggressive"}
+end_state:
+ description: k/v pairs of configuration after module execution
+ returned: always
+ type: dict
+ sample: {"mode": "enabled"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["interface ethernet1/33",
+ "no udld aggressive ; no udld disable"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_udld_interface(module, interface):
+ command = "show run udld all | section " + interface.title() + "$"
+ interface_udld = {}
+ mode = None
+ mode_str = None
+ try:
+ body = run_commands(module, [{"command": command, "output": "text"}])[
+ 0
+ ]
+ if "aggressive" in body:
+ mode = "aggressive"
+ mode_str = "aggressive"
+ elif "no udld enable" in body:
+ mode = "disabled"
+ mode_str = "no udld enable"
+ elif "no udld disable" in body:
+ mode = "enabled"
+ mode_str = "no udld disable"
+ elif "udld disable" in body:
+ mode = "disabled"
+ mode_str = "udld disable"
+ elif "udld enable" in body:
+ mode = "enabled"
+ mode_str = "udld enable"
+ interface_udld["mode"] = mode
+
+ except (KeyError, AttributeError, IndexError):
+ interface_udld = {}
+
+ return interface_udld, mode_str
+
+
+def get_commands_config_udld_interface1(delta, interface, module, existing):
+ commands = []
+ mode = delta["mode"]
+ if mode == "aggressive":
+ commands.append("udld aggressive")
+ else:
+ commands.append("no udld aggressive")
+ commands.insert(0, "interface {0}".format(interface))
+
+ return commands
+
+
+def get_commands_config_udld_interface2(delta, interface, module, existing):
+ commands = []
+ existing, mode_str = get_udld_interface(module, interface)
+ mode = delta["mode"]
+ if mode == "enabled":
+ if mode_str == "no udld enable":
+ command = "udld enable"
+ else:
+ command = "no udld disable"
+ else:
+ if mode_str == "no udld disable":
+ command = "udld disable"
+ else:
+ command = "no udld enable"
+ if command:
+ commands.append(command)
+ commands.insert(0, "interface {0}".format(interface))
+
+ return commands
+
+
+def get_commands_remove_udld_interface(delta, interface, module, existing):
+ commands = []
+ existing, mode_str = get_udld_interface(module, interface)
+
+ mode = delta["mode"]
+ if mode == "aggressive":
+ command = "no udld aggressive"
+ else:
+ if mode == "enabled":
+ if mode_str == "udld enable":
+ command = "no udld enable"
+ else:
+ command = "udld disable"
+ elif mode == "disabled":
+ if mode_str == "no udld disable":
+ command = "udld disable"
+ else:
+ command = "no udld enable"
+ if command:
+ commands.append(command)
+ commands.insert(0, "interface {0}".format(interface))
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ mode=dict(
+ choices=["enabled", "disabled", "aggressive"], required=True
+ ),
+ interface=dict(type="str", required=True),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ interface = module.params["interface"].lower()
+ mode = module.params["mode"]
+ state = module.params["state"]
+
+ proposed = dict(mode=mode)
+ existing, mode_str = get_udld_interface(module, interface)
+ end_state = existing
+
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ changed = False
+ commands = []
+ cmds = []
+ if state == "present":
+ if delta:
+ command = get_commands_config_udld_interface1(
+ delta, interface, module, existing
+ )
+ commands.append(command)
+ cmds = flatten_list(commands)
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+
+ if delta["mode"] == "enabled" or delta["mode"] == "disabled":
+ commands = []
+ command = get_commands_config_udld_interface2(
+ delta, interface, module, existing
+ )
+ commands.append(command)
+ cmds = flatten_list(commands)
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ load_config(module, cmds)
+
+ else:
+ common = set(proposed.items()).intersection(existing.items())
+ if common:
+ command = get_commands_remove_udld_interface(
+ dict(common), interface, module, existing
+ )
+ cmds = flatten_list(commands)
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+
+ if not module.check_mode:
+ end_state, mode_str = get_udld_interface(module, interface)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["end_state"] = end_state
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_user.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_user.py
new file mode 100644
index 00000000..06c291cc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_user.py
@@ -0,0 +1,483 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_user
+extends_documentation_fragment:
+- cisco.nxos.nxos
+author: Peter Sprygada (@privateip)
+short_description: Manage the collection of local users on Nexus devices
+description:
+- This module provides declarative management of the local usernames configured on
+ Cisco Nexus devices. It allows playbooks to manage either individual usernames
+ or the collection of usernames in the current running config. It also supports
+ purging usernames from the configuration that are not explicitly defined.
+version_added: 1.0.0
+options:
+ aggregate:
+ description:
+ - The set of username objects to be configured on the remote Cisco Nexus device. The
+ list entries can either be the username or a hash of username and properties. This
+ argument is mutually exclusive with the C(name) argument.
+ aliases:
+ - users
+ - collection
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - The username to be configured on the remote Cisco Nexus device. This argument
+ accepts a string value and is mutually exclusive with the C(aggregate) argument.
+ type: str
+ configured_password:
+ description:
+ - The password to be configured on the network device. The password needs to be
+ provided in cleartext and it will be encrypted on the device. Please note that
+ this option is not same as C(provider password).
+ type: str
+ update_password:
+ description:
+ - Since passwords are encrypted in the device running config, this argument will
+ instruct the module when to change the password. When set to C(always), the
+ password will always be updated in the device and when set to C(on_create) the
+ password will be updated only if the username is created.
+ choices:
+ - on_create
+ - always
+ type: str
+ roles:
+ description:
+ - The C(role) argument configures the role for the username in the device running
+ configuration. The argument accepts a string value defining the role name. This
+ argument does not check if the role has been configured on the device.
+ aliases:
+ - role
+ type: list
+ elements: str
+ sshkey:
+ description:
+ - The C(sshkey) argument defines the SSH public key to configure for the username. This
+ argument accepts a valid SSH key value.
+ type: str
+ state:
+ description:
+ - The C(state) argument configures the state of the username definition as it
+ relates to the device operational configuration. When set to I(present), the
+ username(s) should be configured in the device active configuration and when
+ set to I(absent) the username(s) should not be in the device active configuration
+ choices:
+ - present
+ - absent
+ type: str
+ name:
+ description:
+ - The username to be configured on the remote Cisco Nexus device. This argument
+ accepts a string value and is mutually exclusive with the C(aggregate) argument.
+ type: str
+ configured_password:
+ description:
+ - The password to be configured on the network device. The password needs to be
+ provided in cleartext and it will be encrypted on the device. Please note that
+ this option is not same as C(provider password).
+ type: str
+ update_password:
+ description:
+ - Since passwords are encrypted in the device running config, this argument will
+ instruct the module when to change the password. When set to C(always), the
+ password will always be updated in the device and when set to C(on_create) the
+ password will be updated only if the username is created.
+ default: always
+ choices:
+ - on_create
+ - always
+ type: str
+ roles:
+ description:
+ - The C(role) argument configures the role for the username in the device running
+ configuration. The argument accepts a string value defining the role name. This
+ argument does not check if the role has been configured on the device.
+ aliases:
+ - role
+ type: list
+ elements: str
+ sshkey:
+ description:
+ - The C(sshkey) argument defines the SSH public key to configure for the username. This
+ argument accepts a valid SSH key value.
+ type: str
+ purge:
+ description:
+ - The C(purge) argument instructs the module to consider the resource definition
+ absolute. It will remove any previously configured usernames on the device
+ with the exception of the `admin` user which cannot be deleted per nxos constraints.
+ type: bool
+ default: no
+ state:
+ description:
+ - The C(state) argument configures the state of the username definition as it
+ relates to the device operational configuration. When set to I(present), the
+ username(s) should be configured in the device active configuration and when
+ set to I(absent) the username(s) should not be in the device active configuration
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- name: create a new user
+ cisco.nxos.nxos_user:
+ name: ansible
+ sshkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+ state: present
+
+- name: remove all users except admin
+ cisco.nxos.nxos_user:
+ purge: yes
+
+- name: set multiple users role
+ cisco.nxos.nxos_user:
+ aggregate:
+ - name: netop
+ - name: netend
+ role: network-operator
+ state: present
+"""
+
+RETURN = """
+commands:
+ description: The list of configuration mode commands to send to the device
+ returned: always
+ type: list
+ sample:
+ - name ansible
+ - name ansible password password
+"""
+import re
+
+from copy import deepcopy
+from functools import partial
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ run_commands,
+ load_config,
+ get_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+ to_list,
+)
+
+BUILTIN_ROLES = [
+ "network-admin",
+ "network-operator",
+ "vdc-admin",
+ "vdc-operator",
+ "priv-15",
+ "priv-14",
+ "priv-13",
+ "priv-12",
+ "priv-11",
+ "priv-10",
+ "priv-9",
+ "priv-8",
+ "priv-7",
+ "priv-6",
+ "priv-5",
+ "priv-4",
+ "priv-3",
+ "priv-2",
+ "priv-1",
+ "priv-0",
+]
+
+
+def get_custom_roles(module):
+ return re.findall(
+ r"^role name (\S+)",
+ get_config(module, flags=["| include '^role name'"]),
+ re.M,
+ )
+
+
+def validate_roles(value, module):
+ valid_roles = BUILTIN_ROLES + get_custom_roles(module)
+ for item in value:
+ if item not in valid_roles:
+ module.fail_json(msg="invalid role specified")
+
+
+def map_obj_to_commands(updates, module):
+ commands = list()
+ update_password = module.params["update_password"]
+
+ for update in updates:
+ want, have = update
+
+ def needs_update(x):
+ return want.get(x) and (want.get(x) != have.get(x))
+
+ def add(x):
+ return commands.append("username %s %s" % (want["name"], x))
+
+ def remove(x):
+ return commands.append("no username %s %s" % (want["name"], x))
+
+ def configure_roles():
+ if want["roles"]:
+ if have:
+ for item in set(have["roles"]).difference(want["roles"]):
+ remove("role %s" % item)
+
+ for item in set(want["roles"]).difference(have["roles"]):
+ add("role %s" % item)
+ else:
+ for item in want["roles"]:
+ add("role %s" % item)
+
+ return True
+ return False
+
+ if want["state"] == "absent":
+ commands.append("no username %s" % want["name"])
+ continue
+
+ roles_configured = False
+ if want["state"] == "present" and not have:
+ roles_configured = configure_roles()
+ if not roles_configured:
+ commands.append("username %s" % want["name"])
+
+ if needs_update("configured_password"):
+ if update_password == "always" or not have:
+ add("password %s" % want["configured_password"])
+
+ if needs_update("sshkey"):
+ add("sshkey %s" % want["sshkey"])
+
+ if not roles_configured:
+ configure_roles()
+
+ return commands
+
+
+def parse_password(data):
+ if not data.get("remote_login"):
+ return "<PASSWORD>"
+
+
+def parse_roles(data):
+ configured_roles = None
+ if "TABLE_role" in data:
+ configured_roles = data.get("TABLE_role")["ROW_role"]
+
+ roles = list()
+ if configured_roles:
+ for item in to_list(configured_roles):
+ roles.append(item["role"])
+ return roles
+
+
+def map_config_to_obj(module):
+ out = run_commands(
+ module, [{"command": "show user-account", "output": "json"}]
+ )
+ data = out[0]
+
+ objects = list()
+
+ for item in to_list(data["TABLE_template"]["ROW_template"]):
+ objects.append(
+ {
+ "name": item["usr_name"],
+ "configured_password": parse_password(item),
+ "sshkey": item.get("sshkey_info"),
+ "roles": parse_roles(item),
+ "state": "present",
+ }
+ )
+ return objects
+
+
+def get_param_value(key, item, module):
+ # if key doesn't exist in the item, get it from module.params
+ if not item.get(key):
+ value = module.params[key]
+
+ # if key does exist, do a type check on it to validate it
+ else:
+ value_type = module.argument_spec[key].get("type", "str")
+ type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type]
+ type_checker(item[key])
+ value = item[key]
+
+ return value
+
+
+def map_params_to_obj(module):
+ aggregate = module.params["aggregate"]
+ if not aggregate:
+ if not module.params["name"] and module.params["purge"]:
+ return list()
+ elif not module.params["name"]:
+ module.fail_json(msg="username is required")
+ else:
+ collection = [{"name": module.params["name"]}]
+ else:
+ collection = list()
+ for item in aggregate:
+ if not isinstance(item, dict):
+ collection.append({"name": item})
+ elif "name" not in item:
+ module.fail_json(msg="name is required")
+ else:
+ collection.append(item)
+
+ objects = list()
+
+ for item in collection:
+ get_value = partial(get_param_value, item=item, module=module)
+ item.update(
+ {
+ "configured_password": get_value("configured_password"),
+ "sshkey": get_value("sshkey"),
+ "roles": get_value("roles"),
+ "state": get_value("state"),
+ }
+ )
+
+ for key, value in iteritems(item):
+ if value:
+ # validate the param value (if validator func exists)
+ validator = globals().get("validate_%s" % key)
+ if all((value, validator)):
+ validator(value, module)
+
+ objects.append(item)
+
+ return objects
+
+
+def update_objects(want, have):
+ updates = list()
+ for entry in want:
+ item = next((i for i in have if i["name"] == entry["name"]), None)
+ if all((item is None, entry["state"] == "present")):
+ updates.append((entry, {}))
+ elif item:
+ for key, value in iteritems(entry):
+ if value and value != item[key]:
+ updates.append((entry, item))
+ return updates
+
+
+def main():
+ """ main entry point for module execution
+ """
+ element_spec = dict(
+ name=dict(),
+ configured_password=dict(no_log=True),
+ update_password=dict(
+ default="always", choices=["on_create", "always"]
+ ),
+ roles=dict(type="list", aliases=["role"], elements="str"),
+ sshkey=dict(),
+ state=dict(default="present", choices=["present", "absent"]),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(
+ type="list",
+ elements="dict",
+ options=aggregate_spec,
+ aliases=["collection", "users"],
+ ),
+ purge=dict(type="bool", default=False),
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ mutually_exclusive = [("name", "aggregate")]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ result = {"changed": False, "warnings": []}
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(module)
+
+ commands = map_obj_to_commands(update_objects(want, have), module)
+
+ if module.params["purge"]:
+ want_users = [x["name"] for x in want]
+ have_users = [x["name"] for x in have]
+ for item in set(have_users).difference(want_users):
+ if item != "admin":
+ item = item.replace("\\", "\\\\")
+ commands.append("no username %s" % item)
+
+ result["commands"] = commands
+
+ # the nxos cli prevents this by rule so capture it and display
+ # a nice failure message
+ if "no username admin" in commands:
+ module.fail_json(msg="cannot delete the `admin` account")
+
+ if commands:
+ if not module.check_mode:
+ responses = load_config(module, commands)
+ for resp in responses:
+ if resp.lower().startswith("wrong password"):
+ module.fail_json(msg=resp)
+ else:
+ result["warnings"].extend(
+ [
+ x[9:]
+ for x in resp.splitlines()
+ if x.startswith("WARNING: ")
+ ]
+ )
+
+ result["changed"] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlan.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlan.py
new file mode 100644
index 00000000..a5729b68
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlan.py
@@ -0,0 +1,849 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vlan
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: (deprecated, removed after 2022-06-01) Manages VLAN
+ resources and attributes.
+description:
+- Manages VLAN configurations on NX-OS switches.
+version_added: 1.0.0
+deprecated:
+ alternative: nxos_vlans
+ why: Updated modules released with more functionality
+ removed_at_date: '2022-06-01'
+author: Jason Edelman (@jedelman8)
+options:
+ vlan_id:
+ description:
+ - Single VLAN ID.
+ type: int
+ vlan_range:
+ description:
+ - Range of VLANs such as 2-10 or 2,5,10-15, etc.
+ type: str
+ name:
+ description:
+ - Name of VLAN or keyword 'default'.
+ type: str
+ interfaces:
+ description:
+ - List of interfaces that should be associated to the VLAN or keyword 'default'.
+ type: list
+ elements: str
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vlan
+ C(name) for associated interfaces. If the value in the C(associated_interfaces)
+ does not match with the operational state of vlan interfaces on device it will
+ result in failure.
+ type: list
+ elements: str
+ vlan_state:
+ description:
+ - Manage the vlan operational state of the VLAN
+ default: active
+ choices:
+ - active
+ - suspend
+ type: str
+ admin_state:
+ description:
+ - Manage the VLAN administrative state of the VLAN equivalent to shut/no shut
+ in VLAN config mode.
+ default: up
+ choices:
+ - up
+ - down
+ type: str
+ mapped_vni:
+ description:
+ - The Virtual Network Identifier (VNI) ID that is mapped to the VLAN. Valid values
+ are integer and keyword 'default'. Range 4096-16773119.
+ type: str
+ aggregate:
+ description: List of VLANs definitions.
+ type: list
+ elements: dict
+ suboptions:
+ vlan_id:
+ description:
+ - Single VLAN ID.
+ required: True
+ type: int
+ vlan_range:
+ description:
+ - Range of VLANs such as 2-10 or 2,5,10-15, etc.
+ type: str
+ name:
+ description:
+ - Name of VLAN or keyword 'default'.
+ type: str
+ interfaces:
+ description:
+ - List of interfaces that should be associated to the VLAN or keyword 'default'.
+ type: list
+ elements: str
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vlan
+ C(name) for associated interfaces. If the value in the C(associated_interfaces)
+ does not match with the operational state of vlan interfaces on device it will
+ result in failure.
+ type: list
+ elements: str
+ vlan_state:
+ description:
+ - Manage the vlan operational state of the VLAN
+ choices:
+ - active
+ - suspend
+ type: str
+ admin_state:
+ description:
+ - Manage the VLAN administrative state of the VLAN equivalent to shut/no shut
+ in VLAN config mode.
+ choices:
+ - up
+ - down
+ type: str
+ mapped_vni:
+ description:
+ - The Virtual Network Identifier (VNI) ID that is mapped to the VLAN. Valid values
+ are integer and keyword 'default'. Range 4096-16773119.
+ type: str
+ mode:
+ description:
+ - Set VLAN mode to classical ethernet or fabricpath. This is a valid option for
+ Nexus 5000 and 7000 series.
+ choices:
+ - ce
+ - fabricpath
+ type: str
+ delay:
+ description:
+ - Time in seconds to wait before checking for the operational state on remote
+ device. This wait is applicable for operational state arguments.
+ type: int
+ state:
+ description:
+ - Manage the state of the resource.
+ choices:
+ - present
+ - absent
+ type: str
+ state:
+ description:
+ - Manage the state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ mode:
+ description:
+ - Set VLAN mode to classical ethernet or fabricpath. This is a valid option for
+ Nexus 5000 and 7000 series.
+ choices:
+ - ce
+ - fabricpath
+ default: ce
+ type: str
+ purge:
+ description:
+ - Purge VLANs not defined in the I(aggregate) parameter. This parameter can be
+ used without aggregate as well.
+ - Removal of Vlan 1 is not allowed and will be ignored by purge.
+ type: bool
+ default: no
+ delay:
+ description:
+ - Time in seconds to wait before checking for the operational state on remote
+ device. This wait is applicable for operational state arguments.
+ default: 10
+ type: int
+
+
+"""
+
+EXAMPLES = """
+- name: Ensure a range of VLANs are not present on the switch
+ cisco.nxos.nxos_vlan:
+ vlan_range: 2-10,20,50,55-60,100-150
+ state: absent
+
+- name: Ensure VLAN 50 exists with the name WEB and is in the shutdown state
+ cisco.nxos.nxos_vlan:
+ vlan_id: 50
+ admin_state: down
+ name: WEB
+
+- name: Ensure VLAN is NOT on the device
+ cisco.nxos.nxos_vlan:
+ vlan_id: 50
+ state: absent
+
+- name: Add interfaces to VLAN and check intent (config + intent)
+ cisco.nxos.nxos_vlan:
+ vlan_id: 100
+ interfaces:
+ - Ethernet2/1
+ - Ethernet2/5
+ associated_interfaces:
+ - Ethernet2/1
+ - Ethernet2/5
+
+- name: Check interfaces assigned to VLAN
+ cisco.nxos.nxos_vlan:
+ vlan_id: 100
+ associated_interfaces:
+ - Ethernet2/1
+ - Ethernet2/5
+
+- name: Create aggregate of vlans
+ cisco.nxos.nxos_vlan:
+ aggregate:
+ - {vlan_id: 4000, mode: ce}
+ - {vlan_id: 4001, name: vlan-4001}
+
+- name: purge vlans - removes all other vlans except the ones mentioned in aggregate)
+ cisco.nxos.nxos_vlan:
+ aggregate:
+ - vlan_id: 1
+ - vlan_id: 4001
+ purge: yes
+
+"""
+
+RETURN = """
+commands:
+ description: Set of command strings to send to the remote device
+ returned: always
+ type: list
+ sample: ["vlan 20", "vlan 55", "vn-segment 5000"]
+"""
+
+import re
+import time
+
+from copy import deepcopy
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ normalize_interface,
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+
+
+def search_obj_in_list(vlan_id, lst):
+ for o in lst:
+ if o["vlan_id"] == vlan_id:
+ return o
+
+
+def get_diff(w, obj):
+ c = deepcopy(w)
+ entries = ("interfaces", "associated_interfaces", "delay", "vlan_range")
+ for key in entries:
+ if key in c:
+ del c[key]
+
+ o = deepcopy(obj)
+ del o["interfaces"]
+ if o["vlan_id"] == w["vlan_id"]:
+ diff_dict = dict(set(c.items()) - set(o.items()))
+ return diff_dict
+
+
+def is_default_name(obj, vlan_id):
+ cname = obj["name"]
+ if "VLAN" in cname:
+ vid = int(cname[4:])
+ if vid == int(vlan_id):
+ return True
+
+ return False
+
+
+def map_obj_to_commands(updates, module):
+ commands = list()
+ purge = module.params["purge"]
+ want, have = updates
+ info = get_capabilities(module).get("device_info")
+ os_platform = info.get("network_os_platform")
+
+ for w in want:
+ vlan_id = w["vlan_id"]
+ name = w["name"]
+ interfaces = w.get("interfaces") or []
+ mapped_vni = w["mapped_vni"]
+ mode = w["mode"]
+ vlan_state = w["vlan_state"]
+ admin_state = w["admin_state"]
+ state = w["state"]
+ del w["state"]
+
+ obj_in_have = search_obj_in_list(vlan_id, have) or {}
+ if not re.match("N[567]", os_platform) or (
+ not obj_in_have.get("mode") and mode == "ce"
+ ):
+ mode = w["mode"] = None
+
+ if state == "absent":
+ if obj_in_have:
+ commands.append("no vlan {0}".format(vlan_id))
+
+ elif state == "present":
+ if not obj_in_have:
+ commands.append("vlan {0}".format(vlan_id))
+
+ if name and name != "default":
+ commands.append("name {0}".format(name))
+ if mode:
+ commands.append("mode {0}".format(mode))
+ if vlan_state:
+ commands.append("state {0}".format(vlan_state))
+ if mapped_vni != "None" and mapped_vni != "default":
+ commands.append("vn-segment {0}".format(mapped_vni))
+ if admin_state == "up":
+ commands.append("no shutdown")
+ if admin_state == "down":
+ commands.append("shutdown")
+ commands.append("exit")
+
+ if interfaces and interfaces[0] != "default":
+ for i in interfaces:
+ commands.append("interface {0}".format(i))
+ commands.append("switchport")
+ commands.append("switchport mode access")
+ commands.append(
+ "switchport access vlan {0}".format(vlan_id)
+ )
+
+ else:
+ diff = get_diff(w, obj_in_have)
+ if diff:
+ commands.append("vlan {0}".format(vlan_id))
+ for key, value in diff.items():
+ if key == "name":
+ if name != "default":
+ if name is not None:
+ commands.append("name {0}".format(value))
+ else:
+ if not is_default_name(obj_in_have, vlan_id):
+ commands.append("no name")
+ if key == "vlan_state" and value:
+ commands.append("state {0}".format(value))
+ if key == "mapped_vni":
+ if value == "default":
+ if obj_in_have["mapped_vni"] != "None":
+ commands.append("no vn-segment")
+ elif value != "None":
+ commands.append("vn-segment {0}".format(value))
+ if key == "admin_state":
+ if value == "up":
+ commands.append("no shutdown")
+ elif value == "down":
+ commands.append("shutdown")
+ if key == "mode" and value:
+ commands.append("mode {0}".format(value))
+ if len(commands) > 1:
+ commands.append("exit")
+ else:
+ del commands[:]
+
+ if interfaces and interfaces[0] != "default":
+ if not obj_in_have["interfaces"]:
+ for i in interfaces:
+ commands.append("vlan {0}".format(vlan_id))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ commands.append("switchport")
+ commands.append("switchport mode access")
+ commands.append(
+ "switchport access vlan {0}".format(vlan_id)
+ )
+
+ elif set(interfaces) != set(obj_in_have["interfaces"]):
+ missing_interfaces = list(
+ set(interfaces) - set(obj_in_have["interfaces"])
+ )
+ for i in missing_interfaces:
+ commands.append("vlan {0}".format(vlan_id))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ commands.append("switchport")
+ commands.append("switchport mode access")
+ commands.append(
+ "switchport access vlan {0}".format(vlan_id)
+ )
+
+ superfluous_interfaces = list(
+ set(obj_in_have["interfaces"]) - set(interfaces)
+ )
+ for i in superfluous_interfaces:
+ commands.append("vlan {0}".format(vlan_id))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ commands.append("switchport")
+ commands.append("switchport mode access")
+ commands.append(
+ "no switchport access vlan {0}".format(vlan_id)
+ )
+
+ elif interfaces and interfaces[0] == "default":
+ if obj_in_have["interfaces"]:
+ for i in obj_in_have["interfaces"]:
+ commands.append("vlan {0}".format(vlan_id))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ commands.append("switchport")
+ commands.append("switchport mode access")
+ commands.append(
+ "no switchport access vlan {0}".format(vlan_id)
+ )
+
+ if purge:
+ for h in have:
+ if h["vlan_id"] == "1":
+ module.warn(
+ "Deletion of vlan 1 is not allowed; purge will ignore vlan 1"
+ )
+ continue
+ obj_in_want = search_obj_in_list(h["vlan_id"], want)
+ if not obj_in_want:
+ commands.append("no vlan {0}".format(h["vlan_id"]))
+
+ return commands
+
+
+def want_vlan_list(module):
+ result = []
+ vlan_range = module.params["vlan_range"]
+ for part in vlan_range.split(","):
+ if part == "none":
+ break
+ if "-" in part:
+ start, end = part.split("-")
+ start, end = int(start), int(end)
+ result.extend([str(i) for i in range(start, end + 1)])
+ else:
+ result.append(part)
+ return result
+
+
+def have_vlan_list(have):
+ result = []
+ if have:
+ for h in have:
+ result.append(str(h.get("vlan_id")))
+ return result
+
+
+def vlan_range_commands(module, have):
+ commands = list()
+ proposed_vlans_list = want_vlan_list(module)
+ existing_vlans_list = have_vlan_list(have)
+
+ if module.params["state"] == "absent":
+ vlans = set(proposed_vlans_list).intersection(existing_vlans_list)
+ for vlan in vlans:
+ commands.append("no vlan {0}".format(vlan))
+
+ elif module.params["state"] == "present":
+ vlans = set(proposed_vlans_list).difference(existing_vlans_list)
+ for vlan in vlans:
+ commands.append("vlan {0}".format(vlan))
+
+ return commands
+
+
+def normalize(interfaces):
+ normalized = None
+ if interfaces:
+ normalized = [normalize_interface(i) for i in interfaces]
+ return normalized
+
+
+def map_params_to_obj(module):
+ obj = []
+ if module.params["vlan_range"]:
+ return []
+
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ d["vlan_id"] = str(d["vlan_id"])
+ d["mapped_vni"] = str(d["mapped_vni"])
+ d["interfaces"] = normalize(d["interfaces"])
+ d["associated_interfaces"] = normalize(d["associated_interfaces"])
+
+ obj.append(d)
+ else:
+ interfaces = normalize(module.params["interfaces"])
+ associated_interfaces = normalize(
+ module.params["associated_interfaces"]
+ )
+
+ obj.append(
+ {
+ "vlan_id": str(module.params["vlan_id"]),
+ "name": module.params["name"],
+ "interfaces": interfaces,
+ "vlan_state": module.params["vlan_state"],
+ "mapped_vni": str(module.params["mapped_vni"]),
+ "state": module.params["state"],
+ "admin_state": module.params["admin_state"],
+ "mode": module.params["mode"],
+ "associated_interfaces": associated_interfaces,
+ }
+ )
+
+ return obj
+
+
+def parse_admin_state(vlan):
+ shutstate = vlan.get("vlanshowbr-shutstate")
+ if shutstate == "noshutdown":
+ return "up"
+ elif shutstate == "shutdown":
+ return "down"
+
+
+def parse_mode(config):
+ mode = None
+
+ if config:
+ match = re.search(r"mode (\S+)", config)
+ if match:
+ mode = match.group(1)
+ return mode
+
+
+def parse_vni(config):
+ vni = None
+
+ if config:
+ match = re.search(r"vn-segment (\S+)", config)
+ if match:
+ vni = match.group(1)
+ return str(vni)
+
+
+def get_vlan_int(interfaces):
+ vlan_int = []
+ for i in interfaces.split(","):
+ if "eth" in i.lower() and "-" in i:
+ int_range = i.split("-")
+ stop = int((int_range)[1])
+ start = int(int_range[0].split("/")[1])
+ eth = int_range[0].split("/")[0]
+ for r in range(start, stop + 1):
+ vlan_int.append(eth + "/" + str(r))
+ else:
+ vlan_int.append(i)
+ return vlan_int
+
+
+def parse_interfaces(module, vlan):
+ vlan_int = []
+ interfaces = vlan.get("vlanshowplist-ifidx")
+ if interfaces:
+ if isinstance(interfaces, list):
+ interfaces_list = [i.strip() for i in interfaces]
+ interfaces_str = ",".join(interfaces_list)
+ vlan_int = get_vlan_int(interfaces_str)
+ else:
+ vlan_int = get_vlan_int(interfaces)
+ return vlan_int
+
+
+def parse_vlan_config(netcfg, vlan_id):
+ parents = ["vlan {0}".format(vlan_id)]
+ config = netcfg.get_section(parents)
+ return config
+
+
+def parse_vlan_options(module, netcfg, output, vlan):
+ obj = {}
+ vlan_id = vlan["vlanshowbr-vlanid-utf"]
+ config = parse_vlan_config(netcfg, vlan_id)
+
+ obj["vlan_id"] = str(vlan_id)
+ obj["name"] = vlan.get("vlanshowbr-vlanname")
+ obj["vlan_state"] = vlan.get("vlanshowbr-vlanstate")
+ obj["admin_state"] = parse_admin_state(vlan)
+ obj["mode"] = parse_mode(config)
+ obj["mapped_vni"] = parse_vni(config)
+ obj["interfaces"] = parse_interfaces(module, vlan)
+ return obj
+
+
+def parse_vlan_non_structured(module, netcfg, vlans):
+ objs = list()
+
+ for vlan in vlans:
+ vlan_match = re.search(r"(\d+)", vlan, re.M)
+ if vlan_match:
+ obj = {}
+ vlan_id = vlan_match.group(1)
+ obj["vlan_id"] = str(vlan_id)
+
+ name_match = re.search(r"{0}\s*(\S+)".format(vlan_id), vlan, re.M)
+ if name_match:
+ name = name_match.group(1)
+ obj["name"] = name
+ state_match = re.search(
+ r"{0}\s*{1}\s*(\S+)".format(vlan_id, re.escape(name)),
+ vlan,
+ re.M,
+ )
+ if state_match:
+ vlan_state_match = state_match.group(1)
+ if vlan_state_match == "suspended":
+ vlan_state = "suspend"
+ admin_state = "up"
+ elif vlan_state_match == "sus/lshut":
+ vlan_state = "suspend"
+ admin_state = "down"
+ if vlan_state_match == "active":
+ vlan_state = "active"
+ admin_state = "up"
+ if vlan_state_match == "act/lshut":
+ vlan_state = "active"
+ admin_state = "down"
+
+ obj["vlan_state"] = vlan_state
+ obj["admin_state"] = admin_state
+
+ vlan = ",".join(vlan.splitlines())
+ interfaces = list()
+ intfs_match = re.search(
+ r"{0}\s*{1}\s*{2}\s*(.*)".format(
+ vlan_id, re.escape(name), vlan_state_match
+ ),
+ vlan,
+ re.M,
+ )
+ if intfs_match:
+ intfs = intfs_match.group(1)
+ intfs = intfs.split()
+ for i in intfs:
+ intf = normalize_interface(i.strip(","))
+ interfaces.append(intf)
+
+ if interfaces:
+ obj["interfaces"] = interfaces
+ else:
+ obj["interfaces"] = None
+
+ config = parse_vlan_config(netcfg, vlan_id)
+ obj["mode"] = parse_mode(config)
+ obj["mapped_vni"] = parse_vni(config)
+
+ objs.append(obj)
+
+ return objs
+
+
+def map_config_to_obj(module):
+ objs = list()
+ output = None
+
+ command = ["show vlan brief | json"]
+ output = run_commands(module, command, check_rc="retry_json")[0]
+ if output:
+ netcfg = CustomNetworkConfig(
+ indent=2, contents=get_config(module, flags=["all"])
+ )
+
+ if isinstance(output, dict):
+ vlans = None
+ try:
+ vlans = output["TABLE_vlanbriefxbrief"]["ROW_vlanbriefxbrief"]
+ except KeyError:
+ return objs
+
+ if vlans:
+ if isinstance(vlans, list):
+ for vlan in vlans:
+ obj = parse_vlan_options(module, netcfg, output, vlan)
+ objs.append(obj)
+ elif isinstance(vlans, dict):
+ obj = parse_vlan_options(module, netcfg, output, vlans)
+ objs.append(obj)
+ else:
+ vlans = list()
+ splitted_line = re.split(r"\n(\d+)|\n{2}", output.strip())
+
+ for line in splitted_line:
+ if not line:
+ continue
+ if len(line) > 0:
+ line = line.strip()
+ if line[0].isdigit():
+ match = re.search(r"^(\d+)$", line, re.M)
+ if match:
+ v = match.group(1)
+ pos1 = splitted_line.index(v)
+ pos2 = pos1 + 1
+ # fmt: off
+ vlaninfo = "".join(splitted_line[pos1: pos2 + 1])
+ # fmt: on
+ vlans.append(vlaninfo)
+
+ if vlans:
+ objs = parse_vlan_non_structured(module, netcfg, vlans)
+ else:
+ return objs
+
+ return objs
+
+
+def check_declarative_intent_params(want, module, result):
+
+ have = None
+ is_delay = False
+
+ for w in want:
+ if w.get("associated_interfaces") is None:
+ continue
+
+ if result["changed"] and not is_delay:
+ time.sleep(module.params["delay"])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(module)
+
+ for i in w["associated_interfaces"]:
+ obj_in_have = search_obj_in_list(w["vlan_id"], have)
+ if (
+ obj_in_have
+ and "interfaces" in obj_in_have
+ and i not in obj_in_have["interfaces"]
+ ):
+ module.fail_json(
+ msg="Interface %s not configured on vlan %s"
+ % (i, w["vlan_id"])
+ )
+
+
+def main():
+ """ main entry point for module execution
+ """
+ element_spec = dict(
+ vlan_id=dict(required=False, type="int"),
+ vlan_range=dict(required=False),
+ name=dict(required=False),
+ interfaces=dict(type="list", elements="str"),
+ associated_interfaces=dict(type="list", elements="str"),
+ vlan_state=dict(
+ choices=["active", "suspend"], required=False, default="active"
+ ),
+ mapped_vni=dict(required=False),
+ delay=dict(default=10, type="int"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ admin_state=dict(choices=["up", "down"], required=False, default="up"),
+ mode=dict(default="ce", choices=["ce", "fabricpath"]),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+ aggregate_spec["vlan_id"] = dict(required=True, type="int")
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec),
+ purge=dict(default=False, type="bool"),
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ required_one_of = [["vlan_id", "aggregate", "vlan_range"]]
+ mutually_exclusive = [
+ ["vlan_id", "aggregate"],
+ ["vlan_range", "name"],
+ ["vlan_id", "vlan_range"],
+ ]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=required_one_of,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ have = map_config_to_obj(module)
+ want = map_params_to_obj(module)
+
+ if module.params["vlan_range"]:
+ commands = vlan_range_commands(module, have)
+ result["commands"] = commands
+ else:
+ commands = map_obj_to_commands((want, have), module)
+ result["commands"] = commands
+
+ if commands:
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ if want:
+ check_declarative_intent_params(want, module, result)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlans.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlans.py
new file mode 100644
index 00000000..8cc4ca90
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vlans.py
@@ -0,0 +1,438 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+# WARNING #
+#############################################
+#
+# This file is auto generated by the resource
+# module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+# by the resource module builder.
+#
+# Changes should be made in the model used to
+# generate this file or in the resource module
+# builder template.
+#
+#############################################
+
+"""
+The module file for nxos_vlans
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vlans
+short_description: VLANs resource module
+description: This module creates and manages VLAN configurations on Cisco NX-OS.
+version_added: 1.0.0
+author: Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOS 7.3.(0)D1(1) on VIRL
+options:
+ running_config:
+ description:
+ - This option is used only with state I(parsed).
+ - The value of this option should be the output received from the NX-OS device
+ by executing the commands B(show vlans | json-pretty) and B(show running-config
+ | section ^vlan) in order and delimited by a line.
+ - The state I(parsed) reads the configuration from C(running_config) option and
+ transforms it into Ansible structured data as per the resource module's argspec
+ and the value is then returned in the I(parsed) key within the result.
+ type: str
+ config:
+ description: A dictionary of Vlan options
+ type: list
+ elements: dict
+ suboptions:
+ vlan_id:
+ description:
+ - Vlan ID.
+ type: int
+ required: true
+ name:
+ description:
+ - Name of VLAN.
+ type: str
+ state:
+ description:
+ - Manage operational state of the vlan.
+ type: str
+ choices:
+ - active
+ - suspend
+ enabled:
+ description:
+ - Manage administrative state of the vlan.
+ type: bool
+ mode:
+ description:
+ - Set vlan mode to classical ethernet or fabricpath. This is a valid option
+ for Nexus 5000, 6000 and 7000 series.
+ type: str
+ choices:
+ - ce
+ - fabricpath
+ mapped_vni:
+ description:
+ - The Virtual Network Identifier (VNI) ID that is mapped to the VLAN.
+ type: int
+ state:
+ description:
+ - The state of the configuration after module completion.
+ - The state I(overridden) would override the configuration of all the
+ VLANs on the device (including VLAN 1) with the provided configuration in
+ the task. Use caution with this state.
+ type: str
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ - gathered
+ - rendered
+ - parsed
+ default: merged
+
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+# vlan 1
+
+- name: Merge provided configuration with device configuration.
+ cisco.nxos.nxos_vlans:
+ config:
+ - vlan_id: 5
+ name: test-vlan5
+ - vlan_id: 10
+ enabled: false
+ state: merged
+
+# After state:
+# ------------
+# vlan 5
+# name test-vlan5
+# state active
+# no shutdown
+# vlan 10
+# state active
+# shutdown
+
+
+# Using replaced
+
+# Before state:
+# -------------
+# vlan 1
+# vlan 5
+# name test-vlan5
+# vlan 10
+# shutdown
+
+- name: Replace device configuration of specified vlan with provided configuration.
+ cisco.nxos.nxos_vlans:
+ config:
+ - vlan_id: 5
+ name: test-vlan
+ enabled: false
+ - vlan_id: 10
+ enabled: false
+ state: replaced
+
+# After state:
+# ------------
+# vlan 1
+# vlan 5
+# name test-vlan
+# state active
+# shutdown
+# vlan 10
+# state active
+# shutdown
+
+
+# Using overridden
+
+# Before state:
+# -------------
+# vlan 1
+# vlan 3
+# name testing
+# vlan 5
+# name test-vlan5
+# shutdown
+# vlan 10
+# shutdown
+
+- name: Override device configuration of all vlans with provided configuration.
+ cisco.nxos.nxos_vlans:
+ config:
+ - vlan_id: 5
+ name: test-vlan
+ - vlan_id: 10
+ state: active
+ state: overridden
+
+# After state:
+# ------------
+# vlan 5
+# name test-vlan
+# state active
+# no shutdown
+# vlan 10
+# state active
+# no shutdown
+
+
+# Using deleted
+
+# Before state:
+# -------------
+# vlan 1
+# vlan 5
+# vlan 10
+
+- name: Delete vlans.
+ cisco.nxos.nxos_vlans:
+ config:
+ - vlan_id: 5
+ - vlan_id: 10
+ state: deleted
+
+# After state:
+# ------------
+#
+
+# Using rendered
+
+- name: Use rendered state to convert task input to device specific commands
+ cisco.nxos.nxos_vlans:
+ config:
+ - vlan_id: 5
+ name: vlan5
+ mapped_vni: 100
+
+ - vlan_id: 6
+ name: vlan6
+ state: suspend
+ state: rendered
+
+# Task Output (redacted)
+# -----------------------
+
+# rendered:
+# - vlan 5
+# - name vlan5
+# - vn-segment 100
+# - vlan 6
+# - name vlan6
+# - state suspend
+
+# Using parsed
+
+# parsed.cfg
+# ------------
+# {
+# "TABLE_vlanbrief": {
+# "ROW_vlanbrief": [
+# {
+# "vlanshowbr-vlanid": "1",
+# "vlanshowbr-vlanid-utf": "1",
+# "vlanshowbr-vlanname": "default",
+# "vlanshowbr-vlanstate": "active",
+# "vlanshowbr-shutstate": "noshutdown"
+# },
+# {
+# "vlanshowbr-vlanid": "5",
+# "vlanshowbr-vlanid-utf": "5",
+# "vlanshowbr-vlanname": "vlan5",
+# "vlanshowbr-vlanstate": "suspend",
+# "vlanshowbr-shutstate": "noshutdown"
+# },
+# {
+# "vlanshowbr-vlanid": "6",
+# "vlanshowbr-vlanid-utf": "6",
+# "vlanshowbr-vlanname": "VLAN0006",
+# "vlanshowbr-vlanstate": "active",
+# "vlanshowbr-shutstate": "noshutdown"
+# },
+# {
+# "vlanshowbr-vlanid": "7",
+# "vlanshowbr-vlanid-utf": "7",
+# "vlanshowbr-vlanname": "vlan7",
+# "vlanshowbr-vlanstate": "active",
+# "vlanshowbr-shutstate": "noshutdown"
+# }
+# ]
+# },
+# "TABLE_mtuinfo": {
+# "ROW_mtuinfo": [
+# {
+# "vlanshowinfo-vlanid": "1",
+# "vlanshowinfo-media-type": "enet",
+# "vlanshowinfo-vlanmode": "ce-vlan"
+# },
+# {
+# "vlanshowinfo-vlanid": "5",
+# "vlanshowinfo-media-type": "enet",
+# "vlanshowinfo-vlanmode": "ce-vlan"
+# },
+# {
+# "vlanshowinfo-vlanid": "6",
+# "vlanshowinfo-media-type": "enet",
+# "vlanshowinfo-vlanmode": "ce-vlan"
+# },
+# {
+# "vlanshowinfo-vlanid": "7",
+# "vlanshowinfo-media-type": "enet",
+# "vlanshowinfo-vlanmode": "ce-vlan"
+# }
+# ]
+# }
+# }
+#
+# vlan 1,5-7
+# vlan 5
+# state suspend
+# name vlan5
+# vlan 7
+# name vlan7
+# vn-segment 100
+
+- name: Use parsed state to convert externally supplied config to structured format
+ cisco.nxos.nxos_vlans:
+ running_config: "{{ lookup('file', 'parsed.cfg') }}"
+ state: parsed
+
+# Task output (redacted)
+# -----------------------
+
+# parsed:
+# - vlan_id: 5
+# enabled: True
+# mode: "ce"
+# name: "vlan5"
+# state: suspend
+#
+# - vlan_id: 6
+# enabled: True
+# mode: "ce"
+# state: active
+#
+# - vlan_id: 7
+# enabled: True
+# mode: "ce"
+# name: "vlan7"
+# state: active
+# mapped_vni: 100
+
+# Using gathered
+
+# Existing device config state
+# -------------------------------
+# nxos-9k# show vlan | json
+# {"TABLE_vlanbrief": {"ROW_vlanbrief": [{"vlanshowbr-vlanid": "1", "vlanshowbr-vlanid-utf": "1", "vlanshowbr-vlanname": "default", "vlanshowbr-vlanstate
+# ": "active", "vlanshowbr-shutstate": "noshutdown"}, {"vlanshowbr-vlanid": "5", "vlanshowbr-vlanid-utf": "5", "vlanshowbr-vlanname": "vlan5", "vlanshowb
+# r-vlanstate": "suspend", "vlanshowbr-shutstate": "noshutdown"}, {"vlanshowbr-vlanid": "6", "vlanshowbr-vlanid-utf": "6", "vlanshowbr-vlanname": "VLAN00
+# 06", "vlanshowbr-vlanstate": "active", "vlanshowbr-shutstate": "noshutdown"}, {"vlanshowbr-vlanid": "7", "vlanshowbr-vlanid-utf": "7", "vlanshowbr-vlan
+# name": "vlan7", "vlanshowbr-vlanstate": "active", "vlanshowbr-shutstate": "shutdown"}]}, "TABLE_mtuinfo": {"ROW_mtuinfo": [{"vlanshowinfo-vlanid": "1",
+# "vlanshowinfo-media-type": "enet", "vlanshowinfo-vlanmode": "ce-vlan"}, {"vlanshowinfo-vlanid": "5", "vlanshowinfo-media-type": "enet", "vlanshowinfo-
+# vlanmode": "ce-vlan"}, {"vlanshowinfo-vlanid": "6", "vlanshowinfo-media-type": "enet", "vlanshowinfo-vlanmode": "ce-vlan"}, {"vlanshowinfo-vlanid": "7"
+# , "vlanshowinfo-media-type": "enet", "vlanshowinfo-vlanmode": "ce-vlan"}]}}
+#
+# nxos-9k# show running-config | section ^vlan
+# vlan 1,5-7
+# vlan 5
+# state suspend
+# name vlan5
+# vlan 7
+# shutdown
+# name vlan7
+# vn-segment 190
+
+- name: Gather vlans facts from the device using nxos_vlans
+ cisco.nxos.nxos_vlans:
+ state: gathered
+
+# Task output (redacted)
+# -----------------------
+# gathered:
+# - vlan_id: 5
+# enabled: True
+# mode: "ce"
+# name: "vlan5"
+# state: suspend
+#
+# - vlan_id: 6
+# enabled: True
+# mode: "ce"
+# state: active
+#
+# - vlan_id: 7
+# enabled: False
+# mode: "ce"
+# name: "vlan7"
+# state: active
+# mapped_vni: 190
+"""
+RETURN = """
+before:
+ description: The configuration as structured data prior to module invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+after:
+ description: The configuration as structured data after module completion.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['vlan 5', 'name test-vlan5', 'state suspend']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.vlans.vlans import (
+ VlansArgs,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.vlans.vlans import (
+ Vlans,
+)
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(
+ argument_spec=VlansArgs.argument_spec, supports_check_mode=True
+ )
+
+ result = Vlans(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc.py
new file mode 100644
index 00000000..635af126
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc.py
@@ -0,0 +1,482 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vpc
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages global VPC configuration
+description:
+- Manages global VPC configuration
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- The feature vpc must be enabled before this module can be used
+- If not using management vrf, vrf must be globally on the device before using in
+ the pkl config
+- Although source IP isn't required on the command line it is required when using
+ this module. The PKL VRF must also be configured prior to using this module.
+- Both pkl_src and pkl_dest are needed when changing PKL VRF.
+options:
+ domain:
+ description:
+ - VPC domain
+ required: true
+ type: str
+ role_priority:
+ description:
+ - Role priority for device. Remember lower is better.
+ type: str
+ system_priority:
+ description:
+ - System priority device. Remember they must match between peers.
+ type: str
+ pkl_src:
+ description:
+ - Source IP address used for peer keepalive link
+ type: str
+ pkl_dest:
+ description:
+ - Destination (remote) IP address used for peer keepalive link
+ - pkl_dest is required whenever pkl options are used.
+ type: str
+ pkl_vrf:
+ description:
+ - VRF used for peer keepalive link
+ - The VRF must exist on the device before using pkl_vrf.
+ - "(Note) 'default' is an overloaded term: Default vrf context for pkl_vrf is
+ 'management'; 'pkl_vrf: default' refers to the literal 'default' rib."
+ type: str
+ peer_gw:
+ description:
+ - Enables/Disables peer gateway
+ type: bool
+ auto_recovery:
+ description:
+ - Enables/Disables auto recovery on platforms that support disable
+ - timers are not modifiable with this attribute
+ - mutually exclusive with auto_recovery_reload_delay
+ type: bool
+ auto_recovery_reload_delay:
+ description:
+ - Manages auto-recovery reload-delay timer in seconds
+ - mutually exclusive with auto_recovery
+ type: str
+ delay_restore:
+ description:
+ - manages delay restore command and config value in seconds
+ type: str
+ delay_restore_interface_vlan:
+ description:
+ - manages delay restore interface-vlan command and config value in seconds
+ - not supported on all platforms
+ type: str
+ delay_restore_orphan_port:
+ description:
+ - manages delay restore orphan-port command and config value in seconds
+ - not supported on all platforms
+ type: str
+ state:
+ description:
+ - Manages desired state of the resource
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- name: configure a simple asn
+ cisco.nxos.nxos_vpc:
+ domain: 100
+ role_priority: 1000
+ system_priority: 2000
+ pkl_dest: 192.168.100.4
+ pkl_src: 10.1.100.20
+ peer_gw: true
+ auto_recovery: true
+
+- name: configure
+ cisco.nxos.nxos_vpc:
+ domain: 100
+ role_priority: 32667
+ system_priority: 2000
+ peer_gw: true
+ pkl_src: 10.1.100.2
+ pkl_dest: 192.168.100.4
+ auto_recovery: true
+
+- name: Configure VPC with delay restore and existing keepalive VRF
+ cisco.nxos.nxos_vpc:
+ domain: 10
+ role_priority: 28672
+ system_priority: 2000
+ delay_restore: 180
+ peer_gw: true
+ pkl_src: 1.1.1.2
+ pkl_dest: 1.1.1.1
+ pkl_vrf: vpckeepalive
+ auto_recovery: true
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["vpc domain 100",
+ "peer-keepalive destination 192.168.100.4 source 10.1.100.20 vrf management",
+ "auto-recovery", "peer-gateway"]
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+CONFIG_ARGS = {
+ "role_priority": "role priority {role_priority}",
+ "system_priority": "system-priority {system_priority}",
+ "delay_restore": "delay restore {delay_restore}",
+ "delay_restore_interface_vlan": "delay restore interface-vlan {delay_restore_interface_vlan}",
+ "delay_restore_orphan_port": "delay restore orphan-port {delay_restore_orphan_port}",
+ "peer_gw": "{peer_gw} peer-gateway",
+ "auto_recovery": "{auto_recovery} auto-recovery",
+ "auto_recovery_reload_delay": "auto-recovery reload-delay {auto_recovery_reload_delay}",
+}
+
+PARAM_TO_DEFAULT_KEYMAP = {
+ "delay_restore": "60",
+ "delay_restore_interface_vlan": "10",
+ "delay_restore_orphan_port": "0",
+ "role_priority": "32667",
+ "system_priority": "32667",
+ "peer_gw": False,
+ "auto_recovery_reload_delay": 240,
+}
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_vrf_list(module):
+
+ try:
+ body = run_commands(module, ["show vrf all | json"])[0]
+ vrf_table = body["TABLE_vrf"]["ROW_vrf"]
+ except (KeyError, AttributeError):
+ return []
+
+ vrf_list = []
+ if vrf_table:
+ for each in vrf_table:
+ vrf_list.append(str(each["vrf_name"].lower()))
+
+ return vrf_list
+
+
+def get_auto_recovery_default(module):
+ auto = False
+ data = run_commands(module, ["show inventory | json"])[0]
+ pid = data["TABLE_inv"]["ROW_inv"][0]["productid"]
+ if re.search(r"N7K", pid):
+ auto = True
+ elif re.search(r"N9K", pid):
+ data = run_commands(module, ["show hardware | json"])[0]
+ ver = data["kickstart_ver_str"]
+ if re.search(r"7.0\(3\)F", ver):
+ auto = True
+
+ return auto
+
+
+def get_vpc(module):
+ body = run_commands(module, ["show vpc | json"])[0]
+ if body:
+ domain = str(body["vpc-domain-id"])
+ else:
+ body = run_commands(module, ["show run vpc | inc domain"])[0]
+ if body:
+ domain = body.split()[2]
+ else:
+ domain = "not configured"
+
+ vpc = {}
+ if domain != "not configured":
+ run = get_config(module, flags=["vpc all"])
+ if run:
+ vpc["domain"] = domain
+ for key in PARAM_TO_DEFAULT_KEYMAP.keys():
+ vpc[key] = PARAM_TO_DEFAULT_KEYMAP.get(key)
+ vpc["auto_recovery"] = get_auto_recovery_default(module)
+ vpc_list = run.split("\n")
+ for each in vpc_list:
+ if "role priority" in each:
+ line = each.split()
+ vpc["role_priority"] = line[-1]
+ if "system-priority" in each:
+ line = each.split()
+ vpc["system_priority"] = line[-1]
+ if re.search(r"delay restore \d+", each):
+ line = each.split()
+ vpc["delay_restore"] = line[-1]
+ if "delay restore interface-vlan" in each:
+ line = each.split()
+ vpc["delay_restore_interface_vlan"] = line[-1]
+ if "delay restore orphan-port" in each:
+ line = each.split()
+ vpc["delay_restore_orphan_port"] = line[-1]
+ if "auto-recovery" in each:
+ vpc["auto_recovery"] = False if "no " in each else True
+ line = each.split()
+ vpc["auto_recovery_reload_delay"] = line[-1]
+ if "peer-gateway" in each:
+ vpc["peer_gw"] = False if "no " in each else True
+ if "peer-keepalive destination" in each:
+ # destination is reqd; src & vrf are optional
+ m = re.search(
+ r"destination (?P<pkl_dest>[\d.]+)"
+ r"(?:.* source (?P<pkl_src>[\d.]+))*"
+ r"(?:.* vrf (?P<pkl_vrf>\S+))*",
+ each,
+ )
+ if m:
+ for pkl in m.groupdict().keys():
+ if m.group(pkl):
+ vpc[pkl] = m.group(pkl)
+ return vpc
+
+
+def pkl_dependencies(module, delta, existing):
+ """peer-keepalive dependency checking.
+ 1. 'destination' is required with all pkl configs.
+ 2. If delta has optional pkl keywords present, then all optional pkl
+ keywords in existing must be added to delta, otherwise the device cli
+ will remove those values when the new config string is issued.
+ 3. The desired behavior for this set of properties is to merge changes;
+ therefore if an optional pkl property exists on the device but not
+ in the playbook, then that existing property should be retained.
+ Example:
+ CLI: peer-keepalive dest 10.1.1.1 source 10.1.1.2 vrf orange
+ Playbook: {pkl_dest: 10.1.1.1, pkl_vrf: blue}
+ Result: peer-keepalive dest 10.1.1.1 source 10.1.1.2 vrf blue
+ """
+ pkl_existing = [i for i in existing.keys() if i.startswith("pkl")]
+ for pkl in pkl_existing:
+ param = module.params.get(pkl)
+ if not delta.get(pkl):
+ if param and param == existing[pkl]:
+ # delta is missing this param because it's idempotent;
+ # however another pkl command has changed; therefore
+ # explicitly add it to delta so that the cli retains it.
+ delta[pkl] = existing[pkl]
+ elif param is None and existing[pkl]:
+ # retain existing pkl commands even if not in playbook
+ delta[pkl] = existing[pkl]
+
+
+def get_commands_to_config_vpc(module, vpc, domain, existing):
+ vpc = dict(vpc)
+
+ domain_only = vpc.get("domain")
+
+ commands = []
+ if "pkl_dest" in vpc:
+ pkl_command = "peer-keepalive destination {pkl_dest}".format(**vpc)
+ if "pkl_src" in vpc:
+ pkl_command += " source {pkl_src}".format(**vpc)
+ if "pkl_vrf" in vpc:
+ pkl_command += " vrf {pkl_vrf}".format(**vpc)
+ commands.append(pkl_command)
+
+ if "auto_recovery" in vpc:
+ if not vpc.get("auto_recovery"):
+ vpc["auto_recovery"] = "no"
+ else:
+ vpc["auto_recovery"] = ""
+
+ if "peer_gw" in vpc:
+ if not vpc.get("peer_gw"):
+ vpc["peer_gw"] = "no"
+ else:
+ vpc["peer_gw"] = ""
+
+ for param in vpc:
+ command = CONFIG_ARGS.get(param)
+ if command is not None:
+ command = command.format(**vpc).strip()
+ if "peer-gateway" in command:
+ commands.append("terminal dont-ask")
+ commands.append(command)
+
+ if commands or domain_only:
+ commands.insert(0, "vpc domain {0}".format(domain))
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ domain=dict(required=True, type="str"),
+ role_priority=dict(required=False, type="str"),
+ system_priority=dict(required=False, type="str"),
+ pkl_src=dict(required=False),
+ pkl_dest=dict(required=False),
+ pkl_vrf=dict(required=False),
+ peer_gw=dict(required=False, type="bool"),
+ auto_recovery=dict(required=False, type="bool"),
+ auto_recovery_reload_delay=dict(required=False, type="str"),
+ delay_restore=dict(required=False, type="str"),
+ delay_restore_interface_vlan=dict(required=False, type="str"),
+ delay_restore_orphan_port=dict(required=False, type="str"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ mutually_exclusive = [("auto_recovery", "auto_recovery_reload_delay")]
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ results = {"changed": False, "warnings": warnings}
+
+ domain = module.params["domain"]
+ role_priority = module.params["role_priority"]
+ system_priority = module.params["system_priority"]
+ pkl_src = module.params["pkl_src"]
+ pkl_dest = module.params["pkl_dest"]
+ pkl_vrf = module.params["pkl_vrf"]
+ peer_gw = module.params["peer_gw"]
+ auto_recovery = module.params["auto_recovery"]
+ auto_recovery_reload_delay = module.params["auto_recovery_reload_delay"]
+ delay_restore = module.params["delay_restore"]
+ delay_restore_interface_vlan = module.params[
+ "delay_restore_interface_vlan"
+ ]
+ delay_restore_orphan_port = module.params["delay_restore_orphan_port"]
+ state = module.params["state"]
+
+ args = dict(
+ domain=domain,
+ role_priority=role_priority,
+ system_priority=system_priority,
+ pkl_src=pkl_src,
+ pkl_dest=pkl_dest,
+ pkl_vrf=pkl_vrf,
+ peer_gw=peer_gw,
+ auto_recovery=auto_recovery,
+ auto_recovery_reload_delay=auto_recovery_reload_delay,
+ delay_restore=delay_restore,
+ delay_restore_interface_vlan=delay_restore_interface_vlan,
+ delay_restore_orphan_port=delay_restore_orphan_port,
+ )
+
+ if not pkl_dest:
+ if pkl_src:
+ module.fail_json(
+ msg="dest IP for peer-keepalive is required"
+ " when src IP is present"
+ )
+ elif pkl_vrf:
+ if pkl_vrf != "management":
+ module.fail_json(
+ msg="dest and src IP for peer-keepalive are required"
+ " when vrf is present"
+ )
+ else:
+ module.fail_json(
+ msg="dest IP for peer-keepalive is required"
+ " when vrf is present"
+ )
+ if pkl_vrf:
+ if pkl_vrf.lower() not in get_vrf_list(module):
+ module.fail_json(
+ msg="The VRF you are trying to use for the peer "
+ "keepalive link is not on device yet. Add it"
+ " first, please."
+ )
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ existing = get_vpc(module)
+
+ commands = []
+ if state == "present":
+ delta = {}
+ for key, value in proposed.items():
+ if str(value).lower() == "default" and key != "pkl_vrf":
+ # 'default' is a reserved word for vrf
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key)
+ if existing.get(key) != value:
+ delta[key] = value
+
+ if delta:
+ pkl_dependencies(module, delta, existing)
+ command = get_commands_to_config_vpc(
+ module, delta, domain, existing
+ )
+ commands.append(command)
+ elif state == "absent":
+ if existing:
+ if domain != existing["domain"]:
+ module.fail_json(
+ msg="You are trying to remove a domain that "
+ "does not exist on the device"
+ )
+ else:
+ commands.append("terminal dont-ask")
+ commands.append("no vpc domain {0}".format(domain))
+
+ cmds = flatten_list(commands)
+ results["commands"] = cmds
+
+ if cmds:
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, cmds)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc_interface.py
new file mode 100644
index 00000000..034582ea
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vpc_interface.py
@@ -0,0 +1,353 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vpc_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages interface VPC configuration
+description:
+- Manages interface VPC configuration
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Either vpc or peer_link param is required, but not both.
+- C(state=absent) removes whatever VPC config is on a port-channel if one exists.
+- Re-assigning a vpc or peerlink from one portchannel to another is not supported. The
+ module will force the user to unconfigure an existing vpc/pl before configuring
+ the same value on a new portchannel
+options:
+ portchannel:
+ description:
+ - Group number of the portchannel that will be configured.
+ required: true
+ type: str
+ vpc:
+ description:
+ - VPC group/id that will be configured on associated portchannel.
+ type: str
+ peer_link:
+ description:
+ - Set to true/false for peer link config on associated portchannel.
+ type: bool
+ state:
+ description:
+ - Manages desired state of the resource.
+ choices:
+ - present
+ - absent
+ default: present
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_vpc_interface:
+ portchannel: 10
+ vpc: 100
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface port-channel100", "vpc 10"]
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_portchannel_list(module):
+ portchannels = []
+ pc_list = []
+
+ try:
+ body = run_commands(module, ["show port-channel summary | json"])[0]
+ pc_list = body["TABLE_channel"]["ROW_channel"]
+ except (KeyError, AttributeError, TypeError):
+ return portchannels
+
+ if pc_list:
+ if isinstance(pc_list, dict):
+ pc_list = [pc_list]
+
+ for pc in pc_list:
+ portchannels.append(pc["group"])
+
+ return portchannels
+
+
+def get_existing_portchannel_to_vpc_mappings(module):
+ pc_vpc_mapping = {}
+
+ try:
+ body = run_commands(module, ["show vpc brief | json"])[0]
+ vpc_table = body["TABLE_vpc"]["ROW_vpc"]
+ except (KeyError, AttributeError, TypeError):
+ vpc_table = None
+
+ if vpc_table:
+ if isinstance(vpc_table, dict):
+ vpc_table = [vpc_table]
+
+ for vpc in vpc_table:
+ pc_vpc_mapping[str(vpc["vpc-id"])] = str(vpc["vpc-ifindex"])
+
+ return pc_vpc_mapping
+
+
+def peer_link_exists(module):
+ found = False
+ run = get_config(module, flags=["vpc"])
+
+ vpc_list = run.split("\n")
+ for each in vpc_list:
+ if "peer-link" in each:
+ found = True
+ return found
+
+
+def get_active_vpc_peer_link(module):
+ peer_link = None
+
+ try:
+ body = run_commands(module, ["show vpc brief | json"])[0]
+ peer_link = body["TABLE_peerlink"]["ROW_peerlink"]["peerlink-ifindex"]
+ except (KeyError, AttributeError, TypeError):
+ return peer_link
+
+ return peer_link
+
+
+def get_portchannel_vpc_config(module, portchannel):
+ peer_link_pc = None
+ peer_link = False
+ vpc = ""
+ pc = ""
+ config = {}
+
+ try:
+ body = run_commands(module, ["show vpc brief | json"])[0]
+ table = body["TABLE_peerlink"]["ROW_peerlink"]
+ except (KeyError, AttributeError, TypeError):
+ table = {}
+
+ if table:
+ peer_link_pc = table.get("peerlink-ifindex", None)
+
+ if peer_link_pc:
+ plpc = str(peer_link_pc[2:])
+ if portchannel == plpc:
+ config["portchannel"] = portchannel
+ config["peer-link"] = True
+ config["vpc"] = vpc
+
+ mapping = get_existing_portchannel_to_vpc_mappings(module)
+
+ for existing_vpc, port_channel in mapping.items():
+ port_ch = str(port_channel[2:])
+ if port_ch == portchannel:
+ pc = port_ch
+ vpc = str(existing_vpc)
+
+ config["portchannel"] = pc
+ config["peer-link"] = peer_link
+ config["vpc"] = vpc
+
+ return config
+
+
+def get_commands_to_config_vpc_interface(
+ portchannel, delta, config_value, existing
+):
+ commands = []
+
+ if not delta.get("peer-link") and existing.get("peer-link"):
+ commands.append("no vpc peer-link")
+ commands.insert(0, "interface port-channel{0}".format(portchannel))
+
+ elif delta.get("peer-link") and not existing.get("peer-link"):
+ commands.append("vpc peer-link")
+ commands.insert(0, "interface port-channel{0}".format(portchannel))
+
+ elif delta.get("vpc") and not existing.get("vpc"):
+ command = "vpc {0}".format(config_value)
+ commands.append(command)
+ commands.insert(0, "interface port-channel{0}".format(portchannel))
+
+ return commands
+
+
+def state_present(portchannel, delta, config_value, existing):
+ commands = []
+
+ command = get_commands_to_config_vpc_interface(
+ portchannel, delta, config_value, existing
+ )
+ commands.append(command)
+
+ return commands
+
+
+def state_absent(portchannel, existing):
+ commands = []
+ if existing.get("vpc"):
+ command = "no vpc"
+ commands.append(command)
+ elif existing.get("peer-link"):
+ command = "no vpc peer-link"
+ commands.append(command)
+ if commands:
+ commands.insert(0, "interface port-channel{0}".format(portchannel))
+
+ return commands
+
+
+def main():
+ argument_spec = dict(
+ portchannel=dict(required=True, type="str"),
+ vpc=dict(required=False, type="str"),
+ peer_link=dict(required=False, type="bool"),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=[["vpc", "peer_link"]],
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ commands = []
+ results = {"changed": False, "warnings": warnings}
+
+ portchannel = module.params["portchannel"]
+ vpc = module.params["vpc"]
+ peer_link = module.params["peer_link"]
+ state = module.params["state"]
+
+ args = {"portchannel": portchannel, "vpc": vpc, "peer-link": peer_link}
+ active_peer_link = None
+
+ if portchannel not in get_portchannel_list(module):
+ if not portchannel.isdigit() or int(
+ portchannel
+ ) not in get_portchannel_list(module):
+ module.fail_json(
+ msg="The portchannel you are trying to make a"
+ " VPC or PL is not created yet. "
+ "Create it first!"
+ )
+ if vpc:
+ mapping = get_existing_portchannel_to_vpc_mappings(module)
+
+ if vpc in mapping and portchannel != mapping[vpc].strip("Po"):
+ module.fail_json(
+ msg="This vpc is already configured on "
+ "another portchannel. Remove it first "
+ "before trying to assign it here. ",
+ existing_portchannel=mapping[vpc],
+ )
+
+ for vpcid, existing_pc in mapping.items():
+ if portchannel == existing_pc.strip("Po") and vpcid != vpc:
+ module.fail_json(
+ msg="This portchannel already has another"
+ " VPC configured. Remove it first "
+ "before assigning this one",
+ existing_vpc=vpcid,
+ )
+
+ if peer_link_exists(module):
+ active_peer_link = get_active_vpc_peer_link(module)
+ if active_peer_link[-2:] == portchannel:
+ module.fail_json(
+ msg="That port channel is the current "
+ "PEER LINK. Remove it if you want it"
+ " to be a VPC"
+ )
+ config_value = vpc
+
+ elif peer_link is not None:
+ if peer_link_exists(module):
+ active_peer_link = get_active_vpc_peer_link(module)[2::]
+ if active_peer_link != portchannel:
+ if peer_link:
+ module.fail_json(
+ msg="A peer link already exists on"
+ " the device. Remove it first",
+ current_peer_link="Po{0}".format(active_peer_link),
+ )
+ config_value = "peer-link"
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ existing = get_portchannel_vpc_config(module, portchannel)
+
+ if state == "present":
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ commands = state_present(
+ portchannel, delta, config_value, existing
+ )
+
+ elif state == "absent" and existing:
+ commands = state_absent(portchannel, existing)
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ load_config(module, cmds)
+ results["changed"] = True
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results["commands"] = cmds
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf.py
new file mode 100644
index 00000000..be6a921c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf.py
@@ -0,0 +1,643 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vrf
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages global VRF configuration.
+description:
+- This module provides declarative management of VRFs on CISCO NXOS network devices.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+- Trishna Guha (@trishnaguha)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Cisco NX-OS creates the default VRF by itself. Therefore, you're not allowed to
+ use default as I(vrf) name in this module.
+- C(vrf) name must be shorter than 32 chars.
+- VRF names are not case sensible in NX-OS. Anyway, the name is stored just like it's
+ inserted by the user and it'll not be changed again unless the VRF is removed and
+ re-created. i.e. C(vrf=NTC) will create a VRF named NTC, but running it again with
+ C(vrf=ntc) will not cause a configuration change.
+options:
+ name:
+ description:
+ - Name of VRF to be managed.
+ aliases:
+ - vrf
+ type: str
+ admin_state:
+ description:
+ - Administrative state of the VRF.
+ default: up
+ choices:
+ - up
+ - down
+ type: str
+ vni:
+ description:
+ - Specify virtual network identifier. Valid values are Integer or keyword 'default'.
+ type: str
+ rd:
+ description:
+ - VPN Route Distinguisher (RD). Valid values are a string in one of the route-distinguisher
+ formats (ASN2:NN, ASN4:NN, or IPV4:NN); the keyword 'auto', or the keyword 'default'.
+ type: str
+ interfaces:
+ description:
+ - List of interfaces to check the VRF has been configured correctly or keyword
+ 'default'.
+ type: list
+ elements: str
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vrf
+ C(name) for associated interfaces. If the value in the C(associated_interfaces)
+ does not match with the operational state of vrf interfaces on device it will
+ result in failure.
+ type: list
+ elements: str
+ aggregate:
+ description: List of VRFs definitions.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of VRF to be managed.
+ aliases:
+ - vrf
+ type: str
+ admin_state:
+ description:
+ - Administrative state of the VRF.
+ choices:
+ - up
+ - down
+ type: str
+ vni:
+ description:
+ - Specify virtual network identifier. Valid values are Integer or keyword 'default'.
+ type: str
+ rd:
+ description:
+ - VPN Route Distinguisher (RD). Valid values are a string in one of the route-distinguisher
+ formats (ASN2:NN, ASN4:NN, or IPV4:NN); the keyword 'auto', or the keyword 'default'.
+ type: str
+ interfaces:
+ description:
+ - List of interfaces to check the VRF has been configured correctly or keyword
+ 'default'.
+ type: list
+ elements: str
+ associated_interfaces:
+ description:
+ - This is a intent option and checks the operational state of the for given vrf
+ C(name) for associated interfaces. If the value in the C(associated_interfaces)
+ does not match with the operational state of vrf interfaces on device it will
+ result in failure.
+ type: list
+ elements: str
+ state:
+ description:
+ - Manages desired state of the resource.
+ choices:
+ - present
+ - absent
+ type: str
+ description:
+ description:
+ - Description of the VRF or keyword 'default'.
+ type: str
+ delay:
+ description:
+ - Time in seconds to wait before checking for the operational state on remote
+ device. This wait is applicable for operational state arguments.
+ type: int
+ purge:
+ description:
+ - Purge VRFs not defined in the I(aggregate) parameter.
+ type: bool
+ default: no
+ state:
+ description:
+ - Manages desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ description:
+ description:
+ - Description of the VRF or keyword 'default'.
+ type: str
+ delay:
+ description:
+ - Time in seconds to wait before checking for the operational state on remote
+ device. This wait is applicable for operational state arguments.
+ default: 10
+ type: int
+"""
+
+EXAMPLES = """
+- name: Ensure ntc VRF exists on switch
+ cisco.nxos.nxos_vrf:
+ name: ntc
+ description: testing
+ state: present
+
+- name: Aggregate definition of VRFs
+ cisco.nxos.nxos_vrf:
+ aggregate:
+ - {name: test1, description: Testing, admin_state: down}
+ - {name: test2, interfaces: Ethernet1/2}
+
+- name: Aggregate definitions of VRFs with Purge
+ cisco.nxos.nxos_vrf:
+ aggregate:
+ - {name: ntc1, description: purge test1}
+ - {name: ntc2, description: purge test2}
+ state: present
+ purge: yes
+
+- name: Delete VRFs exist on switch
+ cisco.nxos.nxos_vrf:
+ aggregate:
+ - {name: ntc1}
+ - {name: ntc2}
+ state: absent
+
+- name: Assign interfaces to VRF declaratively
+ cisco.nxos.nxos_vrf:
+ name: test1
+ interfaces:
+ - Ethernet2/3
+ - Ethernet2/5
+
+- name: Check interfaces assigned to VRF
+ cisco.nxos.nxos_vrf:
+ name: test1
+ associated_interfaces:
+ - Ethernet2/3
+ - Ethernet2/5
+
+- name: Ensure VRF is tagged with interface Ethernet2/5 only (Removes from Ethernet2/3)
+ cisco.nxos.nxos_vrf:
+ name: test1
+ interfaces:
+ - Ethernet2/5
+
+- name: Delete VRF
+ cisco.nxos.nxos_vrf:
+ name: ntc
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - vrf context ntc
+ - no shutdown
+ - interface Ethernet1/2
+ - no switchport
+ - vrf member test2
+"""
+
+import re
+import time
+
+from copy import deepcopy
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+ get_interface_type,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ remove_default_spec,
+)
+
+
+def search_obj_in_list(name, lst):
+ for o in lst:
+ if o["name"] == name:
+ return o
+
+
+def execute_show_command(command, module):
+ if "show run" not in command:
+ output = "json"
+ else:
+ output = "text"
+ cmds = [{"command": command, "output": output}]
+ body = run_commands(module, cmds)
+ return body
+
+
+def get_existing_vrfs(module):
+ objs = list()
+ command = "show vrf all"
+ try:
+ body = execute_show_command(command, module)[0]
+ except IndexError:
+ return list()
+ try:
+ vrf_table = body["TABLE_vrf"]["ROW_vrf"]
+ except (TypeError, IndexError, KeyError):
+ return list()
+
+ if isinstance(vrf_table, list):
+ for vrf in vrf_table:
+ obj = {}
+ obj["name"] = vrf["vrf_name"]
+ objs.append(obj)
+
+ elif isinstance(vrf_table, dict):
+ obj = {}
+ obj["name"] = vrf_table["vrf_name"]
+ objs.append(obj)
+
+ return objs
+
+
+def map_obj_to_commands(updates, module):
+ commands = list()
+ want, have = updates
+ state = module.params["state"]
+ purge = module.params["purge"]
+
+ args = ("rd", "description", "vni")
+
+ for w in want:
+ name = w["name"]
+ admin_state = w["admin_state"]
+ vni = w["vni"]
+ interfaces = w.get("interfaces") or []
+ if purge:
+ state = "absent"
+ else:
+ state = w["state"]
+ del w["state"]
+
+ obj_in_have = search_obj_in_list(name, have)
+ if state == "absent" and obj_in_have:
+ commands.append("no vrf context {0}".format(name))
+
+ elif state == "present":
+ if not obj_in_have:
+ commands.append("vrf context {0}".format(name))
+ for item in args:
+ candidate = w.get(item)
+ if candidate and candidate != "default":
+ cmd = item + " " + str(candidate)
+ commands.append(cmd)
+ if admin_state == "up":
+ commands.append("no shutdown")
+ elif admin_state == "down":
+ commands.append("shutdown")
+ commands.append("exit")
+
+ if interfaces and interfaces[0] != "default":
+ for i in interfaces:
+ commands.append("interface {0}".format(i))
+ if get_interface_type(i) in (
+ "ethernet",
+ "portchannel",
+ ):
+ commands.append("no switchport")
+ commands.append("vrf member {0}".format(name))
+
+ else:
+ # If vni is already configured on vrf, unconfigure it first.
+ if vni:
+ if obj_in_have.get("vni") and vni != obj_in_have.get(
+ "vni"
+ ):
+ commands.append(
+ "no vni {0}".format(obj_in_have.get("vni"))
+ )
+
+ for item in args:
+ candidate = w.get(item)
+ if candidate == "default":
+ if obj_in_have.get(item):
+ cmd = "no " + item + " " + obj_in_have.get(item)
+ commands.append(cmd)
+ elif candidate and candidate != obj_in_have.get(item):
+ cmd = item + " " + str(candidate)
+ commands.append(cmd)
+ if admin_state and admin_state != obj_in_have.get(
+ "admin_state"
+ ):
+ if admin_state == "up":
+ commands.append("no shutdown")
+ elif admin_state == "down":
+ commands.append("shutdown")
+
+ if commands:
+ commands.insert(0, "vrf context {0}".format(name))
+ commands.append("exit")
+
+ if interfaces and interfaces[0] != "default":
+ if not obj_in_have["interfaces"]:
+ for i in interfaces:
+ commands.append("vrf context {0}".format(name))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ if get_interface_type(i) in (
+ "ethernet",
+ "portchannel",
+ ):
+ commands.append("no switchport")
+ commands.append("vrf member {0}".format(name))
+
+ elif set(interfaces) != set(obj_in_have["interfaces"]):
+ missing_interfaces = list(
+ set(interfaces) - set(obj_in_have["interfaces"])
+ )
+ for i in missing_interfaces:
+ commands.append("vrf context {0}".format(name))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ if get_interface_type(i) in (
+ "ethernet",
+ "portchannel",
+ ):
+ commands.append("no switchport")
+ commands.append("vrf member {0}".format(name))
+
+ superfluous_interfaces = list(
+ set(obj_in_have["interfaces"]) - set(interfaces)
+ )
+ for i in superfluous_interfaces:
+ commands.append("vrf context {0}".format(name))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ if get_interface_type(i) in (
+ "ethernet",
+ "portchannel",
+ ):
+ commands.append("no switchport")
+ commands.append("no vrf member {0}".format(name))
+ elif interfaces and interfaces[0] == "default":
+ if obj_in_have["interfaces"]:
+ for i in obj_in_have["interfaces"]:
+ commands.append("vrf context {0}".format(name))
+ commands.append("exit")
+ commands.append("interface {0}".format(i))
+ if get_interface_type(i) in (
+ "ethernet",
+ "portchannel",
+ ):
+ commands.append("no switchport")
+ commands.append("no vrf member {0}".format(name))
+
+ if purge:
+ existing = get_existing_vrfs(module)
+ if existing:
+ for h in existing:
+ if h["name"] in ("default", "management"):
+ pass
+ else:
+ obj_in_want = search_obj_in_list(h["name"], want)
+ if not obj_in_want:
+ commands.append("no vrf context {0}".format(h["name"]))
+
+ return commands
+
+
+def validate_vrf(name, module):
+ if name:
+ name = name.strip()
+ if name == "default":
+ module.fail_json(msg="cannot use default as name of a VRF")
+ elif len(name) > 32:
+ module.fail_json(
+ msg="VRF name exceeded max length of 32", name=name
+ )
+ else:
+ return name
+
+
+def map_params_to_obj(module):
+ obj = []
+ aggregate = module.params.get("aggregate")
+ if aggregate:
+ for item in aggregate:
+ for key in item:
+ if item.get(key) is None:
+ item[key] = module.params[key]
+
+ d = item.copy()
+ d["name"] = validate_vrf(d["name"], module)
+ obj.append(d)
+ else:
+ obj.append(
+ {
+ "name": validate_vrf(module.params["name"], module),
+ "description": module.params["description"],
+ "vni": module.params["vni"],
+ "rd": module.params["rd"],
+ "admin_state": module.params["admin_state"],
+ "state": module.params["state"],
+ "interfaces": module.params["interfaces"],
+ "associated_interfaces": module.params[
+ "associated_interfaces"
+ ],
+ }
+ )
+ return obj
+
+
+def get_value(arg, config, module):
+ extra_arg_regex = re.compile(r"(?:{0}\s)(?P<value>.*)$".format(arg), re.M)
+ value = ""
+ if arg in config:
+ value = extra_arg_regex.search(config).group("value")
+ return value
+
+
+def map_config_to_obj(want, element_spec, module):
+ objs = list()
+
+ for w in want:
+ obj = deepcopy(element_spec)
+ del obj["delay"]
+ del obj["state"]
+
+ command = "show vrf {0}".format(w["name"])
+ try:
+ body = execute_show_command(command, module)[0]
+ vrf_table = body["TABLE_vrf"]["ROW_vrf"]
+ except (TypeError, IndexError):
+ return list()
+
+ name = vrf_table["vrf_name"]
+ obj["name"] = name
+ obj["admin_state"] = vrf_table["vrf_state"].lower()
+
+ command = "show run all | section vrf.context.{0}".format(name)
+ body = execute_show_command(command, module)[0]
+ extra_params = ["vni", "rd", "description"]
+ for param in extra_params:
+ obj[param] = get_value(param, body, module)
+
+ obj["interfaces"] = []
+ command = "show vrf {0} interface".format(name)
+ try:
+ body = execute_show_command(command, module)[0]
+ vrf_int = body["TABLE_if"]["ROW_if"]
+ except (TypeError, IndexError):
+ vrf_int = None
+
+ if vrf_int:
+ if isinstance(vrf_int, list):
+ for i in vrf_int:
+ intf = i["if_name"]
+ obj["interfaces"].append(intf)
+ elif isinstance(vrf_int, dict):
+ intf = vrf_int["if_name"]
+ obj["interfaces"].append(intf)
+
+ objs.append(obj)
+ return objs
+
+
+def check_declarative_intent_params(want, module, element_spec, result):
+
+ have = None
+ is_delay = False
+
+ for w in want:
+ if w.get("associated_interfaces") is None:
+ continue
+
+ if result["changed"] and not is_delay:
+ time.sleep(module.params["delay"])
+ is_delay = True
+
+ if have is None:
+ have = map_config_to_obj(want, element_spec, module)
+
+ for i in w["associated_interfaces"]:
+ obj_in_have = search_obj_in_list(w["name"], have)
+
+ if obj_in_have:
+ interfaces = obj_in_have.get("interfaces")
+ if interfaces is not None and i not in interfaces:
+ module.fail_json(
+ msg="Interface %s not configured on vrf %s"
+ % (i, w["name"])
+ )
+
+
+def vrf_error_check(module, commands, responses):
+ """Checks for VRF config errors and executes a retry in some circumstances.
+ """
+ pattern = "ERROR: Deletion of VRF .* in progress"
+ if re.search(pattern, str(responses)):
+ # Allow delay/retry for VRF changes
+ time.sleep(15)
+ responses = load_config(
+ module, commands, opts={"catch_clierror": True}
+ )
+ if re.search(pattern, str(responses)):
+ module.fail_json(
+ msg="VRF config (and retry) failure: %s " % responses
+ )
+ module.warn("VRF config delayed by VRF deletion - passed on retry")
+
+
+def main():
+ """ main entry point for module execution
+ """
+ element_spec = dict(
+ name=dict(type="str", aliases=["vrf"]),
+ description=dict(type="str"),
+ vni=dict(type="str"),
+ rd=dict(type="str"),
+ admin_state=dict(type="str", default="up", choices=["up", "down"]),
+ interfaces=dict(type="list", elements="str"),
+ associated_interfaces=dict(type="list", elements="str"),
+ delay=dict(type="int", default=10),
+ state=dict(
+ type="str", default="present", choices=["present", "absent"]
+ ),
+ )
+
+ aggregate_spec = deepcopy(element_spec)
+
+ # remove default in aggregate spec, to handle common arguments
+ remove_default_spec(aggregate_spec)
+
+ argument_spec = dict(
+ aggregate=dict(type="list", elements="dict", options=aggregate_spec),
+ purge=dict(type="bool", default=False),
+ )
+
+ argument_spec.update(element_spec)
+ argument_spec.update(nxos_argument_spec)
+
+ required_one_of = [["name", "aggregate"]]
+ mutually_exclusive = [["name", "aggregate"]]
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=required_one_of,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False}
+ if warnings:
+ result["warnings"] = warnings
+
+ want = map_params_to_obj(module)
+ have = map_config_to_obj(want, element_spec, module)
+
+ commands = map_obj_to_commands((want, have), module)
+ result["commands"] = commands
+
+ if commands and not module.check_mode:
+ responses = load_config(
+ module, commands, opts={"catch_clierror": True}
+ )
+ vrf_error_check(module, commands, responses)
+ result["changed"] = True
+
+ check_declarative_intent_params(want, module, element_spec, result)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_af.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_af.py
new file mode 100644
index 00000000..9ac10a0e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_af.py
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vrf_af
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages VRF AF.
+description:
+- Manages VRF AF
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- Default, where supported, restores params default value.
+- In case of C(state=absent) the address-family configuration will be absent. Therefore
+ the options C(route_target_both_auto_evpn) and C(route_targets) are ignored.
+options:
+ vrf:
+ description:
+ - Name of the VRF.
+ required: true
+ type: str
+ afi:
+ description:
+ - Address-Family Identifier (AFI).
+ required: true
+ choices:
+ - ipv4
+ - ipv6
+ type: str
+ route_target_both_auto_evpn:
+ description:
+ - Enable/Disable the EVPN route-target 'auto' setting for both import and export
+ target communities.
+ type: bool
+ route_targets:
+ description:
+ - Specify the route-targets which should be imported and/or exported under the
+ AF. This argument accepts a list of dicts that specify the route-target, the
+ direction (import|export|both) and state of each route-target. Default direction
+ is C(direction=both). See examples.
+ suboptions:
+ rt:
+ description:
+ - Defines the route-target itself
+ required: true
+ type: str
+ direction:
+ description:
+ - Indicates the direction of the route-target (import|export|both)
+ choices:
+ - import
+ - export
+ - both
+ default: both
+ type: str
+ state:
+ description:
+ - Determines whether the route-target with the given direction should be present
+ or not on the device.
+ choices:
+ - present
+ - absent
+ default: present
+ type: str
+ elements: dict
+ type: list
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- cisco.nxos.nxos_vrf_af:
+ vrf: ntc
+ afi: ipv4
+ route_target_both_auto_evpn: true
+ state: present
+
+- cisco.nxos.nxos_vrf_af:
+ vrf: ntc
+ afi: ipv4
+ route_targets:
+ - rt: 65000:1000
+ direction: import
+ - rt: 65001:1000
+ direction: import
+
+- cisco.nxos.nxos_vrf_af:
+ vrf: ntc
+ afi: ipv4
+ route_targets:
+ - rt: 65000:1000
+ direction: import
+ - rt: 65001:1000
+ state: absent
+
+- cisco.nxos.nxos_vrf_af:
+ vrf: ntc
+ afi: ipv4
+ route_targets:
+ - rt: 65000:1000
+ direction: export
+ - rt: 65001:1000
+ direction: export
+
+- cisco.nxos.nxos_vrf_af:
+ vrf: ntc
+ afi: ipv4
+ route_targets:
+ - rt: 65000:1000
+ direction: export
+ state: absent
+
+- cisco.nxos.nxos_vrf_af:
+ vrf: ntc
+ afi: ipv4
+ route_targets:
+ - rt: 65000:1000
+ direction: both
+ state: present
+ - rt: 65001:1000
+ direction: import
+ state: present
+ - rt: 65002:1000
+ direction: both
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["vrf context ntc", "address-family ipv4 unicast"]
+"""
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ NetworkConfig,
+)
+
+import re
+
+
+def match_current_rt(rt, direction, current, rt_commands):
+ command = "route-target %s %s" % (direction, rt.get("rt"))
+ match = re.findall(command, current, re.M)
+ want = bool(rt.get("state") != "absent")
+ if not match and want:
+ rt_commands.append(command)
+ elif match and not want:
+ rt_commands.append("no %s" % command)
+ return rt_commands
+
+
+def main():
+ argument_spec = dict(
+ vrf=dict(required=True),
+ afi=dict(required=True, choices=["ipv4", "ipv6"]),
+ route_target_both_auto_evpn=dict(required=False, type="bool"),
+ state=dict(choices=["present", "absent"], default="present"),
+ route_targets=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ rt=dict(type="str", required=True),
+ direction=dict(
+ choices=["import", "export", "both"], default="both"
+ ),
+ state=dict(choices=["present", "absent"], default="present"),
+ ),
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ result = {"changed": False, "warnings": warnings}
+
+ config_text = get_config(module)
+ config = NetworkConfig(indent=2, contents=config_text)
+
+ path = [
+ "vrf context %s" % module.params["vrf"],
+ "address-family %s unicast" % module.params["afi"],
+ ]
+
+ try:
+ current = config.get_block_config(path)
+ except ValueError:
+ current = None
+
+ commands = list()
+ if current and module.params["state"] == "absent":
+ commands.append("no address-family %s unicast" % module.params["afi"])
+
+ elif module.params["state"] == "present":
+ rt_commands = list()
+
+ if not current:
+ commands.append("address-family %s unicast" % module.params["afi"])
+ current = ""
+
+ have_auto_evpn = "route-target both auto evpn" in current
+ if module.params["route_target_both_auto_evpn"] is not None:
+ want_auto_evpn = bool(module.params["route_target_both_auto_evpn"])
+ if want_auto_evpn and not have_auto_evpn:
+ commands.append("route-target both auto evpn")
+ elif have_auto_evpn and not want_auto_evpn:
+ commands.append("no route-target both auto evpn")
+
+ if module.params["route_targets"] is not None:
+ for rt in module.params["route_targets"]:
+ if rt.get("direction") == "both" or not rt.get("direction"):
+ rt_commands = match_current_rt(
+ rt, "import", current, rt_commands
+ )
+ rt_commands = match_current_rt(
+ rt, "export", current, rt_commands
+ )
+ else:
+ rt_commands = match_current_rt(
+ rt, rt.get("direction"), current, rt_commands
+ )
+
+ if rt_commands:
+ commands.extend(rt_commands)
+
+ if commands and current:
+ commands.insert(
+ 0, "address-family %s unicast" % module.params["afi"]
+ )
+
+ if commands:
+ commands.insert(0, "vrf context %s" % module.params["vrf"])
+ if not module.check_mode:
+ load_config(module, commands)
+ result["changed"] = True
+
+ result["commands"] = commands
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_interface.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_interface.py
new file mode 100644
index 00000000..37826176
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrf_interface.py
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vrf_interface
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages interface specific VRF configuration.
+description:
+- Manages interface specific VRF configuration.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- VRF needs to be added globally with M(cisco.nxos.nxos_vrf) before adding a VRF to an interface.
+- Remove a VRF from an interface will still remove all L3 attributes just as it does
+ from CLI.
+- VRF is not read from an interface until IP address is configured on that interface.
+options:
+ vrf:
+ description:
+ - Name of VRF to be managed.
+ required: true
+ type: str
+ interface:
+ description:
+ - Full name of interface to be managed, i.e. Ethernet1/1.
+ required: true
+ type: str
+ state:
+ description:
+ - Manages desired state of the resource.
+ required: false
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- name: Ensure vrf ntc exists on Eth1/1
+ cisco.nxos.nxos_vrf_interface:
+ vrf: ntc
+ interface: Ethernet1/1
+ state: present
+
+- name: Ensure ntc VRF does not exist on Eth1/1
+ cisco.nxos.nxos_vrf_interface:
+ vrf: ntc
+ interface: Ethernet1/1
+ state: absent
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface loopback16", "vrf member ntc"]
+"""
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+ nxos_argument_spec,
+ normalize_interface,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_interface_type,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+def execute_show_command(command, module):
+ if "show run" not in command:
+ output = "json"
+ else:
+ output = "text"
+ cmds = [{"command": command, "output": output}]
+ return run_commands(module, cmds)[0]
+
+
+def get_interface_mode(interface, intf_type, module):
+ command = "show interface {0}".format(interface)
+ interface = {}
+ mode = "unknown"
+
+ if intf_type in ["ethernet", "portchannel"]:
+ body = execute_show_command(command, module)
+ try:
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ except KeyError:
+ return mode
+
+ if interface_table and isinstance(interface_table, dict):
+ mode = str(interface_table.get("eth_mode", "layer3"))
+ if mode == "access" or mode == "trunk":
+ mode = "layer2"
+ else:
+ return mode
+
+ elif intf_type == "loopback" or intf_type == "svi":
+ mode = "layer3"
+ return mode
+
+
+def get_vrf_list(module):
+ command = "show vrf all"
+ vrf_list = []
+ body = execute_show_command(command, module)
+
+ try:
+ vrf_table = body["TABLE_vrf"]["ROW_vrf"]
+ except (KeyError, AttributeError):
+ return vrf_list
+
+ for each in vrf_table:
+ vrf_list.append(str(each["vrf_name"]))
+
+ return vrf_list
+
+
+def get_interface_info(interface, module):
+ if not interface.startswith("loopback"):
+ interface = interface.capitalize()
+
+ command = "show run interface {0}".format(interface)
+ vrf_regex = r".*vrf\s+member\s+(?P<vrf>\S+).*"
+
+ try:
+ body = execute_show_command(command, module)
+ match_vrf = re.match(vrf_regex, body, re.DOTALL)
+ group_vrf = match_vrf.groupdict()
+ vrf = group_vrf["vrf"]
+ except (AttributeError, TypeError):
+ return ""
+
+ return vrf
+
+
+def is_default(interface, module):
+ command = "show run interface {0}".format(interface)
+
+ try:
+ body = execute_show_command(command, module)
+ raw_list = body.split("\n")
+ if raw_list[-1].startswith("interface"):
+ return True
+ else:
+ return False
+
+ except (KeyError, IndexError):
+ return "DNE"
+
+
+def main():
+ argument_spec = dict(
+ vrf=dict(required=True),
+ interface=dict(type="str", required=True),
+ state=dict(
+ default="present", choices=["present", "absent"], required=False
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ vrf = module.params["vrf"]
+ interface = normalize_interface(module.params["interface"])
+ state = module.params["state"]
+
+ device_info = get_capabilities(module)
+ network_api = device_info.get("network_api", "nxapi")
+
+ current_vrfs = get_vrf_list(module)
+ if vrf not in current_vrfs:
+ warnings.append(
+ "The VRF is not present/active on the device. "
+ "Use nxos_vrf to fix this."
+ )
+
+ intf_type = get_interface_type(interface)
+ if intf_type != "ethernet" and network_api == "cliconf":
+ if is_default(interface, module) == "DNE":
+ module.fail_json(
+ msg="interface does not exist on switch. Verify "
+ "switch platform or create it first with "
+ "nxos_interface if it's a logical interface"
+ )
+
+ mode = get_interface_mode(interface, intf_type, module)
+ if mode == "layer2":
+ module.fail_json(
+ msg="Ensure interface is a Layer 3 port before "
+ "configuring a VRF on an interface. You can "
+ "use nxos_interface"
+ )
+
+ current_vrf = get_interface_info(interface, module)
+ existing = dict(interface=interface, vrf=current_vrf)
+ changed = False
+
+ if not existing["vrf"]:
+ pass
+ elif vrf != existing["vrf"] and state == "absent":
+ module.fail_json(
+ msg="The VRF you are trying to remove "
+ "from the interface does not exist "
+ "on that interface.",
+ interface=interface,
+ proposed_vrf=vrf,
+ existing_vrf=existing["vrf"],
+ )
+
+ commands = []
+ if existing:
+ if state == "absent":
+ if existing and vrf == existing["vrf"]:
+ command = "no vrf member {0}".format(vrf)
+ commands.append(command)
+
+ elif state == "present":
+ if existing["vrf"] != vrf:
+ command = "vrf member {0}".format(vrf)
+ commands.append(command)
+
+ if commands:
+ commands.insert(0, "interface {0}".format(interface))
+
+ if commands:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=commands)
+ else:
+ load_config(module, commands)
+ changed = True
+ if "configure" in commands:
+ commands.pop(0)
+
+ results["commands"] = commands
+ results["changed"] = changed
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrrp.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrrp.py
new file mode 100644
index 00000000..976c6df9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vrrp.py
@@ -0,0 +1,442 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vrrp
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages VRRP configuration on NX-OS switches.
+description:
+- Manages VRRP configuration on NX-OS switches.
+version_added: 1.0.0
+author:
+- Jason Edelman (@jedelman8)
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- VRRP feature needs to be enabled first on the system.
+- SVIs must exist before using this module.
+- Interface must be a L3 port before using this module.
+- C(state=absent) removes the VRRP group if it exists on the device.
+- VRRP cannot be configured on loopback interfaces.
+options:
+ group:
+ description:
+ - VRRP group number.
+ required: true
+ type: str
+ interface:
+ description:
+ - Full name of interface that is being managed for VRRP.
+ required: true
+ type: str
+ interval:
+ description:
+ - Time interval between advertisement or 'default' keyword
+ required: false
+ type: str
+ priority:
+ description:
+ - VRRP priority or 'default' keyword
+ type: str
+ preempt:
+ description:
+ - Enable/Disable preempt.
+ type: bool
+ vip:
+ description:
+ - VRRP virtual IP address or 'default' keyword
+ type: str
+ authentication:
+ description:
+ - Clear text authentication string or 'default' keyword
+ type: str
+ admin_state:
+ description:
+ - Used to enable or disable the VRRP process.
+ choices:
+ - shutdown
+ - no shutdown
+ - default
+ default: shutdown
+ type: str
+ state:
+ description:
+ - Specify desired state of the resource.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+- name: Ensure vrrp group 100 and vip 10.1.100.1 is on vlan10
+ cisco.nxos.nxos_vrrp:
+ interface: vlan10
+ group: 100
+ vip: 10.1.100.1
+
+- name: Ensure removal of the vrrp group config
+ cisco.nxos.nxos_vrrp:
+ interface: vlan10
+ group: 100
+ vip: 10.1.100.1
+ state: absent
+
+- name: Re-config with more params
+ cisco.nxos.nxos_vrrp:
+ interface: vlan10
+ group: 100
+ vip: 10.1.100.1
+ preempt: false
+ priority: 130
+ authentication: AUTHKEY
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface vlan10", "vrrp 150", "address 10.1.15.1",
+ "authentication text testing", "no shutdown"]
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_interface_type,
+)
+from ansible.module_utils.basic import AnsibleModule
+
+
+PARAM_TO_DEFAULT_KEYMAP = {
+ "priority": "100",
+ "interval": "1",
+ "vip": "0.0.0.0",
+ "admin_state": "shutdown",
+}
+
+
+def execute_show_command(command, module):
+ if "show run" not in command:
+ output = "json"
+ else:
+ output = "text"
+
+ commands = [{"command": command, "output": output}]
+ return run_commands(module, commands)[0]
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ if value:
+ new_dict[new_key] = str(value)
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def is_default(interface, module):
+ command = "show run interface {0}".format(interface)
+
+ try:
+ body = execute_show_command(command, module)
+ if "invalid" in body.lower():
+ return "DNE"
+ else:
+ raw_list = body.split("\n")
+ if raw_list[-1].startswith("interface"):
+ return True
+ else:
+ return False
+ except (KeyError):
+ return "DNE"
+
+
+def get_interface_mode(interface, intf_type, module):
+ command = "show interface {0}".format(interface)
+ interface = {}
+ mode = "unknown"
+ body = execute_show_command(command, module)
+ interface_table = body["TABLE_interface"]["ROW_interface"]
+ name = interface_table.get("interface")
+
+ if intf_type in ["ethernet", "portchannel"]:
+ mode = str(interface_table.get("eth_mode", "layer3"))
+
+ if mode == "access" or mode == "trunk":
+ mode = "layer2"
+ elif intf_type == "svi":
+ mode = "layer3"
+
+ return mode, name
+
+
+def get_vrr_status(group, module, interface):
+ command = "show run all | section interface.{0}$".format(interface)
+ body = execute_show_command(command, module)
+ vrf_index = None
+ admin_state = "shutdown"
+
+ if body:
+ splitted_body = body.splitlines()
+ for index in range(0, len(splitted_body) - 1):
+ if splitted_body[index].strip() == "vrrp {0}".format(group):
+ vrf_index = index
+ vrf_section = splitted_body[vrf_index::]
+
+ for line in vrf_section:
+ if line.strip() == "no shutdown":
+ admin_state = "no shutdown"
+ break
+
+ return admin_state
+
+
+def get_existing_vrrp(interface, group, module, name):
+ command = "show vrrp detail interface {0}".format(interface)
+ body = execute_show_command(command, module)
+ vrrp = {}
+
+ vrrp_key = {
+ "sh_group_id": "group",
+ "sh_vip_addr": "vip",
+ "sh_priority": "priority",
+ "sh_group_preempt": "preempt",
+ "sh_auth_text": "authentication",
+ "sh_adv_interval": "interval",
+ }
+
+ try:
+ vrrp_table = body["TABLE_vrrp_group"]
+ except (AttributeError, IndexError, TypeError):
+ return {}
+
+ if isinstance(vrrp_table, dict):
+ vrrp_table = [vrrp_table]
+
+ for each_vrrp in vrrp_table:
+ vrrp_row = each_vrrp["ROW_vrrp_group"]
+ parsed_vrrp = apply_key_map(vrrp_key, vrrp_row)
+
+ if parsed_vrrp["preempt"] == "Disable":
+ parsed_vrrp["preempt"] = False
+ elif parsed_vrrp["preempt"] == "Enable":
+ parsed_vrrp["preempt"] = True
+
+ if parsed_vrrp["group"] == group:
+ parsed_vrrp["admin_state"] = get_vrr_status(group, module, name)
+ return parsed_vrrp
+
+ return vrrp
+
+
+def get_commands_config_vrrp(delta, existing, group):
+ commands = []
+
+ CMDS = {
+ "priority": "priority {0}",
+ "preempt": "preempt",
+ "vip": "address {0}",
+ "interval": "advertisement-interval {0}",
+ "auth": "authentication text {0}",
+ "admin_state": "{0}",
+ }
+
+ for arg in ["vip", "priority", "interval", "admin_state"]:
+ val = delta.get(arg)
+ if val == "default":
+ val = PARAM_TO_DEFAULT_KEYMAP.get(arg)
+ if val != existing.get(arg):
+ commands.append((CMDS.get(arg)).format(val))
+ elif val:
+ commands.append((CMDS.get(arg)).format(val))
+
+ preempt = delta.get("preempt")
+ auth = delta.get("authentication")
+
+ if preempt:
+ commands.append(CMDS.get("preempt"))
+ elif preempt is False:
+ commands.append("no " + CMDS.get("preempt"))
+ if auth:
+ if auth != "default":
+ commands.append((CMDS.get("auth")).format(auth))
+ elif existing.get("authentication"):
+ commands.append("no authentication")
+
+ if commands:
+ commands.insert(0, "vrrp {0}".format(group))
+
+ return commands
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def validate_params(param, module):
+ value = module.params[param]
+
+ if param == "group":
+ try:
+ if int(value) < 1 or int(value) > 255:
+ raise ValueError
+ except ValueError:
+ module.fail_json(
+ msg="Warning! 'group' must be an integer between" " 1 and 255",
+ group=value,
+ )
+ elif param == "priority":
+ try:
+ if int(value) < 1 or int(value) > 254:
+ raise ValueError
+ except ValueError:
+ module.fail_json(
+ msg="Warning! 'priority' must be an integer "
+ "between 1 and 254",
+ priority=value,
+ )
+
+
+def main():
+ argument_spec = dict(
+ group=dict(required=True, type="str"),
+ interface=dict(required=True),
+ interval=dict(required=False, type="str"),
+ priority=dict(required=False, type="str"),
+ preempt=dict(required=False, type="bool"),
+ vip=dict(required=False, type="str"),
+ admin_state=dict(
+ required=False,
+ type="str",
+ choices=["shutdown", "no shutdown", "default"],
+ default="shutdown",
+ ),
+ authentication=dict(required=False, type="str", no_log=True),
+ state=dict(
+ choices=["absent", "present"], required=False, default="present"
+ ),
+ )
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ results = {"changed": False, "commands": [], "warnings": warnings}
+
+ state = module.params["state"]
+ interface = module.params["interface"].lower()
+ group = module.params["group"]
+ priority = module.params["priority"]
+ interval = module.params["interval"]
+ preempt = module.params["preempt"]
+ vip = module.params["vip"]
+ authentication = module.params["authentication"]
+ admin_state = module.params["admin_state"]
+
+ device_info = get_capabilities(module)
+ network_api = device_info.get("network_api", "nxapi")
+
+ if state == "present" and not vip:
+ module.fail_json(msg='the "vip" param is required when state=present')
+
+ intf_type = get_interface_type(interface)
+ if intf_type != "ethernet" and network_api == "cliconf":
+ if is_default(interface, module) == "DNE":
+ module.fail_json(
+ msg="That interface does not exist yet. Create " "it first.",
+ interface=interface,
+ )
+ if intf_type == "loopback":
+ module.fail_json(
+ msg="Loopback interfaces don't support VRRP.",
+ interface=interface,
+ )
+
+ mode, name = get_interface_mode(interface, intf_type, module)
+ if mode == "layer2":
+ module.fail_json(
+ msg="That interface is a layer2 port.\nMake it "
+ "a layer 3 port first.",
+ interface=interface,
+ )
+
+ args = dict(
+ group=group,
+ priority=priority,
+ preempt=preempt,
+ vip=vip,
+ authentication=authentication,
+ interval=interval,
+ admin_state=admin_state,
+ )
+
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ existing = get_existing_vrrp(interface, group, module, name)
+
+ commands = []
+
+ if state == "present":
+ delta = dict(set(proposed.items()).difference(existing.items()))
+ if delta:
+ command = get_commands_config_vrrp(delta, existing, group)
+ if command:
+ commands.append(command)
+ elif state == "absent":
+ if existing:
+ commands.append(["no vrrp {0}".format(group)])
+
+ if commands:
+ commands.insert(0, ["interface {0}".format(interface)])
+ commands = flatten_list(commands)
+ results["commands"] = commands
+ results["changed"] = True
+ if not module.check_mode:
+ load_config(module, commands)
+ if "configure" in commands:
+ commands.pop(0)
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vsan.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vsan.py
new file mode 100644
index 00000000..c68dd0ca
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vsan.py
@@ -0,0 +1,385 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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
+
+
+DOCUMENTATION = """
+module: nxos_vsan
+short_description: Configuration of vsan for Cisco NXOS MDS Switches.
+description:
+- Configuration of vsan for Cisco MDS NXOS.
+version_added: 1.0.0
+author:
+- Suhas Bharadwaj (@srbharadwaj) (subharad@cisco.com)
+notes:
+- Tested against NX-OS 8.4(1)
+options:
+ vsan:
+ description:
+ - List of vsan details to be added or removed
+ type: list
+ elements: dict
+ suboptions:
+ id:
+ description:
+ - Vsan id
+ required: true
+ type: int
+ name:
+ description:
+ - Name of the vsan
+ type: str
+ suspend:
+ description:
+ - suspend the vsan if True
+ type: bool
+ remove:
+ description:
+ - Removes the vsan if True
+ type: bool
+ interface:
+ description:
+ - List of vsan's interfaces to be added
+ type: list
+ elements: str
+"""
+
+EXAMPLES = """
+- name: Test that vsan module works
+ cisco.nxos.nxos_vsan:
+ vsan:
+ - id: 922
+ interface:
+ - fc1/1
+ - fc1/2
+ - port-channel 1
+ name: vsan-SAN-A
+ remove: false
+ suspend: false
+ - id: 923
+ interface:
+ - fc1/11
+ - fc1/21
+ - port-channel 2
+ name: vsan-SAN-B
+ remove: false
+ suspend: true
+ - id: 1923
+ name: vsan-SAN-Old
+ remove: true
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - terminal dont-ask
+ - vsan database
+ - vsan 922 interface fc1/40
+ - vsan 922 interface port-channel 155
+ - no terminal dont-ask
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+import re
+
+__metaclass__ = type
+
+
+class Vsan(object):
+ def __init__(self, vsanid):
+ self.vsanid = vsanid
+ self.vsanname = None
+ self.vsanstate = None
+ self.vsanoperstate = None
+ self.vsaninterfaces = []
+
+
+class GetVsanInfoFromSwitch(object):
+ """docstring for GetVsanInfoFromSwitch"""
+
+ def __init__(self, module):
+ self.module = module
+ self.vsaninfo = {}
+ self.processShowVsan()
+ self.processShowVsanMembership()
+
+ def execute_show_vsan_cmd(self):
+ output = execute_show_command("show vsan", self.module)[0]
+ return output
+
+ def execute_show_vsan_mem_cmd(self):
+ output = execute_show_command("show vsan membership", self.module)[0]
+ return output
+
+ def processShowVsan(self):
+ patv = r"^vsan\s+(\d+)\s+information"
+ patnamestate = "name:(.*)state:(.*)"
+ patoperstate = "operational state:(.*)"
+
+ output = self.execute_show_vsan_cmd().split("\n")
+ for o in output:
+ z = re.match(patv, o.strip())
+ if z:
+ v = z.group(1).strip()
+ self.vsaninfo[v] = Vsan(v)
+
+ z1 = re.match(patnamestate, o.strip())
+ if z1:
+ n = z1.group(1).strip()
+ s = z1.group(2).strip()
+ self.vsaninfo[v].vsanname = n
+ self.vsaninfo[v].vsanstate = s
+
+ z2 = re.match(patoperstate, o.strip())
+ if z2:
+ oper = z2.group(1).strip()
+ self.vsaninfo[v].vsanoperstate = oper
+
+ # 4094/4079 vsan is always present
+ self.vsaninfo["4079"] = Vsan("4079")
+ self.vsaninfo["4094"] = Vsan("4094")
+
+ def processShowVsanMembership(self):
+ patv = r"^vsan\s+(\d+).*"
+ output = self.execute_show_vsan_mem_cmd().split("\n")
+ memlist = []
+ v = None
+ for o in output:
+ z = re.match(patv, o.strip())
+ if z:
+ if v is not None:
+ self.vsaninfo[v].vsaninterfaces = memlist
+ memlist = []
+ v = z.group(1)
+ if "interfaces" not in o:
+ llist = o.strip().split()
+ memlist = memlist + llist
+ self.vsaninfo[v].vsaninterfaces = memlist
+
+ def getVsanInfoObjects(self):
+ return self.vsaninfo
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ output = "text"
+ commands = [{"command": command, "output": output}]
+ return run_commands(module, commands)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def main():
+ vsan_element_spec = dict(
+ id=dict(required=True, type="int"),
+ name=dict(type="str"),
+ remove=dict(type="bool"),
+ suspend=dict(type="bool"),
+ interface=dict(type="list", elements="str"),
+ )
+
+ argument_spec = dict(
+ vsan=dict(type="list", elements="dict", options=vsan_element_spec)
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+ warnings = list()
+ messages = list()
+ commands_executed = list()
+ result = {"changed": False}
+
+ obj = GetVsanInfoFromSwitch(module)
+ dictSwVsanObjs = obj.getVsanInfoObjects()
+
+ commands = []
+ vsan_list = module.params["vsan"]
+
+ for eachvsan in vsan_list:
+ vsanid = str(eachvsan["id"])
+ vsanname = eachvsan["name"]
+ vsanremove = eachvsan["remove"]
+ vsansuspend = eachvsan["suspend"]
+ vsaninterface_list = eachvsan["interface"]
+
+ if int(vsanid) < 1 or int(vsanid) >= 4095:
+ module.fail_json(
+ msg=vsanid
+ + " - This is an invalid vsan. Supported vsan range is 1-4094"
+ )
+
+ if vsanid in dictSwVsanObjs.keys():
+ sw_vsanid = vsanid
+ sw_vsanname = dictSwVsanObjs[vsanid].vsanname
+ sw_vsanstate = dictSwVsanObjs[vsanid].vsanstate
+ sw_vsaninterfaces = dictSwVsanObjs[vsanid].vsaninterfaces
+ else:
+ sw_vsanid = None
+ sw_vsanname = None
+ sw_vsanstate = None
+ sw_vsaninterfaces = []
+
+ if vsanremove:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid)
+ + " is a reserved vsan, hence cannot be removed"
+ )
+ continue
+ if vsanid == sw_vsanid:
+ commands.append("no vsan " + str(vsanid))
+ messages.append("deleting the vsan " + str(vsanid))
+ else:
+ messages.append(
+ "There is no vsan "
+ + str(vsanid)
+ + " present in the switch. Hence there is nothing to delete"
+ )
+ continue
+ else:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid)
+ + " is a reserved vsan, and always present on the switch"
+ )
+ else:
+ if vsanid == sw_vsanid:
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch. Hence there is nothing to configure"
+ )
+ else:
+ commands.append("vsan " + str(vsanid))
+ messages.append("creating vsan " + str(vsanid))
+
+ if vsanname is not None:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid) + " is a reserved vsan, and cannot be renamed"
+ )
+ else:
+ if vsanname == sw_vsanname:
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch, which has the name "
+ + vsanname
+ + " Hence there is nothing to configure"
+ )
+ else:
+ commands.append(
+ "vsan " + str(vsanid) + " name " + vsanname
+ )
+ messages.append(
+ "setting vsan name to "
+ + vsanname
+ + " for vsan "
+ + str(vsanid)
+ )
+
+ if vsansuspend:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid)
+ + " is a reserved vsan, and cannot be suspended"
+ )
+ else:
+ if sw_vsanstate == "suspended":
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch, which is in suspended state "
+ )
+ else:
+ commands.append("vsan " + str(vsanid) + " suspend")
+ messages.append("suspending the vsan " + str(vsanid))
+ else:
+ if sw_vsanstate == "active":
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch, which is in active state "
+ )
+ else:
+ commands.append("no vsan " + str(vsanid) + " suspend")
+ messages.append("no suspending the vsan " + str(vsanid))
+
+ if vsaninterface_list is not None:
+ for each_interface_name in vsaninterface_list:
+ # For fcip,port-channel,vfc-port-channel need to remove the
+ # extra space to compare
+ temp = re.sub(" +", "", each_interface_name)
+ if temp in sw_vsaninterfaces:
+ messages.append(
+ each_interface_name
+ + " is already present in the vsan "
+ + str(vsanid)
+ + " interface list"
+ )
+ else:
+ commands.append(
+ "vsan "
+ + str(vsanid)
+ + " interface "
+ + each_interface_name
+ )
+ messages.append(
+ "adding interface "
+ + each_interface_name
+ + " to vsan "
+ + str(vsanid)
+ )
+
+ if len(commands) != 0:
+ commands = (
+ ["terminal dont-ask"]
+ + ["vsan database"]
+ + commands
+ + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands)
+ commands_executed = cmds
+
+ if commands_executed:
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ commands=commands_executed,
+ msg="Check Mode: No cmds issued to the hosts",
+ )
+ else:
+ result["changed"] = True
+ load_config(module, commands_executed)
+
+ result["messages"] = messages
+ result["commands"] = commands_executed
+ result["warnings"] = warnings
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_domain.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_domain.py
new file mode 100644
index 00000000..1d44b2ce
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_domain.py
@@ -0,0 +1,220 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vtp_domain
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages VTP domain configuration.
+description:
+- Manages VTP domain configuration.
+version_added: 1.0.0
+author:
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- VTP feature must be active on the device to use this module.
+- This module is used to manage only VTP domain names.
+- VTP domain names are case-sensible.
+- If it's never been configured before, VTP version is set to 1 by default. Otherwise,
+ it leaves the previous configured version untouched. Use M(cisco.nxos.nxos_vtp_version)
+ to change it.
+- Use this in combination with M(cisco.nxos.nxos_vtp_password) and M(cisco.nxos.nxos_vtp_version)
+ to fully manage VTP operations.
+options:
+ domain:
+ description:
+ - VTP domain name.
+ required: true
+ type: str
+"""
+
+EXAMPLES = """
+# ENSURE VTP DOMAIN IS CONFIGURED
+- cisco.nxos.nxos_vtp_domain:
+ domain: ntc
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"domain": "ntc"}
+existing:
+ description:
+ - k/v pairs of existing vtp domain
+ returned: always
+ type: dict
+ sample: {"domain": "testing", "version": "2", "vtp_password": "\"}
+end_state:
+ description: k/v pairs of vtp domain after module execution
+ returned: always
+ type: dict
+ sample: {"domain": "ntc", "version": "2", "vtp_password": "\"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["vtp domain ntc"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+)
+from ansible.module_utils.basic import AnsibleModule
+import re
+
+
+def execute_show_command(command, module, output="json"):
+ cmds = [{"command": command, "output": output}]
+ body = run_commands(module, cmds)
+ return body
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_vtp_config(module):
+ command = "show vtp status"
+ body = execute_show_command(command, module, "text")[0]
+ vtp_parsed = {}
+
+ if body:
+ version_regex = r".*VTP version running\s+:\s+(?P<version>\d).*"
+ domain_regex = r".*VTP Domain Name\s+:\s+(?P<domain>\S+).*"
+
+ try:
+ match_version = re.match(version_regex, body, re.DOTALL)
+ version = match_version.groupdict()["version"]
+ except AttributeError:
+ version = ""
+
+ try:
+ match_domain = re.match(domain_regex, body, re.DOTALL)
+ domain = match_domain.groupdict()["domain"]
+ except AttributeError:
+ domain = ""
+
+ if domain and version:
+ vtp_parsed["domain"] = domain
+ vtp_parsed["version"] = version
+ vtp_parsed["vtp_password"] = get_vtp_password(module)
+
+ return vtp_parsed
+
+
+def get_vtp_password(module):
+ command = "show vtp password"
+ output = "json"
+ cap = get_capabilities(module)["device_info"]["network_os_model"]
+ if re.search(r"Nexus 6", cap):
+ output = "text"
+
+ body = execute_show_command(command, module, output)[0]
+
+ if output == "json":
+ password = body.get("passwd", "")
+ else:
+ password = ""
+ rp = r"VTP Password: (\S+)"
+ mo = re.search(rp, body)
+ if mo:
+ password = mo.group(1)
+
+ return str(password)
+
+
+def main():
+ argument_spec = dict(domain=dict(type="str", required=True))
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ domain = module.params["domain"]
+
+ existing = get_vtp_config(module)
+ end_state = existing
+
+ args = dict(domain=domain)
+
+ changed = False
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ commands = []
+ if delta:
+ commands.append(["vtp domain {0}".format(domain)])
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+ end_state = get_vtp_config(module)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["end_state"] = end_state
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_password.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_password.py
new file mode 100644
index 00000000..138f7969
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_password.py
@@ -0,0 +1,287 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vtp_password
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages VTP password configuration.
+description:
+- Manages VTP password configuration.
+version_added: 1.0.0
+author:
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- VTP feature must be active on the device to use this module.
+- This module is used to manage only VTP passwords.
+- Use this in combination with M(cisco.nxos.nxos_vtp_domain) and M(cisco.nxos.nxos_vtp_version) to fully
+ manage VTP operations.
+- You can set/remove password only if a VTP domain already exist.
+- If C(state=absent) and no C(vtp_password) is provided, it remove the current VTP
+ password.
+- If C(state=absent) and C(vtp_password) is provided, the proposed C(vtp_password)
+ has to match the existing one in order to remove it.
+options:
+ vtp_password:
+ description:
+ - VTP password
+ type: str
+ state:
+ description:
+ - Manage the state of the resource
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+"""
+
+EXAMPLES = """
+# ENSURE VTP PASSWORD IS SET
+- cisco.nxos.nxos_vtp_password:
+ state: present
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+
+# ENSURE VTP PASSWORD IS REMOVED
+- cisco.nxos.nxos_vtp_password:
+ state: absent
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"vtp_password": "new_ntc"}
+existing:
+ description:
+ - k/v pairs of existing vtp
+ returned: always
+ type: dict
+ sample: {"domain": "ntc", "version": "1", "vtp_password": "ntc"}
+end_state:
+ description: k/v pairs of vtp after module execution
+ returned: always
+ type: dict
+ sample: {"domain": "ntc", "version": "1", "vtp_password": "new_ntc"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["vtp password new_ntc"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+)
+from ansible.module_utils.basic import AnsibleModule
+import re
+
+
+def execute_show_command(command, module, output="json"):
+ cmds = [{"command": command, "output": output}]
+ body = run_commands(module, cmds)
+ return body
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ value = table.get(key)
+ if value:
+ new_dict[new_key] = str(value)
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def get_vtp_config(module):
+ command = "show vtp status"
+
+ body = execute_show_command(command, module, "text")[0]
+ vtp_parsed = {}
+
+ if body:
+ version_regex = r".*VTP version running\s+:\s+(?P<version>\d).*"
+ domain_regex = r".*VTP Domain Name\s+:\s+(?P<domain>\S+).*"
+
+ try:
+ match_version = re.match(version_regex, body, re.DOTALL)
+ version = match_version.groupdict()["version"]
+ except AttributeError:
+ version = ""
+
+ try:
+ match_domain = re.match(domain_regex, body, re.DOTALL)
+ domain = match_domain.groupdict()["domain"]
+ except AttributeError:
+ domain = ""
+
+ if domain and version:
+ vtp_parsed["domain"] = domain
+ vtp_parsed["version"] = version
+ vtp_parsed["vtp_password"] = get_vtp_password(module)
+
+ return vtp_parsed
+
+
+def get_vtp_password(module):
+ command = "show vtp password"
+ output = "json"
+ cap = get_capabilities(module)["device_info"]["network_os_model"]
+ if re.search(r"Nexus 6", cap):
+ output = "text"
+
+ body = execute_show_command(command, module, output)[0]
+
+ if output == "json":
+ password = body.get("passwd", "")
+ else:
+ password = ""
+ rp = r"VTP Password: (\S+)"
+ mo = re.search(rp, body)
+ if mo:
+ password = mo.group(1)
+
+ return str(password)
+
+
+def main():
+ argument_spec = dict(
+ vtp_password=dict(type="str", no_log=True),
+ state=dict(choices=["absent", "present"], default="present"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ vtp_password = module.params["vtp_password"] or None
+ state = module.params["state"]
+
+ existing = get_vtp_config(module)
+ end_state = existing
+
+ args = dict(vtp_password=vtp_password)
+
+ changed = False
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ commands = []
+ if state == "absent":
+ # if vtp_password is not set, some devices returns '\\' or the string 'None'
+ if (
+ not existing["vtp_password"]
+ or existing["vtp_password"] == "\\"
+ or existing["vtp_password"] == "None"
+ ):
+ pass
+ elif vtp_password is not None:
+ if existing["vtp_password"] == proposed["vtp_password"]:
+ commands.append(["no vtp password"])
+ else:
+ module.fail_json(
+ msg="Proposed vtp password doesn't match "
+ "current vtp password. It cannot be "
+ "removed when state=absent. If you are "
+ "trying to change the vtp password, use "
+ "state=present."
+ )
+ else:
+ if not existing.get("domain"):
+ module.fail_json(
+ msg="Cannot remove a vtp password "
+ "before vtp domain is set."
+ )
+
+ elif existing["vtp_password"] != ("\\"):
+ commands.append(["no vtp password"])
+
+ elif state == "present":
+ if delta:
+ if not existing.get("domain"):
+ module.fail_json(
+ msg="Cannot set vtp password " "before vtp domain is set."
+ )
+
+ else:
+ commands.append(["vtp password {0}".format(vtp_password)])
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+ end_state = get_vtp_config(module)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["end_state"] = end_state
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_version.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_version.py
new file mode 100644
index 00000000..b9561d5d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vtp_version.py
@@ -0,0 +1,217 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vtp_version
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages VTP version configuration.
+description:
+- Manages VTP version configuration.
+version_added: 1.0.0
+author:
+- Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- VTP feature must be active on the device to use this module.
+- This module is used to manage only VTP version.
+- Use this in combination with M(cisco.nxos.nxos_vtp_password) and M(cisco.nxos.nxos_vtp_version)
+ to fully manage VTP operations.
+options:
+ version:
+ description:
+ - VTP version number.
+ required: true
+ choices:
+ - '1'
+ - '2'
+ type: str
+"""
+EXAMPLES = """
+# ENSURE VTP VERSION IS 2
+- cisco.nxos.nxos_vtp_version:
+ version: 2
+ host: '{{ inventory_hostname }}'
+ username: '{{ un }}'
+ password: '{{ pwd }}'
+"""
+
+RETURN = """
+proposed:
+ description: k/v pairs of parameters passed into module
+ returned: always
+ type: dict
+ sample: {"version": "2"}
+existing:
+ description:
+ - k/v pairs of existing vtp
+ returned: always
+ type: dict
+ sample: {"domain": "testing", "version": "1", "vtp_password": "\"}
+end_state:
+ description: k/v pairs of vtp after module execution
+ returned: always
+ type: dict
+ sample: {"domain": "testing", "version": "2", "vtp_password": "\"}
+updates:
+ description: command sent to the device
+ returned: always
+ type: list
+ sample: ["vtp version 2"]
+changed:
+ description: check to see if a change was made on the device
+ returned: always
+ type: bool
+ sample: true
+"""
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_capabilities,
+)
+from ansible.module_utils.basic import AnsibleModule
+import re
+
+
+def execute_show_command(command, module, output="json"):
+ cmds = [{"command": command, "output": output}]
+ body = run_commands(module, cmds)
+ return body
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def get_vtp_config(module):
+ command = "show vtp status"
+ body = execute_show_command(command, module, "text")[0]
+ vtp_parsed = {}
+
+ if body:
+ version_regex = r".*VTP version running\s+:\s+(?P<version>\d).*"
+ domain_regex = r".*VTP Domain Name\s+:\s+(?P<domain>\S+).*"
+
+ try:
+ match_version = re.match(version_regex, body, re.DOTALL)
+ version = match_version.groupdict()["version"]
+ except AttributeError:
+ version = ""
+
+ try:
+ match_domain = re.match(domain_regex, body, re.DOTALL)
+ domain = match_domain.groupdict()["domain"]
+ except AttributeError:
+ domain = ""
+
+ if domain and version:
+ vtp_parsed["domain"] = domain
+ vtp_parsed["version"] = version
+ vtp_parsed["vtp_password"] = get_vtp_password(module)
+
+ return vtp_parsed
+
+
+def get_vtp_password(module):
+ command = "show vtp password"
+ output = "json"
+ cap = get_capabilities(module)["device_info"]["network_os_model"]
+ if re.search(r"Nexus 6", cap):
+ output = "text"
+
+ body = execute_show_command(command, module, output)[0]
+
+ if output == "json":
+ password = body.get("passwd", "")
+ else:
+ password = ""
+ rp = r"VTP Password: (\S+)"
+ mo = re.search(rp, body)
+ if mo:
+ password = mo.group(1)
+
+ return str(password)
+
+
+def main():
+ argument_spec = dict(
+ version=dict(type="str", choices=["1", "2"], required=True)
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+
+ version = module.params["version"]
+
+ existing = get_vtp_config(module)
+ end_state = existing
+
+ args = dict(version=version)
+
+ changed = False
+ proposed = dict((k, v) for k, v in args.items() if v is not None)
+ delta = dict(set(proposed.items()).difference(existing.items()))
+
+ commands = []
+ if delta:
+ commands.append(["vtp version {0}".format(version)])
+
+ cmds = flatten_list(commands)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(changed=True, commands=cmds)
+ else:
+ changed = True
+ load_config(module, cmds)
+ end_state = get_vtp_config(module)
+ if "configure" in cmds:
+ cmds.pop(0)
+
+ results = {}
+ results["proposed"] = proposed
+ results["existing"] = existing
+ results["end_state"] = end_state
+ results["updates"] = cmds
+ results["changed"] = changed
+ results["warnings"] = warnings
+
+ module.exit_json(**results)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep.py
new file mode 100644
index 00000000..86f0d3e3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep.py
@@ -0,0 +1,488 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vxlan_vtep
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Manages VXLAN Network Virtualization Endpoint (NVE).
+description:
+- Manages VXLAN Network Virtualization Endpoint (NVE) overlay interface that terminates
+ VXLAN tunnels.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- The module is used to manage NVE properties, not to create NVE interfaces. Use M(cisco.nxos.nxos_interface)
+ if you wish to do so.
+- C(state=absent) removes the interface.
+- Default, where supported, restores params default value.
+options:
+ interface:
+ description:
+ - Interface name for the VXLAN Network Virtualization Endpoint.
+ required: true
+ type: str
+ description:
+ description:
+ - Description of the NVE interface.
+ type: str
+ host_reachability:
+ description:
+ - Specify mechanism for host reachability advertisement. A Boolean value of 'true'
+ indicates that BGP will be used for host reachability advertisement. A Boolean
+ value of 'false' indicates that no protocol is used for host reachability advertisement.
+ Other host reachability advertisement protocols (e.g. OpenFlow, controller, etc.) are not
+ supported.
+ type: bool
+ shutdown:
+ description:
+ - Administratively shutdown the NVE interface.
+ type: bool
+ source_interface:
+ description:
+ - Specify the loopback interface whose IP address should be used for the NVE interface.
+ type: str
+ source_interface_hold_down_time:
+ description:
+ - Suppresses advertisement of the NVE loopback address until the overlay has converged.
+ type: str
+ global_mcast_group_L3:
+ description:
+ - Global multicast IP prefix for L3 VNIs or the keyword 'default'. This is available on
+ Nexus 9000 series switches running NX-OS software release 9.2(x) or higher.
+ type: str
+ global_mcast_group_L2:
+ description:
+ - Global multicast IP prefix for L2 VNIs or the keyword 'default'. This is available on
+ Nexus 9000 series switches running NX-OS software release 9.2(x) or higher.
+ type: str
+ global_suppress_arp:
+ description:
+ - Enables ARP suppression for all VNIs. This is available on NX-OS 9K series running
+ 9.2.x or higher.
+ type: bool
+ global_ingress_replication_bgp:
+ description:
+ - Configures ingress replication protocol as bgp for all VNIs. This is available on Nexus
+ 9000 series switches running NX-OS software release 9.2(x) or higher.
+ type: bool
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ multisite_border_gateway_interface:
+ description:
+ - Specify the loopback interface whose IP address should be used for the NVE
+ Multisite Border-gateway Interface. This is available on specific Nexus 9000
+ series switches running NX-OS 7.0(3)I7(x) or higher. Specify "default" to remove
+ an existing gateway config.
+ type: str
+ version_added: 1.1.0
+"""
+EXAMPLES = """
+- cisco.nxos.nxos_vxlan_vtep:
+ interface: nve1
+ description: default
+ host_reachability: true
+ source_interface: Loopback0
+ source_interface_hold_down_time: 30
+ shutdown: default
+ multisite_border_gateway_interface: Loopback0
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface nve1", "source-interface loopback0",
+ "source-interface hold-down-time 30", "description simple description",
+ "shutdown", "host-reachability protocol bgp",
+ "multisite border-gateway interface loopback0"]
+"""
+
+import re
+
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ run_commands,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+BOOL_PARAMS = [
+ "shutdown",
+ "host_reachability",
+ "global_ingress_replication_bgp",
+ "global_suppress_arp",
+]
+PARAM_TO_COMMAND_KEYMAP = {
+ "description": "description",
+ "global_suppress_arp": "global suppress-arp",
+ "global_ingress_replication_bgp": "global ingress-replication protocol bgp",
+ "global_mcast_group_L3": "global mcast-group L3",
+ "global_mcast_group_L2": "global mcast-group L2",
+ "host_reachability": "host-reachability protocol bgp",
+ "interface": "interface",
+ "shutdown": "shutdown",
+ "source_interface": "source-interface",
+ "source_interface_hold_down_time": "source-interface hold-down-time",
+ "multisite_border_gateway_interface": "multisite border-gateway interface",
+}
+PARAM_TO_DEFAULT_KEYMAP = {
+ "description": False,
+ "shutdown": True,
+ "source_interface_hold_down_time": "180",
+}
+
+
+def get_value(arg, config, module):
+ if arg in BOOL_PARAMS:
+ REGEX = re.compile(
+ r"\s+{0}\s*$".format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M
+ )
+ NO_SHUT_REGEX = re.compile(r"\s+no shutdown\s*$", re.M)
+ value = False
+ if arg == "shutdown":
+ try:
+ if NO_SHUT_REGEX.search(config):
+ value = False
+ elif REGEX.search(config):
+ value = True
+ except TypeError:
+ value = False
+ else:
+ try:
+ if REGEX.search(config):
+ value = True
+ except TypeError:
+ value = False
+ else:
+ REGEX = re.compile(
+ r"(?:{0}\s)(?P<value>.*)$".format(PARAM_TO_COMMAND_KEYMAP[arg]),
+ re.M,
+ )
+ NO_DESC_REGEX = re.compile(
+ r"\s+{0}\s*$".format("no description"), re.M
+ )
+ SOURCE_INTF_REGEX = re.compile(
+ r"(?:{0}\s)(?P<value>\S+)$".format(PARAM_TO_COMMAND_KEYMAP[arg]),
+ re.M,
+ )
+ value = ""
+ if arg == "description":
+ if NO_DESC_REGEX.search(config):
+ value = False
+ elif PARAM_TO_COMMAND_KEYMAP[arg] in config:
+ value = REGEX.search(config).group("value").strip()
+ elif arg == "source_interface":
+ for line in config.splitlines():
+ try:
+ if PARAM_TO_COMMAND_KEYMAP[arg] in config:
+ value = (
+ SOURCE_INTF_REGEX.search(config)
+ .group("value")
+ .strip()
+ )
+ break
+ except AttributeError:
+ value = ""
+ elif arg == "global_mcast_group_L2":
+ for line in config.splitlines():
+ try:
+ if "global mcast-group" in line and "L2" in line:
+ value = line.split()[2].strip()
+ break
+ except AttributeError:
+ value = ""
+ elif arg == "global_mcast_group_L3":
+ for line in config.splitlines():
+ try:
+ if "global mcast-group" in line and "L3" in line:
+ value = line.split()[2].strip()
+ break
+ except AttributeError:
+ value = ""
+ elif arg == "multisite_border_gateway_interface":
+ for line in config.splitlines():
+ try:
+ if PARAM_TO_COMMAND_KEYMAP[arg] in config:
+ value = (
+ SOURCE_INTF_REGEX.search(config)
+ .group("value")
+ .strip()
+ )
+ break
+ except AttributeError:
+ value = ""
+ else:
+ if PARAM_TO_COMMAND_KEYMAP[arg] in config:
+ value = REGEX.search(config).group("value").strip()
+ return value
+
+
+def get_existing(module, args):
+ existing = {}
+ netcfg = CustomNetworkConfig(
+ indent=2, contents=get_config(module, flags=["all"])
+ )
+
+ interface_string = "interface {0}".format(
+ module.params["interface"].lower()
+ )
+ parents = [interface_string]
+ config = netcfg.get_section(parents)
+
+ if config:
+ for arg in args:
+ existing[arg] = get_value(arg, config, module)
+
+ existing["interface"] = module.params["interface"].lower()
+ else:
+ if interface_string in str(netcfg):
+ existing["interface"] = module.params["interface"].lower()
+ for arg in args:
+ existing[arg] = ""
+ return existing
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ value = table.get(key)
+ if value:
+ new_dict[new_key] = value
+ else:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def fix_commands(commands, module):
+ source_interface_command = ""
+ no_source_interface_command = ""
+ no_host_reachability_command = ""
+ host_reachability_command = ""
+
+ for command in commands:
+ if "no source-interface hold-down-time" in command:
+ pass
+ elif "source-interface hold-down-time" in command:
+ pass
+ elif "no source-interface" in command:
+ no_source_interface_command = command
+ elif "source-interface" in command:
+ source_interface_command = command
+ elif "no host-reachability" in command:
+ no_host_reachability_command = command
+ elif "host-reachability" in command:
+ host_reachability_command = command
+
+ if host_reachability_command:
+ commands.pop(commands.index(host_reachability_command))
+ commands.insert(0, host_reachability_command)
+
+ if source_interface_command:
+ commands.pop(commands.index(source_interface_command))
+ commands.insert(0, source_interface_command)
+
+ if no_host_reachability_command:
+ commands.pop(commands.index(no_host_reachability_command))
+ commands.append(no_host_reachability_command)
+
+ if no_source_interface_command:
+ commands.pop(commands.index(no_source_interface_command))
+ commands.append(no_source_interface_command)
+
+ commands.insert(0, "terminal dont-ask")
+ return commands
+
+
+def gsa_tcam_check(module):
+ """
+ global_suppress_arp is an N9k-only command that requires TCAM resources.
+ This method checks the current TCAM allocation.
+ Note that changing tcam_size requires a switch reboot to take effect.
+ """
+ cmds = [
+ {"command": "show hardware access-list tcam region", "output": "json"}
+ ]
+ body = run_commands(module, cmds)
+ if body:
+ tcam_region = body[0]["TCAM_Region"]["TABLE_Sizes"]["ROW_Sizes"]
+ if bool(
+ [
+ i
+ for i in tcam_region
+ if i["type"].startswith("Ingress ARP-Ether ACL")
+ and i["tcam_size"] == "0"
+ ]
+ ):
+ msg = (
+ "'show hardware access-list tcam region' indicates 'ARP-Ether' tcam size is 0 (no allocated resources). "
+ + "'global_suppress_arp' will be rejected by device."
+ )
+ module.fail_json(msg=msg)
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+ for key, value in proposed_commands.items():
+ if value is True:
+ commands.append(key)
+
+ elif value is False:
+ commands.append("no {0}".format(key))
+
+ elif value == "default":
+ if existing_commands.get(key):
+ existing_value = existing_commands.get(key)
+ if "global mcast-group" in key:
+ commands.append("no {0}".format(key))
+ else:
+ commands.append("no {0} {1}".format(key, existing_value))
+ else:
+ if key.replace(" ", "_").replace("-", "_") in BOOL_PARAMS:
+ commands.append("no {0}".format(key.lower()))
+ module.exit_json(commands=commands)
+ else:
+ if "L2" in key:
+ commands.append("global mcast-group " + value + " L2")
+ elif "L3" in key:
+ commands.append("global mcast-group " + value + " L3")
+ else:
+ command = "{0} {1}".format(key, value.lower())
+ commands.append(command)
+
+ if commands:
+ commands = fix_commands(commands, module)
+ parents = ["interface {0}".format(module.params["interface"].lower())]
+ candidate.add(commands, parents=parents)
+ else:
+ if not existing and module.params["interface"]:
+ commands = [
+ "interface {0}".format(module.params["interface"].lower())
+ ]
+ candidate.add(commands, parents=[])
+
+
+def state_absent(module, existing, proposed, candidate):
+ commands = ["no interface {0}".format(module.params["interface"].lower())]
+ candidate.add(commands, parents=[])
+
+
+def main():
+ argument_spec = dict(
+ interface=dict(required=True, type="str"),
+ description=dict(required=False, type="str"),
+ host_reachability=dict(required=False, type="bool"),
+ global_ingress_replication_bgp=dict(required=False, type="bool"),
+ global_suppress_arp=dict(required=False, type="bool"),
+ global_mcast_group_L2=dict(required=False, type="str"),
+ global_mcast_group_L3=dict(required=False, type="str"),
+ shutdown=dict(required=False, type="bool"),
+ source_interface=dict(required=False, type="str"),
+ source_interface_hold_down_time=dict(required=False, type="str"),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ multisite_border_gateway_interface=dict(required=False, type="str"),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ mutually_exclusive = [
+ ("global_ingress_replication_bgp", "global_mcast_group_L2")
+ ]
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False, "commands": [], "warnings": warnings}
+
+ state = module.params["state"]
+
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+
+ existing = get_existing(module, args)
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key != "interface":
+ if str(value).lower() == "default":
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key)
+ if value is None:
+ if key in BOOL_PARAMS:
+ value = False
+ else:
+ value = "default"
+ if str(existing.get(key)).lower() != str(value).lower():
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+
+ if proposed.get("global_suppress_arp"):
+ gsa_tcam_check(module)
+ if state == "present":
+ if not existing:
+ warnings.append(
+ "The proposed NVE interface did not exist. "
+ "It's recommended to use nxos_interface to create "
+ "all logical interfaces."
+ )
+ state_present(module, existing, proposed, candidate)
+ elif state == "absent" and existing:
+ state_absent(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ result["commands"] = candidate
+ result["changed"] = True
+ load_config(module, candidate)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep_vni.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep_vni.py
new file mode 100644
index 00000000..96cabb71
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_vxlan_vtep_vni.py
@@ -0,0 +1,482 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+module: nxos_vxlan_vtep_vni
+extends_documentation_fragment:
+- cisco.nxos.nxos
+short_description: Creates a Virtual Network Identifier member (VNI)
+description:
+- Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface.
+version_added: 1.0.0
+author: Gabriele Gerbino (@GGabriele)
+notes:
+- Tested against NXOSv 7.3.(0)D1(1) on VIRL
+- default, where supported, restores params default value.
+options:
+ interface:
+ description:
+ - Interface name for the VXLAN Network Virtualization Endpoint.
+ required: true
+ type: str
+ vni:
+ description:
+ - ID of the Virtual Network Identifier.
+ required: true
+ type: str
+ assoc_vrf:
+ description:
+ - This attribute is used to identify and separate processing VNIs that are associated
+ with a VRF and used for routing. The VRF and VNI specified with this command
+ must match the configuration of the VNI under the VRF.
+ type: bool
+ ingress_replication:
+ description:
+ - Specifies mechanism for host reachability advertisement.
+ choices:
+ - bgp
+ - static
+ - default
+ type: str
+ multicast_group:
+ description:
+ - The multicast group (range) of the VNI. Valid values are string and keyword
+ 'default'.
+ type: str
+ peer_list:
+ description:
+ - Set the ingress-replication static peer list. Valid values are an array, a space-separated
+ string of ip addresses, or the keyword 'default'.
+ type: list
+ elements: str
+ suppress_arp:
+ description:
+ - Suppress arp under layer 2 VNI.
+ type: bool
+ suppress_arp_disable:
+ description:
+ - Overrides the global ARP suppression config. This is available on NX-OS 9K series
+ running 9.2.x or higher.
+ type: bool
+ state:
+ description:
+ - Determines whether the config should be present or not on the device.
+ default: present
+ choices:
+ - present
+ - absent
+ type: str
+ multisite_ingress_replication:
+ description:
+ - Enables multisite ingress replication.
+ choices:
+ - disable
+ - enable
+ - optimized
+ type: str
+ version_added: 1.1.0
+"""
+EXAMPLES = """
+- cisco.nxos.nxos_vxlan_vtep_vni:
+ interface: nve1
+ vni: 6000
+ ingress_replication: default
+ multisite_ingress_replication: enable
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample: ["interface nve1", "member vni 6000", "multisite ingress-replication"]
+"""
+
+import re
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ get_config,
+ load_config,
+)
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ nxos_argument_spec,
+)
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
+ CustomNetworkConfig,
+)
+
+BOOL_PARAMS = ["assoc_vrf", "suppress_arp", "suppress_arp_disable"]
+PARAM_TO_DEFAULT_KEYMAP = {
+ "multicast_group": "",
+ "peer_list": [],
+ "ingress_replication": "",
+}
+PARAM_TO_COMMAND_KEYMAP = {
+ "assoc_vrf": "associate-vrf",
+ "interface": "interface",
+ "vni": "member vni",
+ "ingress_replication": "ingress-replication protocol",
+ "multicast_group": "mcast-group",
+ "peer_list": "peer-ip",
+ "suppress_arp": "suppress-arp",
+ "suppress_arp_disable": "suppress-arp disable",
+ "multisite_ingress_replication": "multisite ingress-replication",
+}
+
+
+def get_value(arg, config, module):
+ command = PARAM_TO_COMMAND_KEYMAP[arg]
+ command_val_re = re.compile(
+ r"(?:{0}\s)(?P<value>.*)$".format(command), re.M
+ )
+
+ if arg in BOOL_PARAMS:
+ command_re = re.compile(r"\s+{0}\s*$".format(command), re.M)
+ value = False
+ if command_re.search(config):
+ value = True
+ elif arg == "peer_list":
+ has_command_val = command_val_re.findall(config, re.M)
+ value = []
+ if has_command_val:
+ value = has_command_val
+ elif arg == "multisite_ingress_replication":
+ has_command = re.search(r"^\s+{0}$".format(command), config, re.M)
+ has_command_val = command_val_re.search(config, re.M)
+ value = "disable"
+ if has_command:
+ value = "enable"
+ elif has_command_val:
+ value = "optimized"
+ else:
+ value = ""
+ has_command_val = command_val_re.search(config, re.M)
+ if has_command_val:
+ value = has_command_val.group("value")
+ return value
+
+
+def check_interface(module, netcfg):
+ config = str(netcfg)
+
+ has_interface = re.search(r"(?:interface nve)(?P<value>.*)$", config, re.M)
+ value = ""
+ if has_interface:
+ value = "nve{0}".format(has_interface.group("value"))
+
+ return value
+
+
+def get_existing(module, args):
+ existing = {}
+ netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
+
+ interface_exist = check_interface(module, netcfg)
+ if interface_exist:
+ parents = ["interface {0}".format(interface_exist)]
+ temp_config = netcfg.get_section(parents)
+
+ if (
+ "member vni {0} associate-vrf".format(module.params["vni"])
+ in temp_config
+ ):
+ parents.append(
+ "member vni {0} associate-vrf".format(module.params["vni"])
+ )
+ config = netcfg.get_section(parents)
+ elif "member vni {0}".format(module.params["vni"]) in temp_config:
+ parents.append("member vni {0}".format(module.params["vni"]))
+ config = netcfg.get_section(parents)
+ else:
+ config = {}
+
+ if config:
+ for arg in args:
+ if arg not in ["interface", "vni"]:
+ existing[arg] = get_value(arg, config, module)
+ existing["interface"] = interface_exist
+ existing["vni"] = module.params["vni"]
+
+ return existing, interface_exist
+
+
+def apply_key_map(key_map, table):
+ new_dict = {}
+ for key, value in table.items():
+ new_key = key_map.get(key)
+ if new_key:
+ new_dict[new_key] = value
+ return new_dict
+
+
+def state_present(module, existing, proposed, candidate):
+ commands = list()
+ proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
+ existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
+
+ for key, value in proposed_commands.items():
+ if key == "associate-vrf":
+ command = "member vni {0} {1}".format(module.params["vni"], key)
+ if not value:
+ command = "no {0}".format(command)
+ commands.append(command)
+
+ elif key == "peer-ip" and value != []:
+ for peer in value:
+ commands.append("{0} {1}".format(key, peer))
+
+ elif key == "mcast-group" and value != existing_commands.get(key):
+ commands.append("no {0}".format(key))
+ vni_command = "member vni {0}".format(module.params["vni"])
+ if vni_command not in commands:
+ commands.append("member vni {0}".format(module.params["vni"]))
+ if value != PARAM_TO_DEFAULT_KEYMAP.get(
+ "multicast_group", "default"
+ ):
+ commands.append("{0} {1}".format(key, value))
+
+ elif (
+ key == "ingress-replication protocol"
+ and value != existing_commands.get(key)
+ ):
+ evalue = existing_commands.get(key)
+ dvalue = PARAM_TO_DEFAULT_KEYMAP.get(
+ "ingress_replication", "default"
+ )
+ if value != dvalue:
+ if evalue and evalue != dvalue:
+ commands.append("no {0} {1}".format(key, evalue))
+ commands.append("{0} {1}".format(key, value))
+ else:
+ if evalue:
+ commands.append("no {0} {1}".format(key, evalue))
+
+ elif value is True:
+ commands.append(key)
+ elif value is False:
+ commands.append("no {0}".format(key))
+ elif value == "default" or value == []:
+ if existing_commands.get(key):
+ existing_value = existing_commands.get(key)
+ if key == "peer-ip":
+ for peer in existing_value:
+ commands.append("no {0} {1}".format(key, peer))
+ else:
+ commands.append("no {0} {1}".format(key, existing_value))
+ else:
+ if key.replace(" ", "_").replace("-", "_") in BOOL_PARAMS:
+ commands.append("no {0}".format(key.lower()))
+ elif (
+ key == "multisite ingress-replication"
+ and value != existing_commands.get(key)
+ ):
+ vni_command = "member vni {0}".format(module.params["vni"])
+ if vni_command not in commands:
+ commands.append("member vni {0}".format(module.params["vni"]))
+ if value == "disable":
+ command = "no {0}".format(key)
+ commands.append(command)
+ elif value == "enable":
+ command = "{0}".format(key)
+ commands.append(command)
+ elif value == "optimized":
+ command = "{0} {1}".format(key, value)
+ commands.append(command)
+ else:
+ command = "{0} {1}".format(key, value.lower())
+ commands.append(command)
+
+ if commands:
+ vni_command = "member vni {0}".format(module.params["vni"])
+ ingress_replications_command = "ingress-replication protocol static"
+ ingress_replicationb_command = "ingress-replication protocol bgp"
+ ingress_replicationns_command = (
+ "no ingress-replication protocol static"
+ )
+ ingress_replicationnb_command = "no ingress-replication protocol bgp"
+ interface_command = "interface {0}".format(module.params["interface"])
+
+ if any(
+ c in commands
+ for c in (
+ ingress_replications_command,
+ ingress_replicationb_command,
+ ingress_replicationnb_command,
+ ingress_replicationns_command,
+ )
+ ):
+ static_level_cmds = [cmd for cmd in commands if "peer" in cmd]
+ parents = [interface_command, vni_command]
+ commands = [cmd for cmd in commands if "peer" not in cmd]
+ for cmd in commands:
+ parents.append(cmd)
+ candidate.add(static_level_cmds, parents=parents)
+
+ elif "peer-ip" in commands[0]:
+ static_level_cmds = [cmd for cmd in commands]
+ parents = [
+ interface_command,
+ vni_command,
+ ingress_replications_command,
+ ]
+ candidate.add(static_level_cmds, parents=parents)
+
+ if vni_command in commands:
+ parents = [interface_command]
+ commands.remove(vni_command)
+ if module.params["assoc_vrf"] is None:
+ parents.append(vni_command)
+ candidate.add(commands, parents=parents)
+
+
+def state_absent(module, existing, proposed, candidate):
+ if existing["assoc_vrf"]:
+ commands = [
+ "no member vni {0} associate-vrf".format(module.params["vni"])
+ ]
+ else:
+ commands = ["no member vni {0}".format(module.params["vni"])]
+ parents = ["interface {0}".format(module.params["interface"])]
+ candidate.add(commands, parents=parents)
+
+
+def main():
+ argument_spec = dict(
+ interface=dict(required=True, type="str"),
+ vni=dict(required=True, type="str"),
+ assoc_vrf=dict(required=False, type="bool"),
+ multicast_group=dict(required=False, type="str"),
+ peer_list=dict(required=False, type="list", elements="str"),
+ suppress_arp=dict(required=False, type="bool"),
+ suppress_arp_disable=dict(required=False, type="bool"),
+ ingress_replication=dict(
+ required=False, type="str", choices=["bgp", "static", "default"]
+ ),
+ state=dict(
+ choices=["present", "absent"], default="present", required=False
+ ),
+ multisite_ingress_replication=dict(
+ required=False,
+ type="str",
+ choices=["enable", "optimized", "disable"],
+ ),
+ )
+
+ argument_spec.update(nxos_argument_spec)
+
+ mutually_exclusive = [
+ ("suppress_arp", "suppress_arp_disable"),
+ ("assoc_vrf", "multicast_group"),
+ ("assoc_vrf", "suppress_arp"),
+ ("assoc_vrf", "suppress_arp_disable"),
+ ("assoc_vrf", "ingress_replication"),
+ ]
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ )
+
+ warnings = list()
+ result = {"changed": False, "commands": [], "warnings": warnings}
+
+ if module.params["peer_list"]:
+ if (
+ module.params["peer_list"][0] != "default"
+ and module.params["ingress_replication"] != "static"
+ ):
+ module.fail_json(
+ msg="ingress_replication=static is required "
+ "when using peer_list param"
+ )
+ else:
+ peer_list = module.params["peer_list"]
+ if peer_list[0] == "default":
+ module.params["peer_list"] = "default"
+ else:
+ stripped_peer_list = list(map(str.strip, peer_list))
+ module.params["peer_list"] = stripped_peer_list
+
+ if (
+ module.params["multisite_ingress_replication"] == "enable"
+ or module.params["multisite_ingress_replication"] == "optimized"
+ ):
+ if module.params["ingress_replication"] == "static":
+ module.fail_json(
+ msg="ingress_replication=static is not allowed "
+ "when using multisite_ingress_replication"
+ )
+
+ state = module.params["state"]
+ args = PARAM_TO_COMMAND_KEYMAP.keys()
+ existing, interface_exist = get_existing(module, args)
+
+ if state == "present":
+ if not interface_exist:
+ module.fail_json(
+ msg="The proposed NVE interface does not exist. Use nxos_interface to create it first."
+ )
+ elif interface_exist != module.params["interface"]:
+ module.fail_json(
+ msg="Only 1 NVE interface is allowed on the switch."
+ )
+ elif state == "absent":
+ if interface_exist != module.params["interface"]:
+ module.exit_json(**result)
+ elif existing and existing["vni"] != module.params["vni"]:
+ module.fail_json(
+ msg="ERROR: VNI delete failed: Could not find vni node for {0}".format(
+ module.params["vni"]
+ ),
+ existing_vni=existing["vni"],
+ )
+
+ proposed_args = dict(
+ (k, v) for k, v in module.params.items() if v is not None and k in args
+ )
+
+ proposed = {}
+ for key, value in proposed_args.items():
+ if key in ["multicast_group", "peer_list", "ingress_replication"]:
+ if str(value).lower() == "default":
+ value = PARAM_TO_DEFAULT_KEYMAP.get(key, "default")
+ if key != "interface" and existing.get(key) != value:
+ proposed[key] = value
+
+ candidate = CustomNetworkConfig(indent=3)
+ if state == "present":
+ state_present(module, existing, proposed, candidate)
+ elif existing and state == "absent":
+ state_absent(module, existing, proposed, candidate)
+
+ if candidate:
+ candidate = candidate.items_text()
+ result["changed"] = True
+ result["commands"] = candidate
+ if not module.check_mode:
+ load_config(module, candidate)
+
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_zone_zoneset.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_zone_zoneset.py
new file mode 100644
index 00000000..299b5898
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/nxos_zone_zoneset.py
@@ -0,0 +1,990 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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
+
+
+DOCUMENTATION = """
+module: nxos_zone_zoneset
+short_description: Configuration of zone/zoneset for Cisco NXOS MDS Switches.
+description:
+- Configuration of zone/zoneset for Cisco MDS NXOS.
+version_added: 1.0.0
+author:
+- Suhas Bharadwaj (@srbharadwaj) (subharad@cisco.com)
+notes:
+- Tested against NX-OS 8.4(1)
+options:
+ zone_zoneset_details:
+ description:
+ - List of zone/zoneset details to be added or removed
+ type: list
+ elements: dict
+ suboptions:
+ vsan:
+ description:
+ - vsan id
+ required: true
+ type: int
+ mode:
+ description:
+ - mode of the zone for the vsan
+ choices:
+ - enhanced
+ - basic
+ type: str
+ default_zone:
+ description:
+ - default zone behaviour for the vsan
+ choices:
+ - permit
+ - deny
+ type: str
+ smart_zoning:
+ description:
+ - Removes the vsan if True
+ type: bool
+ zone:
+ description:
+ - List of zone options for that vsan
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - name of the zone
+ required: true
+ type: str
+ remove:
+ description:
+ - Deletes the zone if True
+ type: bool
+ default: false
+ members:
+ description:
+ - Members of the zone that needs to be removed or added
+ type: list
+ elements: dict
+ suboptions:
+ pwwn:
+ description:
+ - pwwn member of the zone, use alias 'device_alias' as option for
+ device_alias member
+ aliases:
+ - device_alias
+ required: true
+ type: str
+ remove:
+ description:
+ - Removes member from the zone if True
+ type: bool
+ default: false
+ devtype:
+ description:
+ - devtype of the zone member used along with Smart zoning config
+ choices:
+ - initiator
+ - target
+ - both
+ type: str
+ zoneset:
+ description:
+ - List of zoneset options for the vsan
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - name of the zoneset
+ required: true
+ type: str
+ remove:
+ description:
+ - Removes zoneset if True
+ type: bool
+ default: false
+ action:
+ description:
+ - activates/de-activates the zoneset
+ choices:
+ - activate
+ - deactivate
+ type: str
+ members:
+ description:
+ - Members of the zoneset that needs to be removed or added
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - name of the zone that needs to be added to the zoneset or removed
+ from the zoneset
+ required: true
+ type: str
+ remove:
+ description:
+ - Removes zone member from the zoneset
+ type: bool
+ default: false
+"""
+
+EXAMPLES = """
+- name: Test that zone/zoneset module works
+ cisco.nxos.nxos_zone_zoneset:
+ zone_zoneset_details:
+ - mode: enhanced
+ vsan: 22
+ zone:
+ - members:
+ - pwwn: 11:11:11:11:11:11:11:11
+ - device_alias: test123
+ - pwwn: 61:61:62:62:12:12:12:12
+ remove: true
+ name: zoneA
+ - members:
+ - pwwn: 10:11:11:11:11:11:11:11
+ - pwwn: 62:62:62:62:21:21:21:21
+ name: zoneB
+ - name: zoneC
+ remove: true
+ zoneset:
+ - action: activate
+ members:
+ - name: zoneA
+ - name: zoneB
+ - name: zoneC
+ remove: true
+ name: zsetname1
+ - action: deactivate
+ name: zsetTestExtra
+ remove: true
+ - mode: basic
+ smart_zoning: true
+ vsan: 21
+ zone:
+ - members:
+ - devtype: both
+ pwwn: 11:11:11:11:11:11:11:11
+ - pwwn: 62:62:62:62:12:12:12:12
+ - devtype: both
+ pwwn: 92:62:62:62:12:12:1a:1a
+ remove: true
+ name: zone21A
+ - members:
+ - pwwn: 10:11:11:11:11:11:11:11
+ - pwwn: 62:62:62:62:21:21:21:21
+ name: zone21B
+ zoneset:
+ - action: activate
+ members:
+ - name: zone21A
+ - name: zone21B
+ name: zsetname212
+
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - terminal dont-ask
+ - zone name zoneA vsan 923
+ - member pwwn 11:11:11:11:11:11:11:11
+ - no member device-alias test123
+ - zone commit vsan 923
+ - no terminal dont-ask
+messages:
+ description: debug messages
+ returned: always
+ type: list
+ sample:
+ - "zone mode is already enhanced ,no change in zone mode configuration for vsan 922"
+ - "zone member '11:11:11:11:11:11:11:11' is already present in zone 'zoneA' in vsan 922 hence nothing to add"
+ - "zone member 'test123' is already present in zone 'zoneA' in vsan 922 hence nothing to add"
+ - "zone member '61:61:62:62:12:12:12:12' is not present in zone 'zoneA' in vsan 922 hence nothing to remove"
+ - "zone member '10:11:11:11:11:11:11:11' is already present in zone 'zoneB' in vsan 922 hence nothing to add"
+ - "zone member '62:62:62:62:21:21:21:21' is already present in zone 'zoneB' in vsan 922 hence nothing to add"
+ - "zone 'zoneC' is not present in vsan 922 , so nothing to remove"
+"""
+
+
+import re
+import json
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+
+
+__metaclass__ = type
+
+
+class ShowZonesetActive(object):
+ """docstring for ShowZonesetActive"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.module = module
+ self.activeZSName = None
+ self.parseCmdOutput()
+
+ def execute_show_zoneset_active_cmd(self):
+ command = (
+ "show zoneset active vsan " + str(self.vsan) + " | grep zoneset"
+ )
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def parseCmdOutput(self):
+ patZoneset = r"zoneset name (\S+) vsan " + str(self.vsan)
+ output = self.execute_show_zoneset_active_cmd().split("\n")
+ if len(output) == 0:
+ return
+ else:
+ for line in output:
+ line = line.strip()
+ mzs = re.match(patZoneset, line.strip())
+ if mzs:
+ self.activeZSName = mzs.group(1).strip()
+ return
+
+ def isZonesetActive(self, zsname):
+ if zsname == self.activeZSName:
+ return True
+ return False
+
+
+class ShowZoneset(object):
+ """docstring for ShowZoneset"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.module = module
+ self.zsDetails = {}
+ self.parseCmdOutput()
+
+ def execute_show_zoneset_cmd(self):
+ command = "show zoneset vsan " + str(self.vsan)
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def parseCmdOutput(self):
+ patZoneset = r"zoneset name (\S+) vsan " + str(self.vsan)
+ patZone = r"zone name (\S+) vsan " + str(self.vsan)
+ output = self.execute_show_zoneset_cmd().split("\n")
+ for line in output:
+ line = line.strip()
+ mzs = re.match(patZoneset, line.strip())
+ mz = re.match(patZone, line.strip())
+ if mzs:
+ zonesetname = mzs.group(1).strip()
+ self.zsDetails[zonesetname] = []
+ continue
+ elif mz:
+ zonename = mz.group(1).strip()
+ v = self.zsDetails[zonesetname]
+ v.append(zonename)
+ self.zsDetails[zonesetname] = v
+
+ def isZonesetPresent(self, zsname):
+ return zsname in self.zsDetails.keys()
+
+ def isZonePresentInZoneset(self, zsname, zname):
+ if zsname in self.zsDetails.keys():
+ return zname in self.zsDetails[zsname]
+ return False
+
+
+class ShowZone(object):
+ """docstring for ShowZone"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.module = module
+ self.zDetails = {}
+ self.parseCmdOutput()
+
+ def execute_show_zone_vsan_cmd(self):
+ command = "show zone vsan " + str(self.vsan)
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def parseCmdOutput(self):
+ patZone = r"zone name (\S+) vsan " + str(self.vsan)
+ output = self.execute_show_zone_vsan_cmd().split("\n")
+ for line in output:
+ line = " ".join(line.strip().split())
+ if "init" in line:
+ line = line.replace("init", "initiator")
+ m = re.match(patZone, line)
+ if m:
+ zonename = m.group(1).strip()
+ self.zDetails[zonename] = []
+ continue
+ else:
+ # For now we support only pwwn and device-alias under zone
+ # Ideally should use 'supported_choices'....but maybe next
+ # time.
+ if "pwwn" in line or "device-alias" in line:
+ v = self.zDetails[zonename]
+ v.append(line)
+ self.zDetails[zonename] = v
+
+ def isZonePresent(self, zname):
+ return zname in self.zDetails.keys()
+
+ def isZoneMemberPresent(self, zname, cmd):
+ if zname in self.zDetails.keys():
+ zonememlist = self.zDetails[zname]
+ for eachline in zonememlist:
+ if cmd in eachline:
+ return True
+ return False
+
+ def get_zDetails(self):
+ return self.zDetails
+
+
+class ShowZoneStatus(object):
+ """docstring for ShowZoneStatus"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.vsanAbsent = False
+ self.module = module
+ self.default_zone = ""
+ self.mode = ""
+ self.session = ""
+ self.sz = ""
+ self.locked = False
+ self.update()
+
+ def execute_show_zone_status_cmd(self):
+ command = "show zone status vsan " + str(self.vsan)
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def update(self):
+
+ output = self.execute_show_zone_status_cmd().split("\n")
+
+ patfordefzone = "VSAN: " + str(self.vsan) + r" default-zone:\s+(\S+).*"
+ patformode = r".*mode:\s+(\S+).*"
+ patforsession = r"^session:\s+(\S+).*"
+ patforsz = r".*smart-zoning:\s+(\S+).*"
+ for line in output:
+ if "is not configured" in line:
+ self.vsanAbsent = True
+ break
+ mdefz = re.match(patfordefzone, line.strip())
+ mmode = re.match(patformode, line.strip())
+ msession = re.match(patforsession, line.strip())
+ msz = re.match(patforsz, line.strip())
+
+ if mdefz:
+ self.default_zone = mdefz.group(1)
+ if mmode:
+ self.mode = mmode.group(1)
+ if msession:
+ self.session = msession.group(1)
+ if self.session != "none":
+ self.locked = True
+ if msz:
+ self.sz = msz.group(1)
+
+ def isLocked(self):
+ return self.locked
+
+ def getDefaultZone(self):
+ return self.default_zone
+
+ def getMode(self):
+ return self.mode
+
+ def getSmartZoningStatus(self):
+ return self.sz
+
+ def isVsanAbsent(self):
+ return self.vsanAbsent
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ output = "text"
+ commands = [{"command": command, "output": output}]
+ return run_commands(module, commands)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def getMemType(supported_choices, allmemkeys, default="pwwn"):
+ for eachchoice in supported_choices:
+ if eachchoice in allmemkeys:
+ return eachchoice
+ return default
+
+
+def main():
+
+ supported_choices = ["device_alias"]
+ zone_member_spec = dict(
+ pwwn=dict(required=True, type="str", aliases=["device_alias"]),
+ devtype=dict(type="str", choices=["initiator", "target", "both"]),
+ remove=dict(type="bool", default=False),
+ )
+
+ zone_spec = dict(
+ name=dict(required=True, type="str"),
+ members=dict(type="list", elements="dict", options=zone_member_spec),
+ remove=dict(type="bool", default=False),
+ )
+
+ zoneset_member_spec = dict(
+ name=dict(required=True, type="str"),
+ remove=dict(type="bool", default=False),
+ )
+
+ zoneset_spec = dict(
+ name=dict(type="str", required=True),
+ members=dict(
+ type="list", elements="dict", options=zoneset_member_spec
+ ),
+ remove=dict(type="bool", default=False),
+ action=dict(type="str", choices=["activate", "deactivate"]),
+ )
+
+ zonedetails_spec = dict(
+ vsan=dict(required=True, type="int"),
+ mode=dict(type="str", choices=["enhanced", "basic"]),
+ default_zone=dict(type="str", choices=["permit", "deny"]),
+ smart_zoning=dict(type="bool"),
+ zone=dict(type="list", elements="dict", options=zone_spec),
+ zoneset=dict(type="list", elements="dict", options=zoneset_spec),
+ )
+
+ argument_spec = dict(
+ zone_zoneset_details=dict(
+ type="list", elements="dict", options=zonedetails_spec
+ )
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ messages = list()
+ commands = list()
+ result = {"changed": False}
+
+ commands_executed = []
+ listOfZoneDetails = module.params["zone_zoneset_details"]
+ for eachZoneZonesetDetail in listOfZoneDetails:
+ vsan = eachZoneZonesetDetail["vsan"]
+ op_mode = eachZoneZonesetDetail["mode"]
+ op_default_zone = eachZoneZonesetDetail["default_zone"]
+ op_smart_zoning = eachZoneZonesetDetail["smart_zoning"]
+ op_zone = eachZoneZonesetDetail["zone"]
+ op_zoneset = eachZoneZonesetDetail["zoneset"]
+
+ # Step1: execute show zone status and get
+ shZoneStatusObj = ShowZoneStatus(module, vsan)
+ sw_default_zone = shZoneStatusObj.getDefaultZone()
+ sw_mode = shZoneStatusObj.getMode()
+ sw_smart_zoning = shZoneStatusObj.getSmartZoningStatus()
+
+ if sw_smart_zoning.lower() == "Enabled".lower():
+ sw_smart_zoning_bool = True
+ else:
+ sw_smart_zoning_bool = False
+
+ if shZoneStatusObj.isVsanAbsent():
+ module.fail_json(
+ msg="Vsan "
+ + str(vsan)
+ + " is not present in the switch. Hence cannot procced."
+ )
+
+ if shZoneStatusObj.isLocked():
+ module.fail_json(
+ msg="zone has acquired lock on the switch for vsan "
+ + str(vsan)
+ + ". Hence cannot procced."
+ )
+
+ # Process zone default zone options
+ if op_default_zone is not None:
+ if op_default_zone != sw_default_zone:
+ if op_default_zone == "permit":
+ commands_executed.append(
+ "zone default-zone permit vsan " + str(vsan)
+ )
+ messages.append(
+ "default zone configuration changed from deny to permit for vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "no zone default-zone permit vsan " + str(vsan)
+ )
+ messages.append(
+ "default zone configuration changed from permit to deny for vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "default zone is already "
+ + op_default_zone
+ + " ,no change in default zone configuration for vsan "
+ + str(vsan)
+ )
+
+ # Process zone mode options
+ if op_mode is not None:
+ if op_mode != sw_mode:
+ if op_mode == "enhanced":
+ commands_executed.append(
+ "zone mode enhanced vsan " + str(vsan)
+ )
+ messages.append(
+ "zone mode configuration changed from basic to enhanced for vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "no zone mode enhanced vsan " + str(vsan)
+ )
+ messages.append(
+ "zone mode configuration changed from enhanced to basic for vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zone mode is already "
+ + op_mode
+ + " ,no change in zone mode configuration for vsan "
+ + str(vsan)
+ )
+
+ # Process zone smart-zone options
+ if op_smart_zoning is not None:
+ if op_smart_zoning != sw_smart_zoning_bool:
+ if op_smart_zoning:
+ commands_executed.append(
+ "zone smart-zoning enable vsan " + str(vsan)
+ )
+ messages.append(
+ "smart-zoning enabled for vsan " + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "no zone smart-zoning enable vsan " + str(vsan)
+ )
+ messages.append(
+ "smart-zoning disabled for vsan " + str(vsan)
+ )
+ else:
+ messages.append(
+ "smart-zoning is already set to "
+ + sw_smart_zoning
+ + " , no change in smart-zoning configuration for vsan "
+ + str(vsan)
+ )
+
+ # Process zone member options
+ # TODO: Obviously this needs to be cleaned up properly, as there are a lot of ifelse statements which is bad
+ # Will take it up later becoz of time constraints
+ if op_zone is not None:
+ shZoneObj = ShowZone(module, vsan)
+ for eachzone in op_zone:
+ zname = eachzone["name"]
+ zmembers = eachzone["members"]
+ removeflag = eachzone["remove"]
+ if removeflag:
+ if shZoneObj.isZonePresent(zname):
+ messages.append(
+ "zone '"
+ + zname
+ + "' is removed from vsan "
+ + str(vsan)
+ )
+ commands_executed.append(
+ "no zone name " + zname + " vsan " + str(vsan)
+ )
+ else:
+ messages.append(
+ "zone '"
+ + zname
+ + "' is not present in vsan "
+ + str(vsan)
+ + " , so nothing to remove"
+ )
+ else:
+ if zmembers is None:
+ if shZoneObj.isZonePresent(zname):
+ messages.append(
+ "zone '"
+ + zname
+ + "' is already present in vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "zone name " + zname + " vsan " + str(vsan)
+ )
+ messages.append(
+ "zone '"
+ + zname
+ + "' is created in vsan "
+ + str(vsan)
+ )
+ else:
+ cmdmemlist = []
+ for eachmem in zmembers:
+ memtype = getMemType(
+ supported_choices, eachmem.keys()
+ )
+ cmd = (
+ memtype.replace("_", "-")
+ + " "
+ + eachmem[memtype]
+ )
+ if op_smart_zoning or sw_smart_zoning_bool:
+ if eachmem["devtype"] is not None:
+ cmd = cmd + " " + eachmem["devtype"]
+ if eachmem["remove"]:
+ if shZoneObj.isZonePresent(zname):
+ if shZoneObj.isZoneMemberPresent(
+ zname, cmd
+ ):
+ cmd = "no member " + cmd
+ cmdmemlist.append(cmd)
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "removing zone member '"
+ + eachmem[memtype]
+ + " of device type '"
+ + eachmem["devtype"]
+ + "' from zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "removing zone member '"
+ + eachmem[memtype]
+ + "' from zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' of device type '"
+ + eachmem["devtype"]
+ + "' is not present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to remove"
+ )
+ else:
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' is not present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to remove"
+ )
+ else:
+ messages.append(
+ "zone '"
+ + zname
+ + "' is not present in vsan "
+ + str(vsan)
+ + " , hence cannot remove the members"
+ )
+
+ else:
+ if shZoneObj.isZoneMemberPresent(zname, cmd):
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' of device type '"
+ + eachmem["devtype"]
+ + "' is already present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to add"
+ )
+ else:
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' is already present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to add"
+ )
+ else:
+ cmd = "member " + cmd
+ cmdmemlist.append(cmd)
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "adding zone member '"
+ + eachmem[memtype]
+ + "' of device type '"
+ + eachmem["devtype"]
+ + "' to zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "adding zone member '"
+ + eachmem[memtype]
+ + "' to zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ if len(cmdmemlist) != 0:
+ commands_executed.append(
+ "zone name " + zname + " vsan " + str(vsan)
+ )
+ commands_executed = commands_executed + cmdmemlist
+
+ # Process zoneset member options
+ if op_zoneset is not None:
+ dactcmd = []
+ actcmd = []
+ shZonesetObj = ShowZoneset(module, vsan)
+ shZonesetActiveObj = ShowZonesetActive(module, vsan)
+ for eachzoneset in op_zoneset:
+ zsetname = eachzoneset["name"]
+ zsetmembers = eachzoneset["members"]
+ removeflag = eachzoneset["remove"]
+ actionflag = eachzoneset["action"]
+ if removeflag:
+ if shZonesetObj.isZonesetPresent(zsetname):
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is removed from vsan "
+ + str(vsan)
+ )
+ commands_executed.append(
+ "no zoneset name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is not present in vsan "
+ + str(vsan)
+ + " ,hence there is nothing to remove"
+ )
+ else:
+ if zsetmembers is not None:
+ cmdmemlist = []
+ for eachzsmem in zsetmembers:
+ zsetmem_name = eachzsmem["name"]
+ zsetmem_removeflag = eachzsmem["remove"]
+ if zsetmem_removeflag:
+ if shZonesetObj.isZonePresentInZoneset(
+ zsetname, zsetmem_name
+ ):
+ cmd = "no member " + zsetmem_name
+ cmdmemlist.append(cmd)
+ messages.append(
+ "removing zoneset member '"
+ + zsetmem_name
+ + "' from zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zoneset member '"
+ + zsetmem_name
+ + "' is not present in zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " ,hence there is nothing to remove"
+ )
+ else:
+ if shZonesetObj.isZonePresentInZoneset(
+ zsetname, zsetmem_name
+ ):
+ messages.append(
+ "zoneset member '"
+ + zsetmem_name
+ + "' is already present in zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " ,hence there is nothing to add"
+ )
+ else:
+ cmd = "member " + zsetmem_name
+ cmdmemlist.append(cmd)
+ messages.append(
+ "adding zoneset member '"
+ + zsetmem_name
+ + "' to zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ if len(cmdmemlist) != 0:
+ commands_executed.append(
+ "zoneset name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ commands_executed = commands_executed + cmdmemlist
+ else:
+ if shZonesetObj.isZonesetPresent(zsetname):
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is already present in vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "zoneset name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is created in vsan "
+ + str(vsan)
+ )
+
+ # Process zoneset activate options
+ if actionflag == "deactivate":
+ if shZonesetActiveObj.isZonesetActive(zsetname):
+ messages.append(
+ "deactivating zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ dactcmd.append(
+ "no zoneset activate name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " is not activated, hence cannot deactivate"
+ )
+ elif actionflag == "activate":
+ if commands_executed:
+ messages.append(
+ "activating zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ actcmd.append(
+ "zoneset activate name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "no changes to existing zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " hence activate action is ignored"
+ )
+ commands_executed = commands_executed + dactcmd + actcmd
+
+ if commands_executed:
+ if op_mode == "enhanced":
+ commands_executed.append("zone commit vsan " + str(vsan))
+ elif op_mode is None:
+ if sw_mode == "enhanced":
+ commands_executed.append("zone commit vsan " + str(vsan))
+
+ if commands_executed:
+ commands_executed = (
+ ["terminal dont-ask"]
+ + commands_executed
+ + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands_executed)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ commands=cmds,
+ msg="Check Mode: No cmds issued to the hosts",
+ )
+ else:
+ result["changed"] = True
+ commands = commands + cmds
+ load_config(module, cmds)
+
+ result["messages"] = messages
+ result["commands"] = commands_executed
+ result["warnings"] = warnings
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_devicealias.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_devicealias.py
new file mode 100644
index 00000000..30edfc75
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_devicealias.py
@@ -0,0 +1,593 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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
+
+
+DOCUMENTATION = """
+module: nxos_devicealias
+short_description: Configuration of device alias for Cisco NXOS MDS Switches.
+description:
+- Configuration of device alias for Cisco MDS NXOS.
+version_added: 1.0.0
+author:
+- Suhas Bharadwaj (@srbharadwaj) (subharad@cisco.com)
+notes:
+- Tested against NX-OS 8.4(1)
+options:
+ distribute:
+ description:
+ - Enable/Disable device-alias distribution
+ type: bool
+ mode:
+ description:
+ - Mode of devices-alias, basic or enhanced
+ choices:
+ - basic
+ - enhanced
+ type: str
+ da:
+ description:
+ - List of device-alias to be added or removed
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of the device-alias to be added or removed
+ required: true
+ type: str
+ pwwn:
+ description:
+ - pwwn to which the name needs to be associated with
+ type: str
+ remove:
+ description:
+ - Removes the device-alias if set to True
+ type: bool
+ default: false
+ rename:
+ description:
+ - List of device-alias to be renamed
+ type: list
+ elements: dict
+ suboptions:
+ old_name:
+ description:
+ - Old name of the device-alias that needs to be renamed
+ required: true
+ type: str
+ new_name:
+ description:
+ - New name of the device-alias
+ required: true
+ type: str
+"""
+
+EXAMPLES = """
+- name: Test that device alias module works
+ cisco.nxos.nxos_devicealias:
+ da:
+ - name: test1_add
+ pwwn: 56:2:22:11:22:88:11:67
+ - name: test2_add
+ pwwn: 65:22:22:11:22:22:11:d
+ - name: dev1
+ remove: true
+ - name: dev2
+ remove: true
+ distribute: true
+ mode: enhanced
+ rename:
+ - new_name: bcd
+ old_name: abc
+ - new_name: bcd1
+ old_name: abc1
+
+
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - terminal dont-ask
+ - device-alias database
+ - device-alias name somename pwwn 10:00:00:00:89:a1:01:03
+ - device-alias name somename1 pwwn 10:00:00:00:89:a1:02:03
+ - device-alias commit
+ - no terminal dont-ask
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+import string
+
+__metaclass__ = type
+
+VALID_DA_CHARS = ("-", "_", "$", "^")
+
+
+class showDeviceAliasStatus(object):
+ """docstring for showDeviceAliasStatus"""
+
+ def __init__(self, module):
+ self.module = module
+ self.distribute = ""
+ self.mode = ""
+ self.locked = False
+ self.update()
+
+ def execute_show_cmd(self, cmd):
+ output = execute_show_command(cmd, self.module)[0]
+ return output
+
+ def update(self):
+ command = "show device-alias status"
+ output = self.execute_show_cmd(command).split("\n")
+ for o in output:
+ if "Fabric Distribution" in o:
+ self.distribute = o.split(":")[1].strip().lower()
+ if "Mode" in o:
+ self.mode = o.split("Mode:")[1].strip().lower()
+ if "Locked" in o:
+ self.locked = True
+
+ def isLocked(self):
+ return self.locked
+
+ def getDistribute(self):
+ return self.distribute
+
+ def getMode(self):
+ return self.mode
+
+
+class showDeviceAliasDatabase(object):
+ """docstring for showDeviceAliasDatabase"""
+
+ def __init__(self, module):
+ self.module = module
+ self.da_dict = {}
+ self.update()
+
+ def execute_show_cmd(self, cmd):
+ output = execute_show_command(cmd, self.module)[0]
+ return output
+
+ def update(self):
+ command = "show device-alias database"
+ # output = execute_show_command(command, self.module)[0].split("\n")
+ output = self.execute_show_cmd(command)
+ self.da_list = output.split("\n")
+ for eachline in self.da_list:
+ if "device-alias" in eachline:
+ sv = eachline.strip().split()
+ self.da_dict[sv[2]] = sv[4]
+
+ def isNameInDaDatabase(self, name):
+ return name in self.da_dict.keys()
+
+ def isPwwnInDaDatabase(self, pwwn):
+ newpwwn = ":".join(
+ ["0" + str(ep) if len(ep) == 1 else ep for ep in pwwn.split(":")]
+ )
+ return newpwwn in self.da_dict.values()
+
+ def isNamePwwnPresentInDatabase(self, name, pwwn):
+ newpwwn = ":".join(
+ ["0" + str(ep) if len(ep) == 1 else ep for ep in pwwn.split(":")]
+ )
+ if name in self.da_dict.keys():
+ if newpwwn == self.da_dict[name]:
+ return True
+ return False
+
+ def getPwwnByName(self, name):
+ if name in self.da_dict.keys():
+ return self.da_dict[name]
+ else:
+ return None
+
+ def getNameByPwwn(self, pwwn):
+ newpwwn = ":".join(
+ ["0" + str(ep) if len(ep) == 1 else ep for ep in pwwn.split(":")]
+ )
+ for n, p in self.da_dict.items():
+ if p == newpwwn:
+ return n
+ return None
+
+
+def isPwwnValid(pwwn):
+ pwwnsplit = pwwn.split(":")
+ if len(pwwnsplit) != 8:
+ return False
+ for eachpwwnsplit in pwwnsplit:
+ if len(eachpwwnsplit) > 2 or len(eachpwwnsplit) < 1:
+ return False
+ if not all(c in string.hexdigits for c in eachpwwnsplit):
+ return False
+ return True
+
+
+def isNameValid(name):
+ if not name[0].isalpha():
+ # Illegal first character. Name must start with a letter
+ return False
+ if len(name) > 64:
+ return False
+ for character in name:
+ if not character.isalnum() and character not in VALID_DA_CHARS:
+ return False
+ return True
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ output = "text"
+ commands = [{"command": command, "output": output}]
+ out = run_commands(module, commands)
+ return out
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def main():
+ element_spec = dict(
+ name=dict(required=True, type="str"),
+ pwwn=dict(type="str"),
+ remove=dict(type="bool", default=False),
+ )
+
+ element_spec_rename = dict(
+ old_name=dict(required=True, type="str"),
+ new_name=dict(required=True, type="str"),
+ )
+
+ argument_spec = dict(
+ distribute=dict(type="bool"),
+ mode=dict(type="str", choices=["enhanced", "basic"]),
+ da=dict(type="list", elements="dict", options=element_spec),
+ rename=dict(type="list", elements="dict", options=element_spec_rename),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ messages = list()
+ commands_to_execute = list()
+ result = {"changed": False}
+
+ distribute = module.params["distribute"]
+ mode = module.params["mode"]
+ da = module.params["da"]
+ rename = module.params["rename"]
+
+ # Step 0.0: Validate syntax of name and pwwn
+ # Also validate syntax of rename arguments
+ if da is not None:
+ for eachdict in da:
+ name = eachdict["name"]
+ pwwn = eachdict["pwwn"]
+ remove = eachdict["remove"]
+ if pwwn is not None:
+ pwwn = pwwn.lower()
+ if not remove:
+ if pwwn is None:
+ module.fail_json(
+ msg="This device alias name "
+ + str(name)
+ + " which needs to be added, does not have pwwn specified. Please specify a valid pwwn"
+ )
+ if not isNameValid(name):
+ module.fail_json(
+ msg="This pwwn name is invalid : "
+ + str(name)
+ + ". Note that name cannot be more than 64 alphanumeric chars, "
+ + "it must start with a letter, and can only contain these characters: "
+ + ", ".join(
+ ["'{0}'".format(c) for c in VALID_DA_CHARS]
+ )
+ )
+ if not isPwwnValid(pwwn):
+ module.fail_json(
+ msg="This pwwn is invalid : "
+ + str(pwwn)
+ + ". Please check that its a valid pwwn"
+ )
+ if rename is not None:
+ for eachdict in rename:
+ oldname = eachdict["old_name"]
+ newname = eachdict["new_name"]
+ if not isNameValid(oldname):
+ module.fail_json(
+ msg="This pwwn name is invalid : "
+ + str(oldname)
+ + ". Note that name cannot be more than 64 alphanumeric chars, "
+ + "it must start with a letter, and can only contain these characters: "
+ + ", ".join(["'{0}'".format(c) for c in VALID_DA_CHARS])
+ )
+ if not isNameValid(newname):
+ module.fail_json(
+ msg="This pwwn name is invalid : "
+ + str(newname)
+ + ". Note that name cannot be more than 64 alphanumeric chars, "
+ + "it must start with a letter, and can only contain these characters: "
+ + ", ".join(["'{0}'".format(c) for c in VALID_DA_CHARS])
+ )
+
+ # Step 0.1: Check DA status
+ shDAStausObj = showDeviceAliasStatus(module)
+ d = shDAStausObj.getDistribute()
+ m = shDAStausObj.getMode()
+ if shDAStausObj.isLocked():
+ module.fail_json(
+ msg="device-alias has acquired lock on the switch. Hence cannot procced."
+ )
+
+ # Step 1: Process distribute
+ commands = []
+ if distribute is not None:
+ if distribute:
+ # playbook has distribute as True(enabled)
+ if d == "disabled":
+ # but switch distribute is disabled(false), so set it to
+ # true(enabled)
+ commands.append("device-alias distribute")
+ messages.append(
+ "device-alias distribute changed from disabled to enabled"
+ )
+ else:
+ messages.append(
+ "device-alias distribute remains unchanged. current distribution mode is enabled"
+ )
+ else:
+ # playbook has distribute as False(disabled)
+ if d == "enabled":
+ # but switch distribute is enabled(true), so set it to
+ # false(disabled)
+ commands.append("no device-alias distribute")
+ messages.append(
+ "device-alias distribute changed from enabled to disabled"
+ )
+ else:
+ messages.append(
+ "device-alias distribute remains unchanged. current distribution mode is disabled"
+ )
+
+ cmds = flatten_list(commands)
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the da_add/da_remove stage
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+
+ # Step 2: Process mode
+ commands = []
+ if mode is not None:
+ if mode == "basic":
+ # playbook has mode as basic
+ if m == "enhanced":
+ # but switch mode is enhanced, so set it to basic
+ commands.append("no device-alias mode enhanced")
+ messages.append(
+ "device-alias mode changed from enhanced to basic"
+ )
+ else:
+ messages.append(
+ "device-alias mode remains unchanged. current mode is basic"
+ )
+
+ else:
+ # playbook has mode as enhanced
+ if m == "basic":
+ # but switch mode is basic, so set it to enhanced
+ commands.append("device-alias mode enhanced")
+ messages.append(
+ "device-alias mode changed from basic to enhanced"
+ )
+ else:
+ messages.append(
+ "device-alias mode remains unchanged. current mode is enhanced"
+ )
+
+ if commands:
+ if distribute:
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+ else:
+ if distribute is None and d == "enabled":
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands)
+
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the end
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+
+ # Step 3: Process da
+ commands = []
+ shDADatabaseObj = showDeviceAliasDatabase(module)
+ if da is not None:
+ da_remove_list = []
+ da_add_list = []
+ for eachdict in da:
+ name = eachdict["name"]
+ pwwn = eachdict["pwwn"]
+ remove = eachdict["remove"]
+ if pwwn is not None:
+ pwwn = pwwn.lower()
+ if remove:
+ if shDADatabaseObj.isNameInDaDatabase(name):
+ commands.append("no device-alias name " + name)
+ da_remove_list.append(name)
+ else:
+ messages.append(
+ name
+ + " - This device alias name is not in switch device-alias database, hence cannot be removed."
+ )
+ else:
+ if shDADatabaseObj.isNamePwwnPresentInDatabase(name, pwwn):
+ messages.append(
+ name
+ + " : "
+ + pwwn
+ + " - This device alias name,pwwn is already in switch device-alias database, hence nothing to configure"
+ )
+ else:
+ if shDADatabaseObj.isNameInDaDatabase(name):
+ module.fail_json(
+ msg=name
+ + " - This device alias name is already present in switch device-alias database but assigned to another pwwn ("
+ + shDADatabaseObj.getPwwnByName(name)
+ + ") hence cannot be added"
+ )
+
+ elif shDADatabaseObj.isPwwnInDaDatabase(pwwn):
+ module.fail_json(
+ msg=pwwn
+ + " - This device alias pwwn is already present in switch device-alias database but assigned to another name ("
+ + shDADatabaseObj.getNameByPwwn(pwwn)
+ + ") hence cannot be added"
+ )
+
+ else:
+ commands.append(
+ "device-alias name " + name + " pwwn " + pwwn
+ )
+ da_add_list.append(name)
+
+ if len(da_add_list) != 0 or len(da_remove_list) != 0:
+ commands = ["device-alias database"] + commands
+ if distribute:
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+ else:
+ if distribute is None and d == "enabled":
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"]
+ + commands
+ + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands)
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the end
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+ if len(da_remove_list) != 0:
+ messages.append(
+ "the required device-alias were removed. "
+ + ",".join(da_remove_list)
+ )
+ if len(da_add_list) != 0:
+ messages.append(
+ "the required device-alias were added. "
+ + ",".join(da_add_list)
+ )
+
+ # Step 5: Process rename
+ commands = []
+ if rename is not None:
+ for eachdict in rename:
+ oldname = eachdict["old_name"]
+ newname = eachdict["new_name"]
+ if shDADatabaseObj.isNameInDaDatabase(newname):
+ module.fail_json(
+ changed=False,
+ commands=cmds,
+ msg=newname
+ + " - this name is already present in the device-alias database, hence we cannot rename "
+ + oldname
+ + " with this one",
+ )
+ if shDADatabaseObj.isNameInDaDatabase(oldname):
+ commands.append(
+ "device-alias rename " + oldname + " " + newname
+ )
+ else:
+ module.fail_json(
+ changed=False,
+ commands=cmds,
+ msg=oldname
+ + " - this name is not present in the device-alias database, hence we cannot rename.",
+ )
+
+ if len(commands) != 0:
+ commands = ["device-alias database"] + commands
+ if distribute:
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"] + commands + ["no terminal dont-ask"]
+ )
+ else:
+ if distribute is None and d == "enabled":
+ commands.append("device-alias commit")
+ commands = (
+ ["terminal dont-ask"]
+ + commands
+ + ["no terminal dont-ask"]
+ )
+ cmds = flatten_list(commands)
+ if cmds:
+ commands_to_execute = commands_to_execute + cmds
+ if module.check_mode:
+ # Check mode implemented at the end
+ pass
+ else:
+ result["changed"] = True
+ load_config(module, cmds)
+
+ # Step END: check for 'check' mode
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ commands=commands_to_execute,
+ msg="Check Mode: No cmds issued to the hosts",
+ )
+
+ result["messages"] = messages
+ result["commands"] = commands_to_execute
+ result["warnings"] = warnings
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_vsan.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_vsan.py
new file mode 100644
index 00000000..c68dd0ca
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_vsan.py
@@ -0,0 +1,385 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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
+
+
+DOCUMENTATION = """
+module: nxos_vsan
+short_description: Configuration of vsan for Cisco NXOS MDS Switches.
+description:
+- Configuration of vsan for Cisco MDS NXOS.
+version_added: 1.0.0
+author:
+- Suhas Bharadwaj (@srbharadwaj) (subharad@cisco.com)
+notes:
+- Tested against NX-OS 8.4(1)
+options:
+ vsan:
+ description:
+ - List of vsan details to be added or removed
+ type: list
+ elements: dict
+ suboptions:
+ id:
+ description:
+ - Vsan id
+ required: true
+ type: int
+ name:
+ description:
+ - Name of the vsan
+ type: str
+ suspend:
+ description:
+ - suspend the vsan if True
+ type: bool
+ remove:
+ description:
+ - Removes the vsan if True
+ type: bool
+ interface:
+ description:
+ - List of vsan's interfaces to be added
+ type: list
+ elements: str
+"""
+
+EXAMPLES = """
+- name: Test that vsan module works
+ cisco.nxos.nxos_vsan:
+ vsan:
+ - id: 922
+ interface:
+ - fc1/1
+ - fc1/2
+ - port-channel 1
+ name: vsan-SAN-A
+ remove: false
+ suspend: false
+ - id: 923
+ interface:
+ - fc1/11
+ - fc1/21
+ - port-channel 2
+ name: vsan-SAN-B
+ remove: false
+ suspend: true
+ - id: 1923
+ name: vsan-SAN-Old
+ remove: true
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - terminal dont-ask
+ - vsan database
+ - vsan 922 interface fc1/40
+ - vsan 922 interface port-channel 155
+ - no terminal dont-ask
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+import re
+
+__metaclass__ = type
+
+
+class Vsan(object):
+ def __init__(self, vsanid):
+ self.vsanid = vsanid
+ self.vsanname = None
+ self.vsanstate = None
+ self.vsanoperstate = None
+ self.vsaninterfaces = []
+
+
+class GetVsanInfoFromSwitch(object):
+ """docstring for GetVsanInfoFromSwitch"""
+
+ def __init__(self, module):
+ self.module = module
+ self.vsaninfo = {}
+ self.processShowVsan()
+ self.processShowVsanMembership()
+
+ def execute_show_vsan_cmd(self):
+ output = execute_show_command("show vsan", self.module)[0]
+ return output
+
+ def execute_show_vsan_mem_cmd(self):
+ output = execute_show_command("show vsan membership", self.module)[0]
+ return output
+
+ def processShowVsan(self):
+ patv = r"^vsan\s+(\d+)\s+information"
+ patnamestate = "name:(.*)state:(.*)"
+ patoperstate = "operational state:(.*)"
+
+ output = self.execute_show_vsan_cmd().split("\n")
+ for o in output:
+ z = re.match(patv, o.strip())
+ if z:
+ v = z.group(1).strip()
+ self.vsaninfo[v] = Vsan(v)
+
+ z1 = re.match(patnamestate, o.strip())
+ if z1:
+ n = z1.group(1).strip()
+ s = z1.group(2).strip()
+ self.vsaninfo[v].vsanname = n
+ self.vsaninfo[v].vsanstate = s
+
+ z2 = re.match(patoperstate, o.strip())
+ if z2:
+ oper = z2.group(1).strip()
+ self.vsaninfo[v].vsanoperstate = oper
+
+ # 4094/4079 vsan is always present
+ self.vsaninfo["4079"] = Vsan("4079")
+ self.vsaninfo["4094"] = Vsan("4094")
+
+ def processShowVsanMembership(self):
+ patv = r"^vsan\s+(\d+).*"
+ output = self.execute_show_vsan_mem_cmd().split("\n")
+ memlist = []
+ v = None
+ for o in output:
+ z = re.match(patv, o.strip())
+ if z:
+ if v is not None:
+ self.vsaninfo[v].vsaninterfaces = memlist
+ memlist = []
+ v = z.group(1)
+ if "interfaces" not in o:
+ llist = o.strip().split()
+ memlist = memlist + llist
+ self.vsaninfo[v].vsaninterfaces = memlist
+
+ def getVsanInfoObjects(self):
+ return self.vsaninfo
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ output = "text"
+ commands = [{"command": command, "output": output}]
+ return run_commands(module, commands)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def main():
+ vsan_element_spec = dict(
+ id=dict(required=True, type="int"),
+ name=dict(type="str"),
+ remove=dict(type="bool"),
+ suspend=dict(type="bool"),
+ interface=dict(type="list", elements="str"),
+ )
+
+ argument_spec = dict(
+ vsan=dict(type="list", elements="dict", options=vsan_element_spec)
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+ warnings = list()
+ messages = list()
+ commands_executed = list()
+ result = {"changed": False}
+
+ obj = GetVsanInfoFromSwitch(module)
+ dictSwVsanObjs = obj.getVsanInfoObjects()
+
+ commands = []
+ vsan_list = module.params["vsan"]
+
+ for eachvsan in vsan_list:
+ vsanid = str(eachvsan["id"])
+ vsanname = eachvsan["name"]
+ vsanremove = eachvsan["remove"]
+ vsansuspend = eachvsan["suspend"]
+ vsaninterface_list = eachvsan["interface"]
+
+ if int(vsanid) < 1 or int(vsanid) >= 4095:
+ module.fail_json(
+ msg=vsanid
+ + " - This is an invalid vsan. Supported vsan range is 1-4094"
+ )
+
+ if vsanid in dictSwVsanObjs.keys():
+ sw_vsanid = vsanid
+ sw_vsanname = dictSwVsanObjs[vsanid].vsanname
+ sw_vsanstate = dictSwVsanObjs[vsanid].vsanstate
+ sw_vsaninterfaces = dictSwVsanObjs[vsanid].vsaninterfaces
+ else:
+ sw_vsanid = None
+ sw_vsanname = None
+ sw_vsanstate = None
+ sw_vsaninterfaces = []
+
+ if vsanremove:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid)
+ + " is a reserved vsan, hence cannot be removed"
+ )
+ continue
+ if vsanid == sw_vsanid:
+ commands.append("no vsan " + str(vsanid))
+ messages.append("deleting the vsan " + str(vsanid))
+ else:
+ messages.append(
+ "There is no vsan "
+ + str(vsanid)
+ + " present in the switch. Hence there is nothing to delete"
+ )
+ continue
+ else:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid)
+ + " is a reserved vsan, and always present on the switch"
+ )
+ else:
+ if vsanid == sw_vsanid:
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch. Hence there is nothing to configure"
+ )
+ else:
+ commands.append("vsan " + str(vsanid))
+ messages.append("creating vsan " + str(vsanid))
+
+ if vsanname is not None:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid) + " is a reserved vsan, and cannot be renamed"
+ )
+ else:
+ if vsanname == sw_vsanname:
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch, which has the name "
+ + vsanname
+ + " Hence there is nothing to configure"
+ )
+ else:
+ commands.append(
+ "vsan " + str(vsanid) + " name " + vsanname
+ )
+ messages.append(
+ "setting vsan name to "
+ + vsanname
+ + " for vsan "
+ + str(vsanid)
+ )
+
+ if vsansuspend:
+ # Negative case:
+ if vsanid == "4079" or vsanid == "4094":
+ messages.append(
+ str(vsanid)
+ + " is a reserved vsan, and cannot be suspended"
+ )
+ else:
+ if sw_vsanstate == "suspended":
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch, which is in suspended state "
+ )
+ else:
+ commands.append("vsan " + str(vsanid) + " suspend")
+ messages.append("suspending the vsan " + str(vsanid))
+ else:
+ if sw_vsanstate == "active":
+ messages.append(
+ "There is already a vsan "
+ + str(vsanid)
+ + " present in the switch, which is in active state "
+ )
+ else:
+ commands.append("no vsan " + str(vsanid) + " suspend")
+ messages.append("no suspending the vsan " + str(vsanid))
+
+ if vsaninterface_list is not None:
+ for each_interface_name in vsaninterface_list:
+ # For fcip,port-channel,vfc-port-channel need to remove the
+ # extra space to compare
+ temp = re.sub(" +", "", each_interface_name)
+ if temp in sw_vsaninterfaces:
+ messages.append(
+ each_interface_name
+ + " is already present in the vsan "
+ + str(vsanid)
+ + " interface list"
+ )
+ else:
+ commands.append(
+ "vsan "
+ + str(vsanid)
+ + " interface "
+ + each_interface_name
+ )
+ messages.append(
+ "adding interface "
+ + each_interface_name
+ + " to vsan "
+ + str(vsanid)
+ )
+
+ if len(commands) != 0:
+ commands = (
+ ["terminal dont-ask"]
+ + ["vsan database"]
+ + commands
+ + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands)
+ commands_executed = cmds
+
+ if commands_executed:
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ commands=commands_executed,
+ msg="Check Mode: No cmds issued to the hosts",
+ )
+ else:
+ result["changed"] = True
+ load_config(module, commands_executed)
+
+ result["messages"] = messages
+ result["commands"] = commands_executed
+ result["warnings"] = warnings
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_zone_zoneset.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_zone_zoneset.py
new file mode 100644
index 00000000..299b5898
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/modules/storage/nxos_zone_zoneset.py
@@ -0,0 +1,990 @@
+#!/usr/bin/python
+# Copyright: Ansible Project
+# 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
+
+
+DOCUMENTATION = """
+module: nxos_zone_zoneset
+short_description: Configuration of zone/zoneset for Cisco NXOS MDS Switches.
+description:
+- Configuration of zone/zoneset for Cisco MDS NXOS.
+version_added: 1.0.0
+author:
+- Suhas Bharadwaj (@srbharadwaj) (subharad@cisco.com)
+notes:
+- Tested against NX-OS 8.4(1)
+options:
+ zone_zoneset_details:
+ description:
+ - List of zone/zoneset details to be added or removed
+ type: list
+ elements: dict
+ suboptions:
+ vsan:
+ description:
+ - vsan id
+ required: true
+ type: int
+ mode:
+ description:
+ - mode of the zone for the vsan
+ choices:
+ - enhanced
+ - basic
+ type: str
+ default_zone:
+ description:
+ - default zone behaviour for the vsan
+ choices:
+ - permit
+ - deny
+ type: str
+ smart_zoning:
+ description:
+ - Removes the vsan if True
+ type: bool
+ zone:
+ description:
+ - List of zone options for that vsan
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - name of the zone
+ required: true
+ type: str
+ remove:
+ description:
+ - Deletes the zone if True
+ type: bool
+ default: false
+ members:
+ description:
+ - Members of the zone that needs to be removed or added
+ type: list
+ elements: dict
+ suboptions:
+ pwwn:
+ description:
+ - pwwn member of the zone, use alias 'device_alias' as option for
+ device_alias member
+ aliases:
+ - device_alias
+ required: true
+ type: str
+ remove:
+ description:
+ - Removes member from the zone if True
+ type: bool
+ default: false
+ devtype:
+ description:
+ - devtype of the zone member used along with Smart zoning config
+ choices:
+ - initiator
+ - target
+ - both
+ type: str
+ zoneset:
+ description:
+ - List of zoneset options for the vsan
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - name of the zoneset
+ required: true
+ type: str
+ remove:
+ description:
+ - Removes zoneset if True
+ type: bool
+ default: false
+ action:
+ description:
+ - activates/de-activates the zoneset
+ choices:
+ - activate
+ - deactivate
+ type: str
+ members:
+ description:
+ - Members of the zoneset that needs to be removed or added
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - name of the zone that needs to be added to the zoneset or removed
+ from the zoneset
+ required: true
+ type: str
+ remove:
+ description:
+ - Removes zone member from the zoneset
+ type: bool
+ default: false
+"""
+
+EXAMPLES = """
+- name: Test that zone/zoneset module works
+ cisco.nxos.nxos_zone_zoneset:
+ zone_zoneset_details:
+ - mode: enhanced
+ vsan: 22
+ zone:
+ - members:
+ - pwwn: 11:11:11:11:11:11:11:11
+ - device_alias: test123
+ - pwwn: 61:61:62:62:12:12:12:12
+ remove: true
+ name: zoneA
+ - members:
+ - pwwn: 10:11:11:11:11:11:11:11
+ - pwwn: 62:62:62:62:21:21:21:21
+ name: zoneB
+ - name: zoneC
+ remove: true
+ zoneset:
+ - action: activate
+ members:
+ - name: zoneA
+ - name: zoneB
+ - name: zoneC
+ remove: true
+ name: zsetname1
+ - action: deactivate
+ name: zsetTestExtra
+ remove: true
+ - mode: basic
+ smart_zoning: true
+ vsan: 21
+ zone:
+ - members:
+ - devtype: both
+ pwwn: 11:11:11:11:11:11:11:11
+ - pwwn: 62:62:62:62:12:12:12:12
+ - devtype: both
+ pwwn: 92:62:62:62:12:12:1a:1a
+ remove: true
+ name: zone21A
+ - members:
+ - pwwn: 10:11:11:11:11:11:11:11
+ - pwwn: 62:62:62:62:21:21:21:21
+ name: zone21B
+ zoneset:
+ - action: activate
+ members:
+ - name: zone21A
+ - name: zone21B
+ name: zsetname212
+
+"""
+
+RETURN = """
+commands:
+ description: commands sent to the device
+ returned: always
+ type: list
+ sample:
+ - terminal dont-ask
+ - zone name zoneA vsan 923
+ - member pwwn 11:11:11:11:11:11:11:11
+ - no member device-alias test123
+ - zone commit vsan 923
+ - no terminal dont-ask
+messages:
+ description: debug messages
+ returned: always
+ type: list
+ sample:
+ - "zone mode is already enhanced ,no change in zone mode configuration for vsan 922"
+ - "zone member '11:11:11:11:11:11:11:11' is already present in zone 'zoneA' in vsan 922 hence nothing to add"
+ - "zone member 'test123' is already present in zone 'zoneA' in vsan 922 hence nothing to add"
+ - "zone member '61:61:62:62:12:12:12:12' is not present in zone 'zoneA' in vsan 922 hence nothing to remove"
+ - "zone member '10:11:11:11:11:11:11:11' is already present in zone 'zoneB' in vsan 922 hence nothing to add"
+ - "zone member '62:62:62:62:21:21:21:21' is already present in zone 'zoneB' in vsan 922 hence nothing to add"
+ - "zone 'zoneC' is not present in vsan 922 , so nothing to remove"
+"""
+
+
+import re
+import json
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
+ load_config,
+ run_commands,
+)
+
+
+__metaclass__ = type
+
+
+class ShowZonesetActive(object):
+ """docstring for ShowZonesetActive"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.module = module
+ self.activeZSName = None
+ self.parseCmdOutput()
+
+ def execute_show_zoneset_active_cmd(self):
+ command = (
+ "show zoneset active vsan " + str(self.vsan) + " | grep zoneset"
+ )
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def parseCmdOutput(self):
+ patZoneset = r"zoneset name (\S+) vsan " + str(self.vsan)
+ output = self.execute_show_zoneset_active_cmd().split("\n")
+ if len(output) == 0:
+ return
+ else:
+ for line in output:
+ line = line.strip()
+ mzs = re.match(patZoneset, line.strip())
+ if mzs:
+ self.activeZSName = mzs.group(1).strip()
+ return
+
+ def isZonesetActive(self, zsname):
+ if zsname == self.activeZSName:
+ return True
+ return False
+
+
+class ShowZoneset(object):
+ """docstring for ShowZoneset"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.module = module
+ self.zsDetails = {}
+ self.parseCmdOutput()
+
+ def execute_show_zoneset_cmd(self):
+ command = "show zoneset vsan " + str(self.vsan)
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def parseCmdOutput(self):
+ patZoneset = r"zoneset name (\S+) vsan " + str(self.vsan)
+ patZone = r"zone name (\S+) vsan " + str(self.vsan)
+ output = self.execute_show_zoneset_cmd().split("\n")
+ for line in output:
+ line = line.strip()
+ mzs = re.match(patZoneset, line.strip())
+ mz = re.match(patZone, line.strip())
+ if mzs:
+ zonesetname = mzs.group(1).strip()
+ self.zsDetails[zonesetname] = []
+ continue
+ elif mz:
+ zonename = mz.group(1).strip()
+ v = self.zsDetails[zonesetname]
+ v.append(zonename)
+ self.zsDetails[zonesetname] = v
+
+ def isZonesetPresent(self, zsname):
+ return zsname in self.zsDetails.keys()
+
+ def isZonePresentInZoneset(self, zsname, zname):
+ if zsname in self.zsDetails.keys():
+ return zname in self.zsDetails[zsname]
+ return False
+
+
+class ShowZone(object):
+ """docstring for ShowZone"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.module = module
+ self.zDetails = {}
+ self.parseCmdOutput()
+
+ def execute_show_zone_vsan_cmd(self):
+ command = "show zone vsan " + str(self.vsan)
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def parseCmdOutput(self):
+ patZone = r"zone name (\S+) vsan " + str(self.vsan)
+ output = self.execute_show_zone_vsan_cmd().split("\n")
+ for line in output:
+ line = " ".join(line.strip().split())
+ if "init" in line:
+ line = line.replace("init", "initiator")
+ m = re.match(patZone, line)
+ if m:
+ zonename = m.group(1).strip()
+ self.zDetails[zonename] = []
+ continue
+ else:
+ # For now we support only pwwn and device-alias under zone
+ # Ideally should use 'supported_choices'....but maybe next
+ # time.
+ if "pwwn" in line or "device-alias" in line:
+ v = self.zDetails[zonename]
+ v.append(line)
+ self.zDetails[zonename] = v
+
+ def isZonePresent(self, zname):
+ return zname in self.zDetails.keys()
+
+ def isZoneMemberPresent(self, zname, cmd):
+ if zname in self.zDetails.keys():
+ zonememlist = self.zDetails[zname]
+ for eachline in zonememlist:
+ if cmd in eachline:
+ return True
+ return False
+
+ def get_zDetails(self):
+ return self.zDetails
+
+
+class ShowZoneStatus(object):
+ """docstring for ShowZoneStatus"""
+
+ def __init__(self, module, vsan):
+ self.vsan = vsan
+ self.vsanAbsent = False
+ self.module = module
+ self.default_zone = ""
+ self.mode = ""
+ self.session = ""
+ self.sz = ""
+ self.locked = False
+ self.update()
+
+ def execute_show_zone_status_cmd(self):
+ command = "show zone status vsan " + str(self.vsan)
+ output = execute_show_command(command, self.module)[0]
+ return output
+
+ def update(self):
+
+ output = self.execute_show_zone_status_cmd().split("\n")
+
+ patfordefzone = "VSAN: " + str(self.vsan) + r" default-zone:\s+(\S+).*"
+ patformode = r".*mode:\s+(\S+).*"
+ patforsession = r"^session:\s+(\S+).*"
+ patforsz = r".*smart-zoning:\s+(\S+).*"
+ for line in output:
+ if "is not configured" in line:
+ self.vsanAbsent = True
+ break
+ mdefz = re.match(patfordefzone, line.strip())
+ mmode = re.match(patformode, line.strip())
+ msession = re.match(patforsession, line.strip())
+ msz = re.match(patforsz, line.strip())
+
+ if mdefz:
+ self.default_zone = mdefz.group(1)
+ if mmode:
+ self.mode = mmode.group(1)
+ if msession:
+ self.session = msession.group(1)
+ if self.session != "none":
+ self.locked = True
+ if msz:
+ self.sz = msz.group(1)
+
+ def isLocked(self):
+ return self.locked
+
+ def getDefaultZone(self):
+ return self.default_zone
+
+ def getMode(self):
+ return self.mode
+
+ def getSmartZoningStatus(self):
+ return self.sz
+
+ def isVsanAbsent(self):
+ return self.vsanAbsent
+
+
+def execute_show_command(command, module, command_type="cli_show"):
+ output = "text"
+ commands = [{"command": command, "output": output}]
+ return run_commands(module, commands)
+
+
+def flatten_list(command_lists):
+ flat_command_list = []
+ for command in command_lists:
+ if isinstance(command, list):
+ flat_command_list.extend(command)
+ else:
+ flat_command_list.append(command)
+ return flat_command_list
+
+
+def getMemType(supported_choices, allmemkeys, default="pwwn"):
+ for eachchoice in supported_choices:
+ if eachchoice in allmemkeys:
+ return eachchoice
+ return default
+
+
+def main():
+
+ supported_choices = ["device_alias"]
+ zone_member_spec = dict(
+ pwwn=dict(required=True, type="str", aliases=["device_alias"]),
+ devtype=dict(type="str", choices=["initiator", "target", "both"]),
+ remove=dict(type="bool", default=False),
+ )
+
+ zone_spec = dict(
+ name=dict(required=True, type="str"),
+ members=dict(type="list", elements="dict", options=zone_member_spec),
+ remove=dict(type="bool", default=False),
+ )
+
+ zoneset_member_spec = dict(
+ name=dict(required=True, type="str"),
+ remove=dict(type="bool", default=False),
+ )
+
+ zoneset_spec = dict(
+ name=dict(type="str", required=True),
+ members=dict(
+ type="list", elements="dict", options=zoneset_member_spec
+ ),
+ remove=dict(type="bool", default=False),
+ action=dict(type="str", choices=["activate", "deactivate"]),
+ )
+
+ zonedetails_spec = dict(
+ vsan=dict(required=True, type="int"),
+ mode=dict(type="str", choices=["enhanced", "basic"]),
+ default_zone=dict(type="str", choices=["permit", "deny"]),
+ smart_zoning=dict(type="bool"),
+ zone=dict(type="list", elements="dict", options=zone_spec),
+ zoneset=dict(type="list", elements="dict", options=zoneset_spec),
+ )
+
+ argument_spec = dict(
+ zone_zoneset_details=dict(
+ type="list", elements="dict", options=zonedetails_spec
+ )
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec, supports_check_mode=True
+ )
+
+ warnings = list()
+ messages = list()
+ commands = list()
+ result = {"changed": False}
+
+ commands_executed = []
+ listOfZoneDetails = module.params["zone_zoneset_details"]
+ for eachZoneZonesetDetail in listOfZoneDetails:
+ vsan = eachZoneZonesetDetail["vsan"]
+ op_mode = eachZoneZonesetDetail["mode"]
+ op_default_zone = eachZoneZonesetDetail["default_zone"]
+ op_smart_zoning = eachZoneZonesetDetail["smart_zoning"]
+ op_zone = eachZoneZonesetDetail["zone"]
+ op_zoneset = eachZoneZonesetDetail["zoneset"]
+
+ # Step1: execute show zone status and get
+ shZoneStatusObj = ShowZoneStatus(module, vsan)
+ sw_default_zone = shZoneStatusObj.getDefaultZone()
+ sw_mode = shZoneStatusObj.getMode()
+ sw_smart_zoning = shZoneStatusObj.getSmartZoningStatus()
+
+ if sw_smart_zoning.lower() == "Enabled".lower():
+ sw_smart_zoning_bool = True
+ else:
+ sw_smart_zoning_bool = False
+
+ if shZoneStatusObj.isVsanAbsent():
+ module.fail_json(
+ msg="Vsan "
+ + str(vsan)
+ + " is not present in the switch. Hence cannot procced."
+ )
+
+ if shZoneStatusObj.isLocked():
+ module.fail_json(
+ msg="zone has acquired lock on the switch for vsan "
+ + str(vsan)
+ + ". Hence cannot procced."
+ )
+
+ # Process zone default zone options
+ if op_default_zone is not None:
+ if op_default_zone != sw_default_zone:
+ if op_default_zone == "permit":
+ commands_executed.append(
+ "zone default-zone permit vsan " + str(vsan)
+ )
+ messages.append(
+ "default zone configuration changed from deny to permit for vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "no zone default-zone permit vsan " + str(vsan)
+ )
+ messages.append(
+ "default zone configuration changed from permit to deny for vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "default zone is already "
+ + op_default_zone
+ + " ,no change in default zone configuration for vsan "
+ + str(vsan)
+ )
+
+ # Process zone mode options
+ if op_mode is not None:
+ if op_mode != sw_mode:
+ if op_mode == "enhanced":
+ commands_executed.append(
+ "zone mode enhanced vsan " + str(vsan)
+ )
+ messages.append(
+ "zone mode configuration changed from basic to enhanced for vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "no zone mode enhanced vsan " + str(vsan)
+ )
+ messages.append(
+ "zone mode configuration changed from enhanced to basic for vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zone mode is already "
+ + op_mode
+ + " ,no change in zone mode configuration for vsan "
+ + str(vsan)
+ )
+
+ # Process zone smart-zone options
+ if op_smart_zoning is not None:
+ if op_smart_zoning != sw_smart_zoning_bool:
+ if op_smart_zoning:
+ commands_executed.append(
+ "zone smart-zoning enable vsan " + str(vsan)
+ )
+ messages.append(
+ "smart-zoning enabled for vsan " + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "no zone smart-zoning enable vsan " + str(vsan)
+ )
+ messages.append(
+ "smart-zoning disabled for vsan " + str(vsan)
+ )
+ else:
+ messages.append(
+ "smart-zoning is already set to "
+ + sw_smart_zoning
+ + " , no change in smart-zoning configuration for vsan "
+ + str(vsan)
+ )
+
+ # Process zone member options
+ # TODO: Obviously this needs to be cleaned up properly, as there are a lot of ifelse statements which is bad
+ # Will take it up later becoz of time constraints
+ if op_zone is not None:
+ shZoneObj = ShowZone(module, vsan)
+ for eachzone in op_zone:
+ zname = eachzone["name"]
+ zmembers = eachzone["members"]
+ removeflag = eachzone["remove"]
+ if removeflag:
+ if shZoneObj.isZonePresent(zname):
+ messages.append(
+ "zone '"
+ + zname
+ + "' is removed from vsan "
+ + str(vsan)
+ )
+ commands_executed.append(
+ "no zone name " + zname + " vsan " + str(vsan)
+ )
+ else:
+ messages.append(
+ "zone '"
+ + zname
+ + "' is not present in vsan "
+ + str(vsan)
+ + " , so nothing to remove"
+ )
+ else:
+ if zmembers is None:
+ if shZoneObj.isZonePresent(zname):
+ messages.append(
+ "zone '"
+ + zname
+ + "' is already present in vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "zone name " + zname + " vsan " + str(vsan)
+ )
+ messages.append(
+ "zone '"
+ + zname
+ + "' is created in vsan "
+ + str(vsan)
+ )
+ else:
+ cmdmemlist = []
+ for eachmem in zmembers:
+ memtype = getMemType(
+ supported_choices, eachmem.keys()
+ )
+ cmd = (
+ memtype.replace("_", "-")
+ + " "
+ + eachmem[memtype]
+ )
+ if op_smart_zoning or sw_smart_zoning_bool:
+ if eachmem["devtype"] is not None:
+ cmd = cmd + " " + eachmem["devtype"]
+ if eachmem["remove"]:
+ if shZoneObj.isZonePresent(zname):
+ if shZoneObj.isZoneMemberPresent(
+ zname, cmd
+ ):
+ cmd = "no member " + cmd
+ cmdmemlist.append(cmd)
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "removing zone member '"
+ + eachmem[memtype]
+ + " of device type '"
+ + eachmem["devtype"]
+ + "' from zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "removing zone member '"
+ + eachmem[memtype]
+ + "' from zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' of device type '"
+ + eachmem["devtype"]
+ + "' is not present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to remove"
+ )
+ else:
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' is not present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to remove"
+ )
+ else:
+ messages.append(
+ "zone '"
+ + zname
+ + "' is not present in vsan "
+ + str(vsan)
+ + " , hence cannot remove the members"
+ )
+
+ else:
+ if shZoneObj.isZoneMemberPresent(zname, cmd):
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' of device type '"
+ + eachmem["devtype"]
+ + "' is already present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to add"
+ )
+ else:
+ messages.append(
+ "zone member '"
+ + eachmem[memtype]
+ + "' is already present in zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ + " hence nothing to add"
+ )
+ else:
+ cmd = "member " + cmd
+ cmdmemlist.append(cmd)
+ if (
+ op_smart_zoning
+ and eachmem["devtype"] is not None
+ ):
+ messages.append(
+ "adding zone member '"
+ + eachmem[memtype]
+ + "' of device type '"
+ + eachmem["devtype"]
+ + "' to zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "adding zone member '"
+ + eachmem[memtype]
+ + "' to zone '"
+ + zname
+ + "' in vsan "
+ + str(vsan)
+ )
+ if len(cmdmemlist) != 0:
+ commands_executed.append(
+ "zone name " + zname + " vsan " + str(vsan)
+ )
+ commands_executed = commands_executed + cmdmemlist
+
+ # Process zoneset member options
+ if op_zoneset is not None:
+ dactcmd = []
+ actcmd = []
+ shZonesetObj = ShowZoneset(module, vsan)
+ shZonesetActiveObj = ShowZonesetActive(module, vsan)
+ for eachzoneset in op_zoneset:
+ zsetname = eachzoneset["name"]
+ zsetmembers = eachzoneset["members"]
+ removeflag = eachzoneset["remove"]
+ actionflag = eachzoneset["action"]
+ if removeflag:
+ if shZonesetObj.isZonesetPresent(zsetname):
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is removed from vsan "
+ + str(vsan)
+ )
+ commands_executed.append(
+ "no zoneset name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is not present in vsan "
+ + str(vsan)
+ + " ,hence there is nothing to remove"
+ )
+ else:
+ if zsetmembers is not None:
+ cmdmemlist = []
+ for eachzsmem in zsetmembers:
+ zsetmem_name = eachzsmem["name"]
+ zsetmem_removeflag = eachzsmem["remove"]
+ if zsetmem_removeflag:
+ if shZonesetObj.isZonePresentInZoneset(
+ zsetname, zsetmem_name
+ ):
+ cmd = "no member " + zsetmem_name
+ cmdmemlist.append(cmd)
+ messages.append(
+ "removing zoneset member '"
+ + zsetmem_name
+ + "' from zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zoneset member '"
+ + zsetmem_name
+ + "' is not present in zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " ,hence there is nothing to remove"
+ )
+ else:
+ if shZonesetObj.isZonePresentInZoneset(
+ zsetname, zsetmem_name
+ ):
+ messages.append(
+ "zoneset member '"
+ + zsetmem_name
+ + "' is already present in zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " ,hence there is nothing to add"
+ )
+ else:
+ cmd = "member " + zsetmem_name
+ cmdmemlist.append(cmd)
+ messages.append(
+ "adding zoneset member '"
+ + zsetmem_name
+ + "' to zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ if len(cmdmemlist) != 0:
+ commands_executed.append(
+ "zoneset name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ commands_executed = commands_executed + cmdmemlist
+ else:
+ if shZonesetObj.isZonesetPresent(zsetname):
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is already present in vsan "
+ + str(vsan)
+ )
+ else:
+ commands_executed.append(
+ "zoneset name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' is created in vsan "
+ + str(vsan)
+ )
+
+ # Process zoneset activate options
+ if actionflag == "deactivate":
+ if shZonesetActiveObj.isZonesetActive(zsetname):
+ messages.append(
+ "deactivating zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ dactcmd.append(
+ "no zoneset activate name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " is not activated, hence cannot deactivate"
+ )
+ elif actionflag == "activate":
+ if commands_executed:
+ messages.append(
+ "activating zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ )
+ actcmd.append(
+ "zoneset activate name "
+ + zsetname
+ + " vsan "
+ + str(vsan)
+ )
+ else:
+ messages.append(
+ "no changes to existing zoneset '"
+ + zsetname
+ + "' in vsan "
+ + str(vsan)
+ + " hence activate action is ignored"
+ )
+ commands_executed = commands_executed + dactcmd + actcmd
+
+ if commands_executed:
+ if op_mode == "enhanced":
+ commands_executed.append("zone commit vsan " + str(vsan))
+ elif op_mode is None:
+ if sw_mode == "enhanced":
+ commands_executed.append("zone commit vsan " + str(vsan))
+
+ if commands_executed:
+ commands_executed = (
+ ["terminal dont-ask"]
+ + commands_executed
+ + ["no terminal dont-ask"]
+ )
+
+ cmds = flatten_list(commands_executed)
+ if cmds:
+ if module.check_mode:
+ module.exit_json(
+ changed=False,
+ commands=cmds,
+ msg="Check Mode: No cmds issued to the hosts",
+ )
+ else:
+ result["changed"] = True
+ commands = commands + cmds
+ load_config(module, cmds)
+
+ result["messages"] = messages
+ result["commands"] = commands_executed
+ result["warnings"] = warnings
+ module.exit_json(**result)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/__init__.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/__init__.py
diff --git a/collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/nxos.py b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/nxos.py
new file mode 100644
index 00000000..912dfe4e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/cisco/nxos/plugins/terminal/nxos.py
@@ -0,0 +1,142 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+import json
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+from ansible.module_utils._text import to_bytes, to_text
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_stdout_re = [
+ re.compile(
+ br"[\r\n](?!\s*<)?(\x1b\S+)*[a-zA-Z_0-9]{1}[a-zA-Z0-9-_.]*[>|#](?:\s*)(\x1b\S+)*$"
+ ),
+ re.compile(br"[\r\n]?[a-zA-Z0-9]{1}[a-zA-Z0-9-_.]*\(.+\)#(?:\s*)$"),
+ ]
+
+ terminal_stderr_re = [
+ re.compile(br"% ?Error"),
+ re.compile(br"\nerror:(.*)", re.I),
+ re.compile(br"^% \w+", re.M),
+ re.compile(br"% ?Bad secret"),
+ re.compile(br"invalid input", re.I),
+ re.compile(br"(?:incomplete|ambiguous) command", re.I),
+ re.compile(br"connection timed out", re.I),
+ re.compile(br"[^\r\n]+ not found", re.I),
+ re.compile(br"'[^']' +returned error code: ?\d+"),
+ re.compile(br"syntax error"),
+ re.compile(br"unknown command"),
+ re.compile(br"user not present"),
+ re.compile(br"invalid (.+?)at '\^' marker", re.I),
+ re.compile(br"configuration not allowed .+ at '\^' marker"),
+ re.compile(
+ br"[B|b]aud rate of console should be.* (\d*) to increase [a-z]* level",
+ re.I,
+ ),
+ re.compile(
+ br"cannot apply non-existing acl policy to interface", re.I
+ ),
+ re.compile(br"Duplicate sequence number", re.I),
+ re.compile(
+ br"Cannot apply ACL to an interface that is a port-channel member",
+ re.I,
+ ),
+ ]
+
+ def on_become(self, passwd=None):
+ if self._get_prompt().strip().endswith(b"enable#"):
+ return
+
+ out = self._exec_cli_command("show privilege")
+ out = to_text(out, errors="surrogate_then_replace").strip()
+
+ # if already at privilege level 15 return
+ if "15" in out:
+ return
+
+ if self.validate_user_role():
+ return
+
+ if "Disabled" in out:
+ raise AnsibleConnectionFailure("Feature privilege is not enabled")
+
+ cmd = {u"command": u"enable"}
+ if passwd:
+ cmd[u"prompt"] = to_text(
+ r"(?i)[\r\n]?Password: $", errors="surrogate_or_strict"
+ )
+ cmd[u"answer"] = passwd
+ cmd[u"prompt_retry_check"] = True
+
+ try:
+ self._exec_cli_command(
+ to_bytes(json.dumps(cmd), errors="surrogate_or_strict")
+ )
+ prompt = self._get_prompt()
+ if prompt is None or not prompt.strip().endswith(b"enable#"):
+ raise AnsibleConnectionFailure(
+ "failed to elevate privilege to enable mode still at prompt [%s]"
+ % prompt
+ )
+ except AnsibleConnectionFailure as e:
+ prompt = self._get_prompt()
+ raise AnsibleConnectionFailure(
+ "unable to elevate privilege to enable mode, at prompt [%s] with error: %s"
+ % (prompt, e.message)
+ )
+
+ def on_unbecome(self):
+ prompt = self._get_prompt()
+ if prompt is None:
+ # if prompt is None most likely the terminal is hung up at a prompt
+ return
+
+ if b"(config" in prompt:
+ self._exec_cli_command("end")
+ self._exec_cli_command("exit")
+
+ elif prompt.endswith(b"enable#"):
+ self._exec_cli_command("exit")
+
+ def on_open_shell(self):
+ try:
+ for cmd in ("terminal length 0", "terminal width 511"):
+ self._exec_cli_command(cmd)
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure("unable to set terminal parameters")
+
+ def validate_user_role(self):
+ user = self._connection._play_context.remote_user
+
+ out = self._exec_cli_command("show user-account %s" % user)
+ out = to_text(out, errors="surrogate_then_replace").strip()
+
+ match = re.search(r"roles:(.+)$", out, re.M)
+ if match:
+ roles = match.group(1).split()
+ if "network-admin" in roles:
+ return True
+ return False