summaryrefslogtreecommitdiffstats
path: root/ansible_collections/dellemc/enterprise_sonic
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:03:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:03:42 +0000
commit66cec45960ce1d9c794e9399de15c138acb18aed (patch)
tree59cd19d69e9d56b7989b080da7c20ef1a3fe2a5a /ansible_collections/dellemc/enterprise_sonic
parentInitial commit. (diff)
downloadansible-upstream.tar.xz
ansible-upstream.zip
Adding upstream version 7.3.0+dfsg.upstream/7.3.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/dellemc/enterprise_sonic')
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml42
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/.gitignore387
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/.yamllint14
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/FILES.json4702
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/LICENSE674
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/MANIFEST.json43
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/README.md253
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml170
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst119
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/100-prefix_lists-resource-module.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/101-bgp-prefix_lists.yaml3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/102-playbook-example-updates.yaml3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/103-standard-interface-naming-and-other-bugfixes.yaml3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/105-vxlan-regression-test-fix.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/2.0.0.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/53-oc-yang-compliance.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/58-vxlan-primary-ip.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/60-bgp-regression-test_fixes.yaml4
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/62-bgp-vxlan-primary-ip.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/63-bgp_af-route_advertise_list.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/67-bgp_neighbors-auth_pwd-and-neighbor_description.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/72-bgp_neighbors-add-attributes.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/76-CLI-command-prompt-answer-fix.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/78-aaa-regression-fix.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/81-add-bgp-peer-group-attributes.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/82-static_routes.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/85-regression-test-fixes.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/88-port_breakout-and-execution-environment.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/90-aaa-and-users-bugfix.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/98-vlans-description.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/99-ntp-resource-module.yaml2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml197
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml33
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/meta/execution-environment.yml3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml53
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/clos-fabric.yaml43
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/all.yaml49
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/leaf.yaml103
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/spine.yaml87
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/inventory.yaml20
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/templates/clos_fabric_cfg.j225
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/hosts12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/interface_naming.yaml32
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/patch.txt8
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_aaa.yaml79
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_api.yaml37
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp.yaml321
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp_communities.yaml66
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_command.yaml58
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_config.yaml42
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_facts.yaml22
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_interfaces_config.yaml77
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_system.yaml18
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_vxlans_config.yaml38
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/src.txt4
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/action/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/action/sonic.py51
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py118
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/sonic.py113
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py66
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py97
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py117
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py48
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py59
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py75
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/bgp_neighbors.py249
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/bgp_neighbors_af.py114
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py53
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py56
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py71
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py81
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py67
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py82
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py89
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py57
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py71
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py83
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py79
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py64
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py80
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py62
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py54
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py66
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py73
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py236
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py598
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py848
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py304
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py368
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py371
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py1100
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py584
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py354
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py414
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py515
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py421
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py323
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py548
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py260
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py458
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py362
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py344
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py294
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py318
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py299
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py265
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py303
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py606
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py111
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py156
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py258
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py129
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py145
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py158
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py229
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py222
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py101
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py147
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py160
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py185
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py135
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py139
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py153
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py125
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/prefix_lists.py158
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py168
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py173
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py143
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py150
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py122
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py126
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py120
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py207
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py155
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py611
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py55
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py511
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py215
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_api.py158
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py390
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py414
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py224
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py301
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py288
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py1112
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py451
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_command.py235
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py329
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py136
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py230
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py296
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py375
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py238
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py516
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py360
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py228
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py423
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py328
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py267
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py214
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py297
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py210
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py241
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py204
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py245
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/terminal/__init__.py0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/plugins/terminal/sonic.py73
-rwxr-xr-xansible_collections/dellemc/enterprise_sonic/rebuild.sh22
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/requirements.txt2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/image-upgrade.yaml31
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml66
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/meta/main.yaml3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/action.facts.report.yaml10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.contains.test.facts.report.yaml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.test.facts.report.yaml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli_tasks_template.yaml14
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/idempotent.facts.report.yaml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/main.yml1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/single.run.facts.report.yaml10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template.j214
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template1.j214
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml54
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/main.yml17
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/defaults/main.yml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/invalid.yaml28
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/main.yaml10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/patch.txt8
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/preparation_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_delete.yaml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_get.yaml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_patch.yaml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_post.yaml27
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_put.yaml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml250
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/cleanup_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml28
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/preparation_tests.yaml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg18
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml324
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/cleanup_tests.yaml10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/main.yml15
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/preparation_tests.yaml20
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml78
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/main.yml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml101
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/main.yml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/tasks_template.yaml23
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml321
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/main.yml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/tasks_template.yaml23
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml316
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml880
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/action_template.yaml10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/cleanup_tests.yaml10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/main.yml19
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/preparation_tests.yaml38
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml468
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/cleanup_tests.yaml15
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/main.yml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/preparation_tests.yaml43
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/defaults/main.yaml1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/bad_operator.yaml33
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/cli_command.yaml26
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/contains.yaml29
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/invalid.yaml25
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/main.yaml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/output.yaml24
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/prompt.yaml15
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/test_local.yaml16
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/timeout.yaml30
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/defaults/main.yml83
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/backup.yaml16
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/main.yml24
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/match_template.yaml64
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/preparation_tests.yaml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/prompt.yaml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/replace_tasks_template.yaml38
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/snmp.j26
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/src.txt3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml153
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/main.yml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/preparation_tests.yaml44
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml101
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/main.yml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/preparation_tests.yaml35
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml244
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/main.yml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml28
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml89
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/main.yml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/preparation_tests.yaml22
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/tasks_template.yaml22
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml107
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml22
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/preparation_tests.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template_del.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml209
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/cleanup_tests.yaml15
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/main.yml16
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/preparation_tests.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml57
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml31
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml122
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/cleanup_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/main.yml15
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/preparation_tests.yaml7
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml114
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/cleanup_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/main.yml16
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template_del.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/templates/cli_test_case_01.cfg0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml172
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/preparation_tests.yaml11
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml47
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/cleanup_tests.yaml13
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/main.yml20
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template_del.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml91
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/cleanup_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/main.yml16
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template_del.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/templates/cli_test_case_01.cfg0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml96
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml14
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml39
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/preparation_tests.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/single_run_template.yaml19
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/templates/cli_test_case_01.cfg2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml42
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/main.yml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/preparation_tests.yaml23
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml139
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml34
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml34
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg10
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml121
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/meta/main.yaml5
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/cleanup_tests.yaml6
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/main.yml34
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/preparation_tests.yaml29
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/tasks_template.yaml21
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/templates/cli_test_case_01.cfg8
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/defaults/main.yml0
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/meta/main.yaml3
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/tasks/main.yml12
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2328
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml39
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.10.txt1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.11.txt1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.12.txt1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.13.txt1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.14.txt1
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.9.txt1
-rwxr-xr-xansible_collections/dellemc/enterprise_sonic/tests/unit/utils/run_test_cases.sh2
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_01_dict_diff_with_key_name.yaml43
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_02_dict_diff_with_key_other.yaml46
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_03_dict_diff_without_key.yaml38
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_04_dict_diff_with_similar_dict.yaml28
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_05_dict_diff_left_only.yaml29
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_06_dict_diff_left_only_with_none.yaml18
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_07_dict_diff_skeleton_only.yaml30
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_08_list_diff_with_key_name.yaml34
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_09_list_diff_with_multi_keys.yaml50
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_10_list_diff_with_key_other.yaml37
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_11_list_diff_with_similar_list.yaml31
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_12_list_diff_with_left_only.yaml40
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_13_list_diff_with_left_only_with_none.yaml48
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_14_list_diff_skeleton_only.yaml61
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_15_list_of_list_diff.yaml163
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_16_complex_list_with_dict_diff.yaml252
-rw-r--r--ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_diff_util.py87
443 files changed, 43754 insertions, 0 deletions
diff --git a/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml b/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml
new file mode 100644
index 00000000..c2ae1dd0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/.github/workflows/ansible-test.yml
@@ -0,0 +1,42 @@
+name: CI
+on:
+ # Run CI against all pushes (direct commits, also merged PRs), Pull Requests
+ push:
+ pull_request:
+ # Run CI once per day (at 06:00 UTC)
+ # This ensures that even if there haven't been commits that we are still testing against latest version of ansible-test for each ansible-base version
+ schedule:
+ - cron: '0 6 * * *'
+jobs:
+ sanity:
+ name: Sanity (${{ matrix.ansible }})
+ strategy:
+ matrix:
+ ansible:
+ - stable-2.9
+ - stable-2.10
+ - stable-2.11
+ - stable-2.12
+ - stable-2.13
+ - devel
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Check out code
+ uses: actions/checkout@v1
+ with:
+ path: ansible_collections/dellemc/enterprise_sonic
+
+ - name: Set up Python 3.9
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.9
+
+ - name: Install ansible-base (${{ matrix.ansible }})
+ run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check
+
+ - name: Install ansible_collections.ansible.netcommon
+ run: ansible-galaxy collection install ansible.netcommon -p ../../
+
+ - name: Run sanity tests
+ run: ansible-test sanity --docker -v --color
diff --git a/ansible_collections/dellemc/enterprise_sonic/.gitignore b/ansible_collections/dellemc/enterprise_sonic/.gitignore
new file mode 100644
index 00000000..c6fc14ad
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/.gitignore
@@ -0,0 +1,387 @@
+
+# Created by https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
+# Edit at https://www.gitignore.io/?templates=git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
+
+### dotenv ###
+.env
+
+### Emacs ###
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+
+# directory configuration
+.dir-locals.el
+
+# network security
+/network-security.data
+
+
+### Git ###
+# Created by git for backups. To disable backups in Git:
+# $ git config --global mergetool.keepBackup false
+*.orig
+
+# Created by git when using merge tools for conflicts
+*.BACKUP.*
+*.BASE.*
+*.LOCAL.*
+*.REMOTE.*
+*_BACKUP_*.txt
+*_BASE_*.txt
+*_LOCAL_*.txt
+*_REMOTE_*.txt
+
+#!! ERROR: jupyternotebook is undefined. Use list command to see defined gitignore types !!#
+
+### Linux ###
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### PyCharm+all ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### PyCharm+all Patch ###
+# Ignores the whole .idea folder and all .iml files
+# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
+
+.idea/
+
+# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
+
+*.iml
+modules.xml
+.idea/misc.xml
+*.ipr
+
+# Sonarlint plugin
+.idea/sonarlint
+
+### pydev ###
+.pydevproject
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# Mr Developer
+.mr.developer.cfg
+.project
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+### Vim ###
+# Swap
+[._]*.s[a-v][a-z]
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+Sessionx.vim
+
+# Temporary
+.netrwhist
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+### WebStorm ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+
+# Generated files
+
+# Sensitive or high-churn files
+
+# Gradle
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+
+# Mongo Explorer plugin
+
+# File-based project format
+
+# IntelliJ
+
+# mpeltonen/sbt-idea plugin
+
+# JIRA plugin
+
+# Cursive Clojure plugin
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+
+# Editor-based Rest Client
+
+# Android studio 3.1+ serialized cache file
+
+### WebStorm Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator/
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
diff --git a/ansible_collections/dellemc/enterprise_sonic/.yamllint b/ansible_collections/dellemc/enterprise_sonic/.yamllint
new file mode 100644
index 00000000..6f0d12b3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/.yamllint
@@ -0,0 +1,14 @@
+---
+extends: default
+
+ignore: |
+ .tox
+
+rules:
+ braces:
+ max-spaces-inside: 1
+ level: error
+ brackets:
+ max-spaces-inside: 1
+ level: error
+ line-length: disable
diff --git a/ansible_collections/dellemc/enterprise_sonic/FILES.json b/ansible_collections/dellemc/enterprise_sonic/FILES.json
new file mode 100644
index 00000000..39a1d634
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/FILES.json
@@ -0,0 +1,4702 @@
+{
+ "files": [
+ {
+ "name": ".",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/test.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "47a1db3b7c5d49f80307023f42c78c5efdf5efa0b1f507f1106b96179b64dc4c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/hosts",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "22f4ec1024a7ff0f9d471735392d71eac3378f5c7ddc7cf1e0c42a958def8e78",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "33ef239ff7b87fa17a8e41edd5cfcba01913ed7f29ae9220d1fcd294f7b231d7",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e0709445b8b0c43b7436aae58dd21ece6fc1f5c1b63346e4cf7f663382278c97",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/tasks/action_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "119fd02d958cb737e829d957a02c45b92e19dd44150723f75a9b77bdd098acb4",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d898a4d581cee3c6dfdf32cb63c0e4d0a553e7bf894ea821faca6cb6f5b533fd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5984f513be79d50fcd8ee692355ea36cde13ff8a0261bb8ec9a4d80099310514",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8d53284cc72caddd5bc84a507bb7330af983d44c35427ac7fbe623a29aa7d47f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6b8363d6259e43af603abde938ed159d1a3a083cbef49fa37095bdc779133921",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40edc63e0538af91144b80f5a699aaaa241151458d0f4a556953b240d97ef068",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf32b77246c5b9050e568364312b5b812eefbb1e4123dd527381614b7620e2f6",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d4dac61e6125e688485a484cc6e62a60ca1350ca656f448a6d3064be98c74c2b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7198a5cabb43600d45494873192676174231f617e4bd15e13ba4e8f10b6944dc",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_lag_interfaces/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5116cc9727de8d455b371ae1876ebb2e874b3bae27e31aa18e29b53aed41f535",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/test_get.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b2f3508fde7b2945a3bf1465952903d3e75c3aa05aabe36cef40c1cd4b360ff6",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/test_delete.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7bc736ed91ef8290a3c45dfe9b8d350a01996eff4b103c1d4e90852b23a80095",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/test_post.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7d11c6cb5b64a6b2a4b3edd715092a67e28597ded5da11073ff1ec47db284a6b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/test_patch.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d7c5c4dab020223f06f75446e5939d0a4c486d9884e961f564b839483f713327",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eac1646289012eda25f0d8bca465f95a23a0850f8a1d091097ccb072f4085b0c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/test_put.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "261a7ba9a567b95b01df14b7e871846c2c3225bea9c04bdd946f16f3edbb017e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/invalid.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0ee03a07e59693b9e1d817500caadabf8a4aec1e6c738d37d161510072e6cade",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a246f4c1b1644f52f5904f83b028181aef1a2cce126f245f4f5b9befa8924e55",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/tasks/patch.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4c419a4942f8ef3b29bdd307b3ac95c2e516285c7a63e7fdb02bf41937f13aa1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3bca81357e79e2b9e2ecb820a7f32d0f1da5266e69ef6f5d729d8dbb023eedb1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_api/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a9c25024f5d908d307a288172699971c215b190bda0d6092793202822eed1fe6",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "57d68154ace90d7cdf438b926ac4ff8d4197f1865019473d3078c2a48555b103",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c28676c6385f511e2875acca9d5f3ffc05418b4fc6220838fd8c7588272d93d2",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "19a1f1cb2ed8eeea3d1e48936ac12afcd8a8e3f2c50318a99cc4fb0167a26416",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf66c8d3e6ab8abfe99759ba204cf9bc33e8108248489ad13dfcbc8b6e8b1484",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_af/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9d9e4c73c7952f36475c79fbf88ed2e5866588bafe4acec029dfc50071b75e5a",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "54286b592a5d524eae86898f55b0d50583409bad0022ab7c1a136b98664ae714",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e40faf23f810bdd4ac7c98dc1c7004625abf54db17a9799898d35be0092b6a22",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e3c20b43609a7a0a867ef1f51dd5b891e6e8b891f5f07d04aada1883d1b6c96",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_interfaces/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "99b3e8c1065b4d95c39957ea69fe09994c59af516a58101ac68b931bd788a98e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a84c7d4404bbb768b0634e9321d7a083bc962efb448821d56e10703c582e8bef",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2abb0cea9c4eb807effba8647f512ff4e4fdd2cd5252b5d60fae4f6ffc9e060c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "31c383fa30c2abeca617c48be2fc75db4d060cbc9236e883367dd2ca2304e009",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/tasks/tasks_template_del.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e752456ebc7460997bb44a41ea07ebc6a3fedadbb2982d04df82456d0cb8e1f2",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f2800620b593938fecb1b8a7354c680bcc3f1fcbb59381ca25889d2424b82198",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_tacacs_server/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/test_local.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "34965a77f9bac6ffd2653154ed6557afe435b6c6d0ad6756eb7aed5628c7d6f5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/bad_operator.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3373786cbc0c28195672a291d5b1d6a2448a3bef439403a1379ccc0ea89db16e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/output.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eedd0d5419877e550dc7c78533a383b251a37355cacc754fe6f718a0e8bc17cf",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/timeout.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d800cc09a8f6d98aae25410d7238fabe1cd7a8e0785dbae6fa83a8d9096b6f54",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/contains.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2cdc7492975a756c71eb807b856492e6880b93ed4c9b39a5e05b7fc3019e371c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/cli_command.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "542bb18b9c65a5368b080c2784cc4f16ab167609dd661cf9d33588c62f90b2a5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/invalid.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "74b8fc35c0e680a8624044c863392393c8aa353d93f3a4a5c17e2b125c72e1bd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "770573da53e590ea699b0b226a5a91dcc1457b192b5759490033fb86c71ad2e0",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/tasks/prompt.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "366447b1b94627afdbcf11ad0a6e23ed74a4b6ef3f75b34ceb33d1d0645b571f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/defaults/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c5b387bd0d09356a40ee76b1d0a90106ad3766c7e8692c81cac72d55e183e31f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_command/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a5cbf2e2e7af16e77a326ef68a4978c1c765ed72d750e58f9f70ed732ef43669",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f2c19cc1d5d88c8ab54c56bac91d31ca61f168a9b2c9cc5e92701f1c0c51937",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e40faf23f810bdd4ac7c98dc1c7004625abf54db17a9799898d35be0092b6a22",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f72f840292265f4c4a9d96c0ec18aec1f73302bcd870b4b351ed319286eddd3",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_as_paths/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b7bf6d628ad50843b81ac4b8221bd23398c18a2bcbe92c98e8c7da7d24eec798",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f9acb095a9607444ec0702ae6a45672173b6cacec6e0a417bdbcfe9fd604ffb6",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c07e16779a411ed03160eec075e1e68ffafa845a1fb7ee61395279df34e0f868",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f64b5c536fe544240ce9c38f0cb6cd324c5759d627a5c290bf0347fd2740512",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vlans/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f0bff3d9b88e7d50ba7d3b8cc320b341d90fe84aedef4a841c2bfd08ea07ee8",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d5eef5b94c6fc079ac62361205017ccae7ff45227fd22ab16599376c9834c27b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d77cf2a8aac0500bc814f27970125522820fe871b8dac2779a2318a126ccdb6e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fc014f65407813d0e13ee7f4f778ee7e2f79bf26e4089c21a332a425395637ec",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "04127c5fbb729e1e955d2aeaadb9ec67d2df28afaa7cd9918a836942fbdaed9a",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_prefix_lists/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/templates/task_template.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "37b12abf0655a42fd64fdea6887fde54768b767bd7462c7012f380a31f88e9a2",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/templates/task_template1.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2f4cef7b0dc8762cbfa1d9de0ce48d24aedaf0154866fcdcb674e57d337a2d41",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/single.run.facts.report.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "51afd3cab10d587776002beedfc327d9856842d346fb3b958639c5e18a9c56c9",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/cli.contains.test.facts.report.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "091ee6abaef30f2c589fc21284a35cf328bcb7de1e363ed764efce289f1143d9",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/idempotent.facts.report.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f228e22999e5b13f192bb7e5064c46e4f8b8dcd817bccb6e1a1d4bf33368c30c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/cli.test.facts.report.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ef3d5981a08ff0adb1ca89e06e6741c7b8c284b4c1a93eca38758c2984a51047",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/cli_tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2dff2da6f27a91052f22a4f0eeacd562333b1feb240dbce16b0ba03913f029e9",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/tasks/action.facts.report.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2d51ee6515850e5d44140d451c2027d0037dc9b294fd0b5e16986f8fda94803e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f82f73da84954bb7f19f2dceb7105bb98e34f83943514a99533dcde30707c6c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/common/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bcdccb453965a51bf1740643f331108bfd38f1dabd58e303847ea0a616a2d72e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b85a04c09ea8cfe7fecc4f16df9e0b176a60912f0e10763ebe9670abf28526cd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e58c61f59e06878c38a38d9496c6ed895cd2abeda2af54f0c0443f21ca77fba1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c3adf9189fd52548ab34379294ae73202e3736016a8cfb7985be05668b0d19b1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6d975800ac27a2a441997aa994bba5a04f0fb1794d252bb282eeb8302b018547",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f86c7979a8ba1918a426d3a04d8eea9ca1d1f0bb0e9470e9be5a1a426b0e1aa4",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_ntp/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5a96aabcabc3d254e8325892587b996901af87114287fef39cdd36185e46e6af",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "09c098dc215af4b393250ae3d4c505193e70a2852d8032f2fbc62be6d74582a3",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0b4579fd763f8f7516ebebf80381c0f526b5d107994da1047ba96329973424dc",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "93bbfc9186c081b624cc9e5e1f5d891046c2e93638e08ade2bf9bca970b3b5a0",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/tasks/tasks_template_del.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9a4e4f54ba5b2c1a99608e0e4c42da660ec2e7bcb0cc1000b4510ca803410108",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5f4b8ef73168fc7cdbf0582a176198e4034c0c8301120a9e65a170c7e394bde6",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_radius_server/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "27f81e38d26fecf36640fe5ce73aaf390bb9ad9fc98e5b10149c44148c5bc444",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f63bf8a1f623c7626ccc7131205a93e63d1850f705e8b939c6fbd33edf6d0f8f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d3ac0c1d087259eefbdb96467fc90f40d9e326094623648ecd845c4945967e97",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9505adb80d90b058a0afa8e2ac8bae2e4e8f18b6d5fb8141d1a5350ed6fe7c73",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "181074d3ddf903f23c2065bbee708f2c5dfb575af70c0061685cb359b9f2cc2b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ebd7db1ebfdb114eb0280e5ff41adaffff92caa2df2f74cc7dc98a33214d36ad",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vxlan/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f16453f6d7fd07d75a5ff1909a51efdd02316b84331c30780dee2258cb462540",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "429e9498f56bd56705727f4b34b5b77c457220a51f7f2c43ff74f86b5e54eddf",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "59f11ea9543380e101f9d2d9bd11d47f0ec0225aac74b706647b7bb6babad758",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/tasks/tasks_template_del.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6dbf73cf30a5e17226e7ef3ae721b8f04642195ab7139cb66c0c95e7c8c49b43",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1a3fdc2249981d95b911544645bc81439e0bab922c06eb72d9a53457122539ca",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_mclag/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d4729194e8b87f49e0b444373c1b3b2ad2a0b50a0f0ebd894c8abaec1075d706",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/tasks/single_run_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a04afddc08a265c6589fdce4d84bf70aebe36072ef0475503e65b70af97c8137",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "aa927e1b108c8b0f9744cfc79244782d7e0cef0518db83faab3a34373ffbed04",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6ee0b650f4b18b6530df7383f4bf2168d8ee13ca49946f3f37e415dce05aff37",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/tasks/cli_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "37f946b76a42a134a1e8bd8bd337760512e44038b418be2d3943a2c05459f6f2",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "895f8900d570fdfc3c187e09c688ceefb21cd13bcd8af8b78beb2dc7b59c5a4b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "29e2bb2399e1576c889e4d1a2c915773ca57c3b0befa939be5c1f30bd422f6c1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_users/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8e05b1c592caa3ffa0aef7d93d7f1e12d42c1eb98ce4f6b5b8736ac698ebd351",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a0a4218ce9437e1a54f2edc499296591ffdf35f861f5d12eee0f53b76292032f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c4f683f060d44c641f18b803daa9e74f8d6eba27f2831b3fd56768c2679e3334",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a640983377723baab16e6b9a3fd9b5f68fd947abd94282237c8591a396fefc94",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f27630400987fa257da883e08c8fc908510f51e2dea4bbba00219d329fe3eae4",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "31e02192de52bb9e3299115d5392687e7baf808d0224e168e426bf1fd98a6048",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_vrfs/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "63fc9c716055d7d4f82e422447281cc8abb5a04246d72f2c39d9263005be9df8",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ce433fedccb55543967ac45623afcdbff87204bd9644ba9837455656014869e5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ce1db9efc87c607d309aa70caf435631ddcab6ac40ad5c7011f2e95346a021e9",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "88c9dd3878632528a559747ecfb71bcf3e0664de2a5da122e016d4ec013c610d",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/tasks/tasks_template_del.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "351f58c0c80bb45715a3bb406182187784093bc9e3611d08142cc290309d7d92",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d09922e1001ecaedc3e5326bd7f1c4e9133357ba2eba8df4c8fe8e9ffb9eb4b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_system/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/templates/src.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f34c81b433f45db921128f31abcd6b7292fe74b83b00e6afea2248d04180ae3f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/templates/snmp.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6266bb7ac15be37fb065fafa521e12a666b9804d4518a395119fa8036b0cbf29",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a007378ceca161a4eb1db9203784be6954e3f461c5fd78b888ffab8704c9fb02",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/replace_tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2e7ab0a5a93310cfbee8b1af86f2f0762fb2bef823e7db61c0a46582f0f93744",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "83bff7c7bea4a71d7f170f2d9b1986bd0c7599a93734900e8f33aae7c2c58188",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/prompt.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d6c986e2e9e1cd87f431968150a20f814a98571d907184d6d52be627ca7cab3",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e1da61c5de56ece70ce75e17370d095c770e7d38e405130f2d2630f5667e312c",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/match_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e8f50c7e79553dc6c817ad9877d33a1f81b2263d33dd2f41357e482b7aad9cd9",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/tasks/backup.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6d5b51e2b07f37b5b249f996f8a96eac0fa5d039b61878f955a713e87d0a62b5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2730670a3b92845afba70ef5f44310c2f0d2e00786e5b31cb5fc7232f10f13dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_config/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e524bd719ff9f90f29484d75dac7524b7cc1be6ee3cbb968c4e7b1a6c5794d3b",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9ec3be9a9befda131f6bb0f763bd36fb5e42fc826611408f8e9a0b9428a0b558",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "05c972841c89a97abfeffe3ea0ce4df26d90a7ea0e4b12a43e94391dc8f4a8a1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "162e1ee53ad29b43871e3319c81a8a897c3fa5e431c3adfa11225c39819c1b68",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2df5842ca274f91f4c478ebfa10e895039cffe8e44eff6eb846a0206feb04192",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_static_routes/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/templates/regression_html_report.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ba914f5e928e8d590b975bc1392def64bf9724d0a405a17e20c2a40ff297ff34",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2c2f62bacde14dae48a0123e1512b5e1b99b3f1fd4163698e67c8e3fd77b471e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/test_reports/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bcdccb453965a51bf1740643f331108bfd38f1dabd58e303847ea0a616a2d72e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3d9027febd06263adb0f67629fb7b0527fc899eb65e5f6a9f51975014f6e7cf8",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d61e40a8240f2b86ff932423f91581a859ec4dcdaa8f9cdf827fbe50220d23b5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e94c4316918dfed7d0c87b5b4571e9a12b8d788a630915ba3b284fbb91144a53",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c9d8034be99bc0d49696016feafc04a35c2e1d57c78507fe48b7a970e08b59b0",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8c74c59e8e666909f5dc072cafbfb243973612317c3a2aef51c9474ddb84c505",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_neighbors_af/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e61f468a3341e060c6f5022a306918865c7b844316956ecaca28d57a5daab261",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9cf66706c721d3eea2222ebb0aba2c6fe1b4cdba36cd8bbdbffb39882f551d51",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e40faf23f810bdd4ac7c98dc1c7004625abf54db17a9799898d35be0092b6a22",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e8ded0a1e8c9fa73380e115371f1cfc30a59fbe4520fddf1fec4c09f8317ad4d",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l3_interfaces/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "94eb56dccb6322b64d45eec3612397a2a008682177606216e39491da17cd53ee",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dc7a2c3172e30fa52832d171c90691a412bbb3c73ca1909580e99aa4a30200ee",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7d0de7954ee326560a6ae01bfe2109c8ed1ce0fcb4e62167d00f3ff642f61151",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7de7b7896a20012c48a078b7a6d98d3921017dac84e73a7f444723221879f615",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "696ae45b0e5d370c8b2330355a266ecb910da3d29073a6892ca3f756c18af4c8",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_aaa/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "af0aad24caea6100f1eaf28dd660a9deb60f86870295a92c77ba0dc0ee9b92b1",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "073811d763dcb3be1f2c0d57104df45f46beeab9bb3536bbc2e2f30aefa4ef73",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cad38a766eec3a35af5750b608e7fd59d8ab450b489b0ff9018ba4c9979c1a78",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c07e16779a411ed03160eec075e1e68ffafa845a1fb7ee61395279df34e0f868",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f9f8c4a3db4f35e73942f4a63a109181a274470176e955ab0b9a4403446f743f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_l2_interfaces/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2a37d04c718145cde6606600895ad433696a4dc3b6b310b04ab2e9f58edebf30",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dae166d577c0cd9eba123111b44403e2144b64f1b5d3cdc99019855ae47c60f4",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bace46482617b72b8d31da031c3196edd8baba912e5314d8453d073897a25189",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "af2a290eb466df24bdb922fea12f682d747f282f863e490f20c5e52b04257c87",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_ext_communities/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7d4f4ca7215bfc805c826f3d98811131a6b0d2a4edd3e7e7ebb98ecac9fbf9ac",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7fa4dc0183d6c8a679bc13428976c0398b8a87bec0d1032fc6b9d29dc2e4a72a",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ee8893139926d4625752da4613cdf0615613a69ad298a5adbf58641af1249f1e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "487f3c966b63215abc807841742b426ee59657562296a0dfbeab298bf8cd8c7f",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "547f1a09ce0a20c3f9e1ad4b189618dfc6f260b23ddd9c19d29228d08409f228",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e9ba4af225692807f8176182acc8f195d083e9be5c9efa46b6dcba30c8e21f04",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_port_breakout/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b16570e34d6f129bbec985a4ed0852051e0bb2a4007e8a9a73120f9a1d3ae876",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "96df216263dfc8ce5cd25ba67e58fd506f99536d0b72ea98f2bf08dd07a787f4",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "516ce145037ca601b5913a25a07e14e9b3a7d46692ec81bf1e38274e0ff70734",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5ba3b2a9e01ac02e139d1bd1a178e4ec3056645044c3358e8ef7ed9ecb525022",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dc406ecec44fdd088a078456ed4385c7df08875bbc1b12bc216a8ce5b88da5c5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp_communities/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "aa7d7ab0948506ca0f3bf4deba39bc70898dc04b0374a4cce76c8683b4017c53",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/tasks/cleanup_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8e2e15fed3ac641fa8ba9f1e38738208075baeea21eb1aa10b0841fb41f41e45",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/tasks/tasks_template.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b1b65fd3d5bdec60096b7d91bd758d977b2eed46c249b4df10265401e8dfe6a2",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/tasks/preparation_tests.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16512a75dbf2fc6cfa66cb442290d1547bc3642a7c045205178b96081b25c75e",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a0496cb1c3516588ee23d24020b55400241530bfa142471bcc79d25a07064f11",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ba7db7bb0225c59771f2c8809ba3de33ec974de3821518f70faea65cc24cdfd5",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/regression/roles/sonic_bgp/meta/main.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4780e46505dc61639a5541c0108b65dadd794ba515d4d102a4b5a791e5f857dd",
+ "format": 1
+ },
+ {
+ "name": "tests/regression/image-upgrade.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "84e1170d9f27767c3a0d2e7b907d96bb10d74f57f1bb2248236f6a49dc755ce7",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.13.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.12.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.14.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.10.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.9.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.11.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0fcb36eb462773c00b14c9e9f4db400f752ceed07d6ceb2a34833393f0b01134",
+ "format": 1
+ },
+ {
+ "name": "tests/unit",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_08_list_diff_with_key_name.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "53df5bef4ac87a55b8cd11579629df0e6e4ee57792a5d58f59244b66cbb92658",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_06_dict_diff_left_only_with_none.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "577edce2fe3f5b3573baca8e1598d03472d94f13022312a9889856d97f76374e",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_16_complex_list_with_dict_diff.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c2a792a7e39c57489071ab25856b424634aeac2af43667aa4fd3ab7cb13c7d40",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_14_list_diff_skeleton_only.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dcddcdb3e97ce114fa86f7f962d696168cf148edcbf0cddc8d36df9641ad379b",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_07_dict_diff_skeleton_only.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9760ac5089e77476a52ee1efbd4232f38e177b62885ee2e9631f1076272d276c",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_15_list_of_list_diff.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "98fe9982ace3f968b67242e35f064d8d9f9a25f73b63f694657b2808275990bf",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_09_list_diff_with_multi_keys.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0c884c6143486c1ee318eac464482e1b6ec8a025f6baab4c55223714d2cc1d13",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_01_dict_diff_with_key_name.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5f11925fd9829a7a35c04d3e20c65f0b11d430085ea17dc723abfae4ac4c7793",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_03_dict_diff_without_key.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3bffd27e737421b22c04880d10937e46f090a9689258c62a00795aeaf5346dec",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_12_list_diff_with_left_only.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "95311358af54019d9a50561343234568ffe3efb0bedc7a494777364bde5811cb",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/run_test_cases.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ef4f165634c7ea1bf73582720f09afb8bd36b659634d87cecb32106f489ee228",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_13_list_diff_with_left_only_with_none.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c612be85eaa3e52fd91fea15165a1dd59b099150388454c5860b88963760e84d",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_11_list_diff_with_similar_list.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9c7344834b8311f648c471bc75751cce4dbccc694d0ad3ee305fea43b48c4445",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_05_dict_diff_left_only.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9479c46d12e27f6034cb554d54d7ff56fcb87c73379a8e66812b845e9aa178e9",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_10_list_diff_with_key_other.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "85209545034d4a4cc26b1d77ee75501d604ea0f7c5184284ef8384f275e22555",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_02_dict_diff_with_key_other.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6acd991d85424f67810eef7c4be94d87645937b2914b229089372e25de8c8214",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_04_dict_diff_with_similar_dict.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cb4977c361463a2502b6d4b6a62e97ca29aafa7771fb7bb3fd78c7665ec3bab0",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/utils/test_diff_util.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d11c8e7447e4b27ec23d8b524c08edb2e57939c46231644e0999f9a542bf5fab",
+ "format": 1
+ },
+ {
+ "name": "README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e9e65f5f05a2180c6335355f0497ff6ec97bc6fe7b58a8e3bd9c3442899cbf8e",
+ "format": 1
+ },
+ {
+ "name": "changelogs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "changelogs/.plugin-cache.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0c306452265a20c55ab0327c7a69da747859d5073f753fc2be7d3eaa675348f",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/67-bgp_neighbors-auth_pwd-and-neighbor_description.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dac2cd591c6738698dee9a0e587493d0a4c61ee79d2f6fe64c60333f8ae73f88",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/88-port_breakout-and-execution-environment.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d742b197a152cc147e3bd6477a11a747deef7fd516eda9c7f3c2e4d9000fe991",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/101-bgp-prefix_lists.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f26b66c8066853c84fe32450518d9760adcce95ad683df715a833af6fea111b6",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/63-bgp_af-route_advertise_list.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40e2227d2e3667f49f14757ba091aeb1379c914eea2063f7259191cf4491f3fa",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/105-vxlan-regression-test-fix.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "87de2069e628fda750dea1586bd5e9f40276a526935e6d01f78773ac13b70c56",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/62-bgp-vxlan-primary-ip.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ad15905abe1a87c8bb5fe6c34ead669762f069a3171d17bab816e35a7046244f",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/100-prefix_lists-resource-module.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "309e213c2a3687e8dc57a65fbf7cacf2f032418766122b0192886aa1d17627ea",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/98-vlans-description.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f25fc69f892ccdc4eb886e11181fe3ab2ceaea79e5be5cfd035b0da61e4643ab",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/81-add-bgp-peer-group-attributes.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "537e421e4799a214763280bfd3a4802a5857347a77f462fba03022066ad2dc2d",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/102-playbook-example-updates.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "43c855cff9a6bd74b6f1f0bab83f700a82b3e4626319362915f44158ee5863db",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/60-bgp-regression-test_fixes.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6dbdf5dbece77482ae924fe1388c60e66438d707f880b7646fc692a3eeb25a75",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/76-CLI-command-prompt-answer-fix.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "21aebb22452bbef55d94d81657cfabaa27c42b1145d73e36329a9c44f63dbd89",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/99-ntp-resource-module.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ab72a4cbd13109b6ddc4cc36db329291c06434d8d6d5d9928d7a29d49dccffe2",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/53-oc-yang-compliance.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "656920f3c577cb86e2fd9f95a80ac4edf1713f7738513cd44fbb676576b196fd",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/90-aaa-and-users-bugfix.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "36925781ddb1e95d03ab80af6ab298cf103ef5acc7c6fe2be4cc9e3b6b03fe98",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/85-regression-test-fixes.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bae305ef61589f0d470b25fb76346f12bacf39dfb1a0ecd75448e30a289626de",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/72-bgp_neighbors-add-attributes.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "611ec7ba89300979ad81cae95fc64100fcf4d58ae917b103aca26b69c3924dfb",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/103-standard-interface-naming-and-other-bugfixes.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16cc77693ecc9ea440d549c006c6a85c6c738ef8277ffe58e0d17f2c525c1a31",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/58-vxlan-primary-ip.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ee968bb88126aaf9166905b000beca43714c32c4594e1cd5e828df00940bfb40",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/2.0.0.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fbf43f6b1763d2c3987a38732f999753bb8733d1ad3f78e06cf1515ef77497b2",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/78-aaa-regression-fix.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2a6a225719cda02382197d27da9b4ce375d10f08572dadca5c6094a4cc995b01",
+ "format": 1
+ },
+ {
+ "name": "changelogs/archive_fragments/2.0.0/82-static_routes.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b86f183d989887e885b0292789df77ed4d789d0fec1f9a97436032c82466a6f2",
+ "format": 1
+ },
+ {
+ "name": "changelogs/CHANGELOG.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5c07bc10602bb49ad903484c5c58ecd1bedf2197a6f320da391592823b891cfd",
+ "format": 1
+ },
+ {
+ "name": "changelogs/config.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "85862c6553f33777c8fd2f7fe3cfbe2d840deafc202b404052c6c829fb64495e",
+ "format": 1
+ },
+ {
+ "name": "changelogs/changelog.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2ad2a65538878b7704f2cae149e38beebaf5bc92d3ff7d245b6dbd9ef4762877",
+ "format": 1
+ },
+ {
+ "name": ".github",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".github/workflows",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".github/workflows/ansible-test.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0dc2fd66d9bb583a726caf8281f456be2130992b6f93d3155a7bc1a711b855fc",
+ "format": 1
+ },
+ {
+ "name": ".yamllint",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5f8b92f9b2c5645d9086cdb2d96378b445156e01d5ebeab075f0ac76da82e7c1",
+ "format": 1
+ },
+ {
+ "name": "playbooks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/group_vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/group_vars/leaf.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "60eef68dd51d94525aa1cea56aa6dbaae87a46a3d5fc4efe7879b3b5e79655f2",
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/group_vars/all.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0a6c90571d07e82c8a66b09d45260ced0939a233d550f69e7f68dd48dc641ac8",
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/group_vars/spine.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "44381e20e833f6a7604b513f0ffc0cc6af6f36a30b1f7a4033c5695971da6201",
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/inventory.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f2e9dd7b920b95fac532f8c8ac27bb8faf3c673c95d9195b1f0cb304c4b20339",
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/clos-fabric.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3ab1475beffd968cc128f78d5c3c1f2a6a0cfc4af57c360844c50d70e7b33949",
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/bgp_l3_fabric/templates/clos_fabric_cfg.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e43955991fc5aa4ed6e3eb8d6f754950ae86d73085dd609e00b575f438f14863",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_config.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3cc666ac7573cda9a7fb31ec0f6c9522e4b519df6e90b19708ec3d09e676ac04",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_bgp_communities.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf0f646176e380267d83130d1ddff118a8f02a5b3deb72734b0036a119d99f87",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/interface_naming.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1c50115b3792373e596572f74850d719d9ff19cc6cdf0f6487786f1dd39c8d4c",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_vxlans_config.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6ee295aca02d2b75dfc9e0f9c45ffdf00303290b5bcd8f694fb003762598830c",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_system.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "73cb798413beaf4faef806b166154564e2f519d72d1d7caecbca4c5564f8725f",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_facts.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9cf5193bd7b653e8a023070bf4f3c5cd6f1bb12d703345c36250c8a6500739ab",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/src.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "689fd13743ca803cec2985835752fb0d32b2edeacd10b987c74764da29e02a18",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_command.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0ae21487112d3a6ac66d0cd917ac689fb53d8743a8d09571e3337a8b5dc4e25b",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_interfaces_config.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c108d1f66b3f6eb47bebd4c547725d8446c61fc2b335ae4b0ae0751c532d2b63",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/hosts",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "475f5c931add33d3a98320a72d77f626fac1bcb7c1ed0dccb2e0076f82271002",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_bgp.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1203ff6b38fc784f480b0cc61c12e0f61a035ae5143f1cdc344c30f16e7ab5eb",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_api.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3881bace49d2a88accf2297d8c01675617ee1b17517322a3d837f2b002a1c4bb",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/sonic_aaa.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4ea8077dce4c37d517434b92715be316de2b23c4e3b783dd881a841a6e86eabe",
+ "format": 1
+ },
+ {
+ "name": "playbooks/common_examples/patch.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bdce7dfd3911b2d741667a57e3cb96e51a78cced38dd0a5f52a0bf37409f498a",
+ "format": 1
+ },
+ {
+ "name": "plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vxlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vxlans/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vxlans/vxlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4c0ce8d53537f8d62d93cb6eab94f613269a9abb3fd6089a251344f29ace1581",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/l3_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "175568e94084854f2f4338d76b8fe62871258d200dd422db6b77998a1b534c6b",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/l3_interfaces/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/facts.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1ce8bd508d419b7a1c80302d56ca3b23b30f0dfaaca99fb5af06b27369077f6d",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/users",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/users/users.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "aa4a5a009e2bd6b77a97c254e0f7f462fe556d08028902740e9707f8804bbd13",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/aaa",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/aaa/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/aaa/aaa.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ef4b054752c855757ba2727a9838b5aa61cf88699765c9b842eecc9727048ff5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/static_routes",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/static_routes/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/static_routes/static_routes.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78d654467abcbf68095d79cb59c29ba6470ca4e5ac426a9595b7e3f37fc931ae",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/port_breakout",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "494bdf331a5806f341f0125b6ba46ff888a935c4aff4d5b896f405764fc59a8d",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vrfs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vrfs/vrfs.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7ed445c1fa06bfb70ed56605171012c5b5fd9c13b1f0cd3fb81af6214ddc3ae0",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/interfaces/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/interfaces/interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d2eccbe6d74967f1198f8d1b5858a259a09850226ecc4d940d016be5e44b6e81",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_communities/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "41c01d93815ca5cbb62cd6ac2918041e5a67b792d11dfe291932f0f64b2ce4e1",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_ext_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_ext_communities/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6322ce72c60c1098c7b04ba2c05dc2be5c70ff1760373f131005369995efa440",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/lag_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/lag_interfaces/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "abc6c91a997dd3a42dcac331eb3d7b9b8ffc91fd2d80dc389752ef8e5d7e9d37",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/prefix_lists",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/prefix_lists/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/prefix_lists/prefix_lists.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "76cef8ee665598ede7762fb8c87be50b1b189c52818a25e233da7504f24273e5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp/bgp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "48d0915a4d169eb7accb4d921fa2956c2019e7e757fd4747bea093d2c6637b4f",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d7daff29f60266f474a5ecb39569367146eee672c8bc7b5882cedd713d7c32ef",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/ntp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/ntp/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/ntp/ntp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c8142bfefd6b7392123b7785a0c90ed43e6bf24ad4881fbd6d1df42c0c43fa27",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/system",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/system/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/system/system.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7716880b9ab1847ad3a9a4c5f70fdfd7176c33853d4ee9b4711d94bcbf51cc29",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_as_paths",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_as_paths/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7f2c4b27f8142c32eaa6219d00b261145ba5990b73ee96f2113ce3b8714b44c2",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vlans/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/vlans/vlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "571ecaf2bef5d15c91f39a0b22922282757d9b32305d4221e9967815bc195a39",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/tacacs_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f8c0dc71948a994b45b2ab55601891f26665cc538b23a01e55f8f947afc859bd",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/mclag",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/mclag/mclag.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cd027811ad981ae9b946f7247b648baccab1cbc9b877784ddc9cdb578fe68cc3",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_af/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "58292688b6f354c7e19bfc59bdcc91f76fc68cdf7ce4e0968c36ba0fde3e7334",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/l2_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "58fb25ed857a13d6af43a2609b90f09ac135694ce44997fd428242815e145b5f",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/radius_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/radius_server/radius_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a3f05a591387d9c24cb7dc066621ac75229b9fb5f871e165546daea9fa2d6a87",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors_af/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "35667a83682027b3b6051ab7729a43847de0092318b8645a4910b9b2ac003d39",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/facts",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/facts/facts.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a4e95f45024b8de1d0898d87a33fa01311168b82f0de6ec7efa012b927f3fe08",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/facts/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vxlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vxlans/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "de5a9c713994b11f97572c67af335fef9ef883c5ae28bbe8b3d29509f1479708",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/l3_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ea892c0cb638d806819a81b730c8bf272d1d22f5a785feab6bb7899d5d8290f5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/l3_interfaces/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/users",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/users/users.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e7016795dc961f47c1363b62c733419ed2f45bb51083b8c13a9cdcdc78393b80",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/aaa",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/aaa/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/aaa/aaa.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "099cb25564ace85aff3f9b665f0e8291cf30cc566393aa73fe7fd7495adb75b5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/static_routes",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/static_routes/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "664fa1bfe5e35df445395c8d97c3664d61b3616e46a1e566dfae8dffe5d3cd51",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/port_breakout",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ed14043c2d3655852bddd40ee00d07bb3731e109d738a8df15cdfe8c859dfcc3",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vrfs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ac08fa8db8afd66c4a6856700ca38615bcf8b8fba6d712981493f5452ea61c62",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/interfaces/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9858adae2a8083270cc1d513b77fce9fe93c9789c5968c8e843b843ed940bfab",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_communities/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "64e16da1a2cc3af929d265a73536e9ce637861ecd8723c0043bc7536ca9acbba",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_ext_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_ext_communities/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "880c709b98414d38453eadce51b2771ed5d6c8bdd3fb6cbde45b60191e4568f9",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/lag_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/lag_interfaces/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "980ce16a72689da30138007e45e755e9f32b020769e884b8b856505acf8d9863",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/prefix_lists",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/prefix_lists/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3047173ed7946102928cd2b23bfd7117ad4574d86f2a6330bb5a74ffaccd6368",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp/bgp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "28692ff70358779f277a4bb9f5c2dc2b9b53ab4288b2102e01ae5394c4a12cc9",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_neighbors",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_neighbors/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_neighbors/bgp_neighbors.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "94a1aaed1dd4bd1ed6965983a5a40e33f68a9af96d4610fed1ff318effa0724c",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/ntp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/ntp/ntp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "027ba50a79f0347df9702030f8849258711e516df33da4b8949b51cad88d3c86",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/system",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/system/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/system/system.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "53707e1f9e6d035be3514ea21d61a0fec4a0dd75ebeb4e44a803f63a3020be7d",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_as_paths",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_as_paths/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d9ccf45561ffa483c765a8c0ce00a2818e271c43134f9c46b3dfb3aee80f12c7",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vlans/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/vlans/vlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c0f599c09eaf915361ca18d5898de652abea87835ababe1ac726f8367a6fabd5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/tacacs_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4dcd58e947b120be713fa3f571f6c5485cab6bdfe7efc75a4e90cec626a46a71",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/mclag",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/mclag/mclag.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "df57beee6331242c7c1cced8fea388ec78291d8ce2b32c20c5e0e50738bfb9f2",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_af/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cb90a1d43f0acc3ab981e9579533505e4c23fc7d5c045b3266e13ad41b4606fa",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/l2_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3762f248b6f01066931467b74ffbe37b8d8c535fc97fe571f6d01acb1669f74c",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/radius_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "00a2640dd2eacd73a6ee61c7f00f913964e9a3072f0550682f6d4732ff119c58",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_neighbors_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/bgp_neighbors_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5c4ef73270de54df0b340f4fefb1ba3ec2b7668fd5260a8013a9efc9cead738e",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/utils/bgp_utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "efbf8c72fe520e860020fceef08d8e3a9c3aaa10fb3601999d96d604aceb09a7",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/utils/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/utils/interfaces_util.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "356468c4f1fdb40153c2b220ddb2c5441feecd4a177067aef748edc824feaa1a",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/utils/utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6b6f8897c68aaff47fd0bc497e5d99bbf71d5a78837c000f2b0d4ae719c44300",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/sonic.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2852d2baa75d3b3a9260f156c500738eb2d37ef2861a6d63e20290c93bc7c8c1",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/vxlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/vxlans/vxlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "751480b64c2188fb97cc3eabde60a7d328fc780bb82dac9dbb215ce75da58b3e",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/l3_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "79d2b8680dc3f023e536ab624ad00c3b36c2d592a22f9cde98ffa66bc95fa2c8",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/users",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/users/users.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "45344f3b1a07ad06af3e0cda474f78bebde079b4a929d27a9e99a888aa3231fc",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/aaa",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/aaa/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/aaa/aaa.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eed3f6afdc941372f55212449b300bdfc5fa0150eb5068dd98848133ae8eb7ff",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/static_routes",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/static_routes/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/static_routes/static_routes.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "146d536b99180f3f99e0c0e9c0d0b97487b2e1d6b736c85e448bb17b6dbe96f5",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/port_breakout",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1b925177c3c5e66b32066d1c16d45c73878da5e19f7f14d309aeabebe90ef8c8",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/vrfs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/vrfs/vrfs.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "121221ad6e55fffad0b00ca0a5e7cd58b35a4e7a575481dfd074c6b41411ad31",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/interfaces/interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e4289ea78fef1e900174cf355a7093f5ea144573abdf7bcab63879430d8a1d3d",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_communities/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "873dc9442732042397ac3346598dbfc8040eff5b388a6a6c5462d156ea6c999e",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_ext_communities",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_ext_communities/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a66f6fa13607ce0aae48f7ec944cc7410fd2443efa696fc59d254803402aea99",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/lag_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "42ea3ce2e2c15ec958f3072986def9bb6bbeee7e523a54e0cde7923ab7e855cb",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/prefix_lists",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/prefix_lists/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fad647ed365fd0991abcb3f70efb94c443dc8ab0fe19751690cfb5b529467763",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp/bgp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "294427bd5c538c82eca5212bcfb57967230aa64892c5e2073ad12bf889381a97",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_neighbors",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f94c033887e9c1065c0a8155bb7e6f36888e2e66c7e475d86400dc87660a6489",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/ntp",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/ntp/ntp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a19fdad43d518a26619fc9fb39a2d6ef3362cd1cc89252d74f69dc760ed815b3",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/system",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/system/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/system/system.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4a59efae25b1106a79087ecdfcb268fe2c6566ea61af795cce9d806fbe9f89e8",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_as_paths",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7fff98321324ac64feb8edc5f8042f16bd4fa145c36781d53e86978d6edd9637",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/vlans",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/vlans/vlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0f26c8eeb4cd0b4de7fd058ef2a7d1e7f728cd5d4acb0f78f19d5ea729569255",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/tacacs_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6019047cfa54495d68c7ddca3b1b39bdb90d0e8854dd7835d8a0ad31fe55226a",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/mclag",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/mclag/mclag.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8cb43549024ce183291b1dbfd8512ee25d9401a9413e631457cdc1852dbdf98e",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bd007d97137f9f87e82a2e3b3e545cdc811908ff7bf3ae142d304b0732ddc8db",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/l2_interfaces",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cbb277af89e7ebe04824f8c6c31cbfba3882f67a084861507153a2ef3c50c01c",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/radius_server",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/radius_server/radius_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "76ad9bce200ded760e69515a4694b1dbbd32347a2257f5b2841cd64644d48724",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_neighbors_af",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b34c814844873084b563d07af9334c6ee2d5cb11ae805a05c4a039c08c1d03dc",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/network/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/action",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/action/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/action/sonic.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2e65e39c5bbe05db761122a44c5c3adb7941e0ee0a4500d9c4948ade1ef7602b",
+ "format": 1
+ },
+ {
+ "name": "plugins/httpapi",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/httpapi/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/httpapi/sonic.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5b2a6e27212e4bffa10e60b4f6b72df823ad8a84445a1039f75efe5296e0941e",
+ "format": 1
+ },
+ {
+ "name": "plugins/cliconf",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/cliconf/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/cliconf/sonic.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4fbeb05a4a01583778cad49752c4fa8f3562926b4f7cbb399700aa0f59b1168b",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_command.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a6b8f8a44ea5ba6c0e6d4369a94cc35b856243595383b24a2c79d8b09dc0fa33",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_vrfs.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f65a8f6e26c21af62aca63db39f12c28226e173706e2475a336f5774c9d0fd6e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "754a4198b765e73a9bb40f297a304e8c7c94d4e83839ee07b4a5602c0b9164a7",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_tacacs_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a610ddc06cd5a8088ed2eae495f4e99f2adbbf3242aac9503b82317ee7dc1212",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_radius_server.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a64fe7ce6837f85d5ebdc5772ab8649ff4e07ee79c2ff56bf29dec4286cc260a",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_vlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0820305b7619cfc6080ec69893ea8093bef0224f7078833a9da4ee139e901257",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_aaa.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c2a2be0a9b65b42e19f099adf045b875961c5876d7ef17457828bf1c7ffd2833",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_l2_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d1ee00011322c29a0936114c42775a91932c6b46605109a0c7f53911018f3e85",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_ntp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "92cfdb9b5e31bddf0ef61f01b255e1f4754c2fe6a1ce47571202ef3155b69d51",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_mclag.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e55c254e12c77ea06035f9cdd87a4248e89f7cd9d0df068266e7226c78d5fd20",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e531d9a1f281831911dba58b4e5568bea27ae13b5a46119fd0a50c234d80a4f",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_l3_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b9c8607ca719658fa1a5f4bf20e1bf0b71f06618664b56aa3f3f771c0416d535",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_system.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "daf0edd6622a20743c5c9f51d4c4a7732389719390f512885cd3af012e65ad92",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_users.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1104943f68ff99954e8481b915fefb206af930a9c6d6edc79f43aee233ea1db5",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp_neighbors.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "417b1a3e3b325ffbef1b84db13dedef88c9e935d441d3b2f1cd8ed79eaa933bd",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "869e043591404007ed592618bcb282ed59c64c88e02e77b8fca083323bce30dd",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_port_breakout.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1896147e09a083d1fbce1900b21c29572b6dd992884947c2ffb4fb9379148e2f",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp_ext_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a0949a6e579a7eaef3d4b355ecaf328c8357c8b4d1ca7f8cf5624ed95582b81e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_lag_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "77d851e7f2915eb77407dccabfb7e9a84a02c740e3bde4067ae22ddd11932088",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp_as_paths.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e2c98cdd58166716bd27b7c658dd75849959877c0f987a1ad5f189d33762ee8e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_config.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2f596c49a79cc793a11a9f045602ae9dc226dd49b0e6ed5ebebb901814db2c3a",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_facts.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "66e57eab9be3a92fabd25d58d4b50ad4989b5ed82930b9fa26005a778d004aaa",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp_communities.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3053900903a2b5998e14d9a055cd0c907e9d4a1fcbcc76aff3efb616977ce038",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_bgp_neighbors_af.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "06e2639cc7ce2b03bd29e3909bf26525cefbd47035d9cb28927a96ccb91a16ad",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_prefix_lists.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b7226bb99d6699bf1f974bafb6659d23949b94afb80cac714c374a2ffb126ad7",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_static_routes.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d0a90d0134fdcbb81da9195eadbd91821ba400199c32c6f8750c549a6653b58a",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_interfaces.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3d602d47c8e1bc50ba18d8929117ba98da1dade7d01b4099f7ddec558eeafba0",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/sonic_vxlans.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3c10ca81fd5969960f897c93b2159f6ac6c9ecd05084272433da32ab156f176a",
+ "format": 1
+ },
+ {
+ "name": "plugins/terminal",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/terminal/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/terminal/sonic.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f2f1d5a838f3d51c0921f6d0cd260d83a81e045fba2f0d1ce0d23648b829a5d3",
+ "format": 1
+ },
+ {
+ "name": ".gitignore",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "258e4be3cfda40797fe734b375b6f94c110c9a9bebce196fedce319a457ce720",
+ "format": 1
+ },
+ {
+ "name": "requirements.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c6d28b94b5c9ff4aaf737e91c092a668381c47c05904b61b1f6f240d74b565a2",
+ "format": 1
+ },
+ {
+ "name": "LICENSE",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986",
+ "format": 1
+ },
+ {
+ "name": "rebuild.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6dd5b9c7962049017eb8e32db7111774b055979517146675d91a3b389c77bf9c",
+ "format": 1
+ },
+ {
+ "name": "meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "meta/runtime.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d6e301db0d0e6b8d8df2dac7fe249f0d21f5deadb47edb23f31fc25a87ea704f",
+ "format": 1
+ },
+ {
+ "name": "meta/execution-environment.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0e30f905b015d1d8a17d3a540cb8892b479fcbf9cb873ac2ff0665fe499f318e",
+ "format": 1
+ }
+ ],
+ "format": 1
+} \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/LICENSE b/ansible_collections/dellemc/enterprise_sonic/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program 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.
+
+ This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json b/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json
new file mode 100644
index 00000000..06fd002e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/MANIFEST.json
@@ -0,0 +1,43 @@
+{
+ "collection_info": {
+ "namespace": "dellemc",
+ "name": "enterprise_sonic",
+ "version": "2.0.0",
+ "authors": [
+ "Senthil Kumar Ganesan <Senthil_Kumar_Ganesa@Dell.com>",
+ "Abirami <Abirami_N@Dell.com>",
+ "Dhivya <Dhivya_P@Dell.com>",
+ "Mohamed Javeed <Mohamed_Javeed_Faroo@Dell.com>",
+ "Nirai Madai <Niraimadaiselvam_Mar@Dell.com>",
+ "Shade Talabi <Shade_Talabi@Dell.com>",
+ "Kerry Meyer <Kerry_Meyer@Dell.com>",
+ "Mingjun Zhang <Mingjun_Zhang@Dell.com>"
+ ],
+ "readme": "README.md",
+ "tags": [
+ "dellemc",
+ "dell",
+ "networking",
+ "nos",
+ "sonic"
+ ],
+ "description": "Ansible Network Collection for Enterprise SONiC Distribution by Dell Technologies",
+ "license": [],
+ "license_file": "LICENSE",
+ "dependencies": {
+ "ansible.netcommon": ">=2.0.0"
+ },
+ "repository": "https://github.com/ansible-collections/dellemc.enterprise_sonic",
+ "documentation": null,
+ "homepage": null,
+ "issues": "https://github.com/ansible-collections/dellemc.enterprise_sonic/issues"
+ },
+ "file_manifest_file": {
+ "name": "FILES.json",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f419780826940a762216126025170e01d39987da0d8fb273560a2488307493f5",
+ "format": 1
+ },
+ "format": 1
+} \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/README.md b/ansible_collections/dellemc/enterprise_sonic/README.md
new file mode 100644
index 00000000..4c17c9e9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/README.md
@@ -0,0 +1,253 @@
+Ansible Network Collection for Enterprise SONiC Distribution by Dell Technologies
+=================================================================================
+
+This collection includes Ansible core modules, network resource modules, and plugins needed to provision and manage Dell EMC PowerSwitch platforms running Enterprise SONiC Distribution by Dell Technologies. Sample playbooks and documentation are also included to show how the collection can be used.
+
+Supported connections
+---------------------
+The SONiC Ansible collection supports network_cli and httpapi connections.
+
+Plugins
+--------
+**CLICONF plugin**
+
+Name | Description
+--- | ---
+[network_cli](https://github.com/ansible-collections/dellemc.enterprise_sonic)|Use Ansible CLICONF to run commands on Enterprise SONiC
+
+**HTTPAPI plugin**
+
+Name | Description
+--- | ---
+[httpapi](https://github.com/ansible-collections/dellemc.enterprise_sonic)|Use Ansible HTTPAPI to run commands on Enterprise SONiC
+
+Collection core modules
+------------------------
+Name | Description | Connection type
+--- | --- | ---
+[**sonic_command**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_command_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-command-module)|Run commands through the Management Framework CLI|network_cli
+[**sonic_config**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_config_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-config-module)|Manage configuration through the Management Framework CLI|network_cli
+[**sonic_api**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_api_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-api-module)|Perform REST operations through the Management Framework REST API|httpapi
+
+Collection network resource modules
+-----------------------------------
+Listed are the SONiC Ansible network resource modules which need ***httpapi*** as the connection type. Supported operations are ***merged*** and ***deleted***.
+
+| **Interfaces** | **BGP** | **VRF** | **Users** |
+| -------------- | ------- | ------- | ------- |
+| [**sonic_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-interfaces-module)|[**sonic_bgp**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-module)| [**sonic_vrfs**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vrfs_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vrfs-module)|[**sonic_users**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_users_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-users-module)|
+| [**sonic_l2_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l2_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-l2-interfaces-module)| [**sonic_bgp_af**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_af_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-af-module)| **MCLAG** | **AAA** |
+| [**sonic_l3_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_l3_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-l3-interfaces-module) |[**sonic_bgp_as_paths**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_as_paths_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-as-paths-module)| [**sonic_mclag**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_mclag_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-mclag-module)| [**sonic_aaa**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_aaa_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-aaa-module)|
+|**Port channel**|[**sonic_bgp_communities**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_communities_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-communities-module)| **VxLANs** |[**sonic_tacacs_server**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_tacacs_server_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-tacacs-server-module)|
+|[**sonic_lag_interfaces**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_lag_interfaces_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-lag-interfaces-module)|[**sonic_bgp_ext_communities**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_ext_communities_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-ext-communities-module)| [**sonic_vxlans**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vxlans_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vxlans-module)|[**sonic_radius_server**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_radius_server_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-radius-server-module)|
+|**VLANs**|[**sonic_bgp_neighbors**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_neighbors_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-neighbors-module)| **Port breakout** | **System** |
+|[**sonic_vlans**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_vlans_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-vlans-module)|[**sonic_bgp_neighbors_af**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_bgp_neighbors_af_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-bgp-neighbors-af-module)|[**sonic_port_breakout**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_port_breakout_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-port-breakout-module) |[**sonic_system**](https://docs.ansible.com/ansible/latest/collections/dellemc/enterprise_sonic/sonic_system_module.html#ansible-collections-dellemc-enterprise-sonic-sonic-system-module) |
+
+Sample use case playbooks
+-------------------------
+The playbooks directory includes this sample playbook that show end-to-end use cases.
+
+Name | Description
+--- | ---
+[**BGP Layer 3 fabric**](https://github.com/ansible-collections/dellemc.enterprise_sonic/tree/master/playbooks/bgp_l3_fabric)|Example playbook to build a Layer 3 leaf-spine fabric
+
+Version compatibility
+----------------------
+* Recommended Ansible version 2.10 or higher
+* Enterprise SONiC Distribution by Dell Technologies version 3.1 or higher
+* Recommended Python 3.5 or higher, or Python 2.7
+* Dell Enterprise SONiC images for releases 3.1 - 3.5: Use Ansible Enterprise SONiC collection version 1.1.0 or later 1.m.n versions (from the 1.x branch of this repo)
+* Dell Enterprise SONiC images for release 4.0 and later 4.x.y releases: Use Ansible Enterprise SONiC collection version 2.0.0 or later 2.m.n releases (from the "2.x" branch of this repo).
+* In general: Dell Enterprise SONiC release versions "R.x.y" are supported by Ansible Enterprise SONiC collection versions "R-2.m.n" on branch "R-2.x".
+
+
+> **NOTE**: Community SONiC versions that include the Management Framework container should work as well, however, this collection has not been tested nor validated
+ with community versions and is not supported.
+
+Installation of Ansible 2.11+
+-----------------------------
+##### Dependencies for Ansible Enterprise SONiC collection
+
+ pip3 install paramiko>=2.7
+ pip3 install jinja2>=2.8
+ pip3 install ansible-core
+
+Installation of Ansible 2.10+
+-----------------------------
+##### Dependencies for Ansible Enterprise SONiC collection
+
+ pip3 install paramiko>=2.7
+ pip3 install jinja2>=2.8
+ pip3 install ansible-base
+
+
+Installation of Ansible 2.9
+---------------------------
+##### Dependencies for Ansible Enterprise SONiC collection
+
+ pip3 install paramiko>=2.7
+ pip3 install jinja2>=2.8
+ pip3 install ansible
+
+##### Setting Environment Variables
+
+To use the Enterprise SONiC collection in Ansible 2.9, it is required to add one of the two available environment variables.
+
+Option 1: Add the environment variable while running the playbook.
+
+
+ ANSIBLE_NETWORK_GROUP_MODULES=sonic ansible-playbook sample_playbook.yaml -i inventory.ini
+
+
+Option 2: Add the environment variable in user profile.
+
+
+ ANSIBLE_NETWORK_GROUP_MODULES=sonic
+
+
+Installation of Enterprise SONiC collection from Ansible Galaxy
+---------------------------------------------------------------
+
+Install the latest version of the Enterprise SONiC collection from Ansible Galaxy.
+
+ ansible-galaxy collection install dellemc.enterprise_sonic
+
+To install a specific version, specify a version range identifier. For example, to install the most recent version that is greater than or equal to 1.0.0 and less than 2.0.0.
+
+ ansible-galaxy collection install 'dellemc.enterprise_sonic:>=1.0.0,<2.0.0'
+
+
+Sample playbooks
+-----------------
+**VLAN configuration using CLICONF**
+
+***sonic_network_cli.yaml***
+
+ ---
+
+ - name: SONiC Management Framework CLI configuration examples
+ hosts: sonic_switches
+ gather_facts: no
+ connection: network_cli
+ tasks:
+ - name: Add VLAN entry
+ dellemc.enterprise_sonic.sonic_config:
+ commands: ['interface Vlan 700','exit']
+ save: yes
+ register: config_op
+ - name: Test SONiC single command
+ dellemc.enterprise_sonic.sonic_command:
+ commands: 'show vlan'
+ register: cmd_op
+
+**VLAN configuration using HTTPAPI**
+
+***sonic_httpapi.yaml***
+
+ ---
+
+ - name: SONiC Management Framework REST API examples
+ hosts: sonic_switches
+ gather_facts: no
+ connection: httpapi
+ tasks:
+ - name: Perform PUT operation to add a VLAN network instance
+ dellemc.enterprise_sonic.sonic_api:
+ url: data/openconfig-network-instance:network-instances/network-instance=Vlan100
+ method: "PUT"
+ body: {"openconfig-network-instance:network-instance": [{"name": "Vlan100","config": {"name": "Vlan100"}}]}
+ status_code: 204
+ - name: Perform GET operation to view VLAN network instance
+ dellemc.enterprise_sonic.sonic_api:
+ url: data/openconfig-network-instance:network-instances/network-instance=Vlan100
+ method: "GET"
+ status_code: 200
+ register: api_op
+
+**Configuration using network resource modules**
+
+***sonic_resource_modules.yaml***
+
+ ---
+
+ - name: VLANs, Layer 2 and Layer 3 interfaces configuration using Enterprise SONiC resource modules
+ hosts: sonic_switches
+ gather_facts: no
+ connection: httpapi
+ tasks:
+ - name: Configure VLANs
+ dellemc.enterprise_sonic.sonic_vlans:
+ config:
+ - vlan_id: 701
+ - vlan_id: 702
+ - vlan_id: 703
+ - vlan_id: 704
+ state: merged
+ register: sonic_vlans_output
+ - name: Configure Layer 2 interfaces
+ dellemc.enterprise_sonic.sonic_l2_interfaces:
+ config:
+ - name: Eth1/2
+ access:
+ vlan: 701
+ trunk:
+ allowed_vlans:
+ - vlan: 702
+ - vlan: 703
+ state: merged
+ register: sonic_l2_interfaces_output
+ - name: Configure Layer 3 interfaces
+ dellemc.enterprise_sonic.sonic_l3_interfaces:
+ config:
+ - name: Eth1/3
+ ipv4:
+ - address: 8.1.1.1/16
+ ipv6:
+ - address: 3333::1/16
+ state: merged
+ register: sonic_l3_interfaces_output
+
+***host_vars/sonic_sw1.yaml***
+
+ hostname: sonic_sw1
+
+ # Common parameters for connection type httpapi or network_cli:
+ ansible_user: xxxx
+ ansible_pass: xxxx
+ ansible_network_os: dellemc.enterprise_sonic.sonic
+
+ # Additional parameters for connection type httpapi:
+ ansible_httpapi_use_ssl: true
+ ansible_httpapi_validate_certs: false
+
+***inventory.ini***
+
+ [sonic_sw1]
+ sonic_sw1 ansible_host=100.104.28.119
+
+ [sonic_sw2]
+ sonic_sw2 ansible_host=100.104.28.120
+
+ [sonic_switches:children]
+ sonic_sw1
+ sonic_sw2
+
+Releasing, Versioning and Deprecation
+-------------------------------------
+
+This collection follows [Semantic Versioning](https://semver.org/). More details on versioning can be found [in the Ansible docs](https://docs.ansible.com/ansible/latest/dev_guide/developing_collections.html#collection-versions).
+
+We plan to regularly release new minor or bugfix versions once new features or bugfixes have been implemented.
+
+Enterprise SONiC Ansible Modules deprecation cycle is aligned with [Ansible](https://docs.ansible.com/ansible/latest/dev_guide/module_lifecycle.html).
+
+Source control branches on Github:
+ - Released code versions are located on "release" branches with names of the form "M.x", where "M" specifies the "major" release version for releases residing on the branch.
+ - Unreleased and pre-release code versions are located on sub-branches of the "main" branch. This is a development branch, and is not intended for use in production environments.
+
+Code of Conduct
+---------------
+
+This repository adheres to the [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html)
+
+(c) 2020-2021 Dell Inc. or its subsidiaries. All Rights Reserved.
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml
new file mode 100644
index 00000000..9d3d51d0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/.plugin-cache.yaml
@@ -0,0 +1,170 @@
+objects:
+ role: {}
+plugins:
+ become: {}
+ cache: {}
+ callback: {}
+ cliconf:
+ sonic:
+ description: Use sonic cliconf to run command on Dell OS10 platform
+ name: sonic
+ version_added: null
+ connection: {}
+ httpapi:
+ sonic:
+ description: HttpApi Plugin for devices supporting Restconf SONIC API
+ name: sonic
+ version_added: 1.0.0
+ inventory: {}
+ lookup: {}
+ module:
+ sonic_aaa:
+ description: Manage AAA and its parameters
+ name: sonic_aaa
+ namespace: ''
+ version_added: 1.1.0
+ sonic_api:
+ description: Manages REST operations on devices running Enterprise SONiC
+ name: sonic_api
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp:
+ description: Manage global BGP and its parameters
+ name: sonic_bgp
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp_af:
+ description: Manage global BGP address-family and its parameters
+ name: sonic_bgp_af
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp_as_paths:
+ description: Manage BGP autonomous system path (or as-path-list) and its parameters
+ name: sonic_bgp_as_paths
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp_communities:
+ description: Manage BGP community and its parameters
+ name: sonic_bgp_communities
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp_ext_communities:
+ description: Manage BGP extended community-list and its parameters
+ name: sonic_bgp_ext_communities
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp_neighbors:
+ description: Manage a BGP neighbor and its parameters
+ name: sonic_bgp_neighbors
+ namespace: ''
+ version_added: 1.0.0
+ sonic_bgp_neighbors_af:
+ description: Manage the BGP neighbor address-family and its parameters
+ name: sonic_bgp_neighbors_af
+ namespace: ''
+ version_added: 1.0.0
+ sonic_command:
+ description: Runs commands on devices running Enterprise SONiC
+ name: sonic_command
+ namespace: ''
+ version_added: 1.0.0
+ sonic_config:
+ description: Manages configuration sections on devices running Enterprise SONiC
+ name: sonic_config
+ namespace: ''
+ version_added: 1.0.0
+ sonic_facts:
+ description: Collects facts on devices running Enterprise SONiC
+ name: sonic_facts
+ namespace: ''
+ version_added: 1.0.0
+ sonic_interfaces:
+ description: Configure Interface attributes on interfaces such as, Eth, LAG,
+ VLAN, and loopback. (create a loopback interface if it does not exist.)
+ name: sonic_interfaces
+ namespace: ''
+ version_added: 1.0.0
+ sonic_l2_interfaces:
+ description: Configure interface-to-VLAN association that is based on access
+ or trunk mode
+ name: sonic_l2_interfaces
+ namespace: ''
+ version_added: 1.0.0
+ sonic_l3_interfaces:
+ description: Configure the IPv4 and IPv6 parameters on Interfaces such as, Eth,
+ LAG, VLAN, and loopback
+ name: sonic_l3_interfaces
+ namespace: ''
+ version_added: 1.0.0
+ sonic_lag_interfaces:
+ description: Manage link aggregation group (LAG) interface parameters
+ name: sonic_lag_interfaces
+ namespace: ''
+ version_added: 1.0.0
+ sonic_mclag:
+ description: Manage multi chassis link aggregation groups domain (MCLAG) and
+ its parameters
+ name: sonic_mclag
+ namespace: ''
+ version_added: 1.0.0
+ sonic_ntp:
+ description: Manage NTP configuration on SONiC.
+ name: sonic_ntp
+ namespace: ''
+ version_added: 2.0.0
+ sonic_port_breakout:
+ description: Configure port breakout settings on physical interfaces
+ name: sonic_port_breakout
+ namespace: ''
+ version_added: 1.0.0
+ sonic_prefix_lists:
+ description: prefix list configuration handling for SONiC
+ name: sonic_prefix_lists
+ namespace: ''
+ version_added: 2.0.0
+ sonic_radius_server:
+ description: Manage RADIUS server and its parameters
+ name: sonic_radius_server
+ namespace: ''
+ version_added: 1.0.0
+ sonic_static_routes:
+ description: Manage static routes configuration on SONiC
+ name: sonic_static_routes
+ namespace: ''
+ version_added: 2.0.0
+ sonic_system:
+ description: Configure system parameters
+ name: sonic_system
+ namespace: ''
+ version_added: 1.0.0
+ sonic_tacacs_server:
+ description: Manage TACACS server and its parameters
+ name: sonic_tacacs_server
+ namespace: ''
+ version_added: 1.1.0
+ sonic_users:
+ description: Manage users and its parameters
+ name: sonic_users
+ namespace: ''
+ version_added: 1.1.0
+ sonic_vlans:
+ description: Manage VLAN and its parameters
+ name: sonic_vlans
+ namespace: ''
+ version_added: 1.0.0
+ sonic_vrfs:
+ description: Manage VRFs and associate VRFs to interfaces such as, Eth, LAG,
+ VLAN, and loopback
+ name: sonic_vrfs
+ namespace: ''
+ version_added: 1.0.0
+ sonic_vxlans:
+ description: Manage VxLAN EVPN and its parameters
+ name: sonic_vxlans
+ namespace: ''
+ version_added: 1.0.0
+ netconf: {}
+ shell: {}
+ strategy: {}
+ vars: {}
+version: 2.0.0
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst b/ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst
new file mode 100644
index 00000000..c9a571cd
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/CHANGELOG.rst
@@ -0,0 +1,119 @@
+======================================
+Dellemc.Enterprise_Sonic Release Notes
+======================================
+
+.. contents:: Topics
+
+
+v2.0.0
+======
+
+Release Summary
+---------------
+
+This release provides Dell SONiC Enterprise Ansible Collection support for SONiC 4.x images. It is the first release for the 2.x branch of the collection. Subsequent enhancements for support of SONiC 4.x images will also be provided as needed on the 2.x branch. This release also contains bugfixes and enhancements to supplement the Ansible functionality provided previously for SONiC 3.x images. The changelog describes changes made to the modules and plugins included in this collection since release 1.1.0.
+
+
+Major Changes
+-------------
+
+- Added 'static_routes' module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/82).
+- Added a resource module for NTP support (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/99).
+- Added a resource module for support of prefix lists (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/100).
+- Updated backend REST API request formats in all applicable modules for compatibility with SONiC 4.x openconfig YANG compliant REST APIs. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/53)
+
+Minor Changes
+-------------
+
+- Added an execution-environment.yml file to the "meta" directory to enable use of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88).
+- bgp_af - Added support for BGP options to configure usage and advertisement of vxlan primary IP address related attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/62).
+- bgp_as_paths - updated module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102)
+- bgp_neighbors - Add BGP peer group support for multiple attributes. The added attributes correspond to the same set of attributes added for BGP neighbors with PR 72 (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81).
+- bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72).
+- bgp_neighbors - add an auth_pwd dictionary and nbr_description attribute to the argspec (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/67).
+- bgp_neighbors - added prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101).
+- bgp_neighbors_af - added prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101).
+- playbook - updated examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102)
+- sonic_vxlans - Add configuration capability for the primary IP address of a vxlan vtep to facilitate vxlan path redundundancy (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/58).
+- vlans - Added support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98).
+- workflow - Added stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90).
+
+Breaking Changes / Porting Guide
+--------------------------------
+
+- bgp_af - Add the route_advertise_list dictionary to the argspec to replace the deleted, obsolete advertise_prefix attribute used for SONiC 3.x images on the 1.x branch of this collection. This change corresponds to a SONiC 4.0 OC YANG REST compliance change for the BGP AF REST API. It enables specification of a route map in conjunction with each route advertisement prefix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/63).
+- bgp_af - remove the obsolete 'advertise_prefix' attribute from argspec and config code. This and subsequent co-req replacement with the new route advertise list argument structure require corresponding changes in playbooks previoulsly used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60)
+- bgp_neighbors - Replace the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for useon SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72).
+- bgp_neighbors - Replace, for BGP peer groups, the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for useon SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81).
+
+Bugfixes
+--------
+
+- Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103).
+- Fixed regression test sequencing and other regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/85).
+- aaa - Fixed a bug in facts gathering by providing required conditional branching (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90)
+- aaa - Modify regression test sequencing to enable correct testing of the functionality for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78).
+- bgp_neighbors - remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60)
+- port_breakout - Fixed a bug in formulation of port breakout REST APIs (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88).
+- sonic - Fix a bug in handling of interface names in standard interface naming mode (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103).
+- sonic_command - Fix bugs in handling of CLI commands involving a prompt and answer sequence (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/76/files).
+- users - Fixed a bug in facts gathering (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90).
+- vxlan - update Vxlan test cases to comply with SONiC behavior (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/105).
+
+New Modules
+-----------
+
+- dellemc.enterprise_sonic.sonic_ntp - Manage NTP configuration on SONiC.
+- dellemc.enterprise_sonic.sonic_prefix_lists - prefix list configuration handling for SONiC
+- dellemc.enterprise_sonic.sonic_static_routes - Manage static routes configuration on SONiC
+
+v1.1.0
+======
+
+New Modules
+-----------
+
+- dellemc.enterprise_sonic.sonic_aaa - AAA resource module.
+- dellemc.enterprise_sonic.sonic_radius_server - RADIUS resource module.
+- dellemc.enterprise_sonic.sonic_system - SYSTEM resource module.
+- dellemc.enterprise_sonic.sonic_tacacs_server - TACACS Server resource module.
+
+v1.0.0
+======
+
+New Plugins
+-----------
+
+Cliconf
+~~~~~~~
+
+- dellemc.enterprise_sonic.sonic - Use Ansible CLICONF to run commands on Enterprise SONiC.
+
+Httpapi
+~~~~~~~
+
+- dellemc.enterprise_sonic.sonic - Use Ansible HTTPAPI to run commands on Enterprise SONiC.
+
+New Modules
+-----------
+
+- dellemc.enterprise_sonic.sonic_api - Perform REST operations through the Management Framework REST API.
+- dellemc.enterprise_sonic.sonic_bgp - BGP resource module.
+- dellemc.enterprise_sonic.sonic_bgp_af - BGP AF resource module.
+- dellemc.enterprise_sonic.sonic_bgp_as_paths - BGP AS path resource module.
+- dellemc.enterprise_sonic.sonic_bgp_communities - BGP communities resource module.
+- dellemc.enterprise_sonic.sonic_bgp_ext_communities - BGP Ext communities resource module.
+- dellemc.enterprise_sonic.sonic_bgp_neighbors - BGP neighbors resource module.
+- dellemc.enterprise_sonic.sonic_bgp_neighbors_af - BGP neighbors AF resource module.
+- dellemc.enterprise_sonic.sonic_command - Run commands through Management Framework CLI.
+- dellemc.enterprise_sonic.sonic_config - Manage configuration through the Management Framework CLI.
+- dellemc.enterprise_sonic.sonic_interfaces - Interface resource module.
+- dellemc.enterprise_sonic.sonic_l2_interfaces - Layer 2 interface resource module.
+- dellemc.enterprise_sonic.sonic_l3_interfaces - Layer 3 interface resource module.
+- dellemc.enterprise_sonic.sonic_lag_interfaces - Link aggregation (LAG) resource module.
+- dellemc.enterprise_sonic.sonic_mclag - MCLAG resource module.
+- dellemc.enterprise_sonic.sonic_port_breakout - port breakout resource module.
+- dellemc.enterprise_sonic.sonic_users - USERS resource module.
+- dellemc.enterprise_sonic.sonic_vlans - VLAN resource module.
+- dellemc.enterprise_sonic.sonic_vrfs - VRF resource module.
+- dellemc.enterprise_sonic.sonic_vxlans - VxLAN EVPN resource module.
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/100-prefix_lists-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/100-prefix_lists-resource-module.yaml
new file mode 100644
index 00000000..25321ab1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/100-prefix_lists-resource-module.yaml
@@ -0,0 +1,2 @@
+major_changes:
+ - Added a resource module for support of prefix lists (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/100).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/101-bgp-prefix_lists.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/101-bgp-prefix_lists.yaml
new file mode 100644
index 00000000..7a37ef89
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/101-bgp-prefix_lists.yaml
@@ -0,0 +1,3 @@
+minor_changes:
+ - bgp_neighbors - added prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101).
+ - bgp_neighbors_af - added prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/102-playbook-example-updates.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/102-playbook-example-updates.yaml
new file mode 100644
index 00000000..a82f06e0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/102-playbook-example-updates.yaml
@@ -0,0 +1,3 @@
+minor_changes:
+ - bgp_as_paths - updated module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102)
+ - playbook - updated examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102)
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/103-standard-interface-naming-and-other-bugfixes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/103-standard-interface-naming-and-other-bugfixes.yaml
new file mode 100644
index 00000000..ef97e26b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/103-standard-interface-naming-and-other-bugfixes.yaml
@@ -0,0 +1,3 @@
+bugfixes:
+ - sonic - Fix a bug in handling of interface names in standard interface naming mode (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103).
+ - Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/105-vxlan-regression-test-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/105-vxlan-regression-test-fix.yaml
new file mode 100644
index 00000000..480f11aa
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/105-vxlan-regression-test-fix.yaml
@@ -0,0 +1,2 @@
+bugfixes:
+ - vxlan - update Vxlan test cases to comply with SONiC behavior (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/105).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/2.0.0.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/2.0.0.yaml
new file mode 100644
index 00000000..c73fafa5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/2.0.0.yaml
@@ -0,0 +1,2 @@
+release_summary: |
+ This release provides Dell SONiC Enterprise Ansible Collection support for SONiC 4.x images. It is the first release for the 2.x branch of the collection. Subsequent enhancements for support of SONiC 4.x images will also be provided as needed on the 2.x branch. This release also contains bugfixes and enhancements to supplement the Ansible functionality provided previously for SONiC 3.x images. The changelog describes changes made to the modules and plugins included in this collection since release 1.1.0.
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/53-oc-yang-compliance.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/53-oc-yang-compliance.yaml
new file mode 100644
index 00000000..fba15e6b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/53-oc-yang-compliance.yaml
@@ -0,0 +1,2 @@
+major_changes:
+- Updated backend REST API request formats in all applicable modules for compatibility with SONiC 4.x openconfig YANG compliant REST APIs. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/53)
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/58-vxlan-primary-ip.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/58-vxlan-primary-ip.yaml
new file mode 100644
index 00000000..e7bf9858
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/58-vxlan-primary-ip.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+ - sonic_vxlans - Add configuration capability for the primary IP address of a vxlan vtep to facilitate vxlan path redundundancy (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/58).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/60-bgp-regression-test_fixes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/60-bgp-regression-test_fixes.yaml
new file mode 100644
index 00000000..44482602
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/60-bgp-regression-test_fixes.yaml
@@ -0,0 +1,4 @@
+bugfixes:
+ - bgp_neighbors - remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60)
+breaking_changes:
+ - bgp_af - remove the obsolete 'advertise_prefix' attribute from argspec and config code. This and subsequent co-req replacement with the new route advertise list argument structure require corresponding changes in playbooks previoulsly used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60)
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/62-bgp-vxlan-primary-ip.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/62-bgp-vxlan-primary-ip.yaml
new file mode 100644
index 00000000..2d8b6ab9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/62-bgp-vxlan-primary-ip.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+- bgp_af - Added support for BGP options to configure usage and advertisement of vxlan primary IP address related attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/62).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/63-bgp_af-route_advertise_list.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/63-bgp_af-route_advertise_list.yaml
new file mode 100644
index 00000000..ea75c732
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/63-bgp_af-route_advertise_list.yaml
@@ -0,0 +1,2 @@
+breaking_changes:
+ - bgp_af - Add the route_advertise_list dictionary to the argspec to replace the deleted, obsolete advertise_prefix attribute used for SONiC 3.x images on the 1.x branch of this collection. This change corresponds to a SONiC 4.0 OC YANG REST compliance change for the BGP AF REST API. It enables specification of a route map in conjunction with each route advertisement prefix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/63).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/67-bgp_neighbors-auth_pwd-and-neighbor_description.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/67-bgp_neighbors-auth_pwd-and-neighbor_description.yaml
new file mode 100644
index 00000000..95e82941
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/67-bgp_neighbors-auth_pwd-and-neighbor_description.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+ - bgp_neighbors - add an auth_pwd dictionary and nbr_description attribute to the argspec (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/67).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/72-bgp_neighbors-add-attributes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/72-bgp_neighbors-add-attributes.yaml
new file mode 100644
index 00000000..c92d315b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/72-bgp_neighbors-add-attributes.yaml
@@ -0,0 +1,5 @@
+minor_changes:
+ - bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72).
+
+breaking_changes:
+ - bgp_neighbors - Replace the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for useon SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/76-CLI-command-prompt-answer-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/76-CLI-command-prompt-answer-fix.yaml
new file mode 100644
index 00000000..762759e0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/76-CLI-command-prompt-answer-fix.yaml
@@ -0,0 +1,2 @@
+bugfixes:
+ - sonic_command - Fix bugs in handling of CLI commands involving a prompt and answer sequence (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/76/files).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/78-aaa-regression-fix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/78-aaa-regression-fix.yaml
new file mode 100644
index 00000000..941e9625
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/78-aaa-regression-fix.yaml
@@ -0,0 +1,2 @@
+bugfixes:
+ - aaa - Modify regression test sequencing to enable correct testing of the functionality for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/81-add-bgp-peer-group-attributes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/81-add-bgp-peer-group-attributes.yaml
new file mode 100644
index 00000000..f47ebe53
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/81-add-bgp-peer-group-attributes.yaml
@@ -0,0 +1,5 @@
+minor_changes:
+ - bgp_neighbors - Add BGP peer group support for multiple attributes. The added attributes correspond to the same set of attributes added for BGP neighbors with PR 72 (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81).
+
+breaking_changes:
+ - bgp_neighbors - Replace, for BGP peer groups, the previously defined standalone "bfd" attribute with a bfd dictionary containing multiple attributes. This change corresponds to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images must be modified for useon SONiC 4.0 images to use the new definition for the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/82-static_routes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/82-static_routes.yaml
new file mode 100644
index 00000000..2b9ccf88
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/82-static_routes.yaml
@@ -0,0 +1,2 @@
+major_changes:
+ - Added 'static_routes' module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/82).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/85-regression-test-fixes.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/85-regression-test-fixes.yaml
new file mode 100644
index 00000000..fe54f7bc
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/85-regression-test-fixes.yaml
@@ -0,0 +1,2 @@
+bugfixes:
+ - Fixed regression test sequencing and other regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/85).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/88-port_breakout-and-execution-environment.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/88-port_breakout-and-execution-environment.yaml
new file mode 100644
index 00000000..6c5775f4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/88-port_breakout-and-execution-environment.yaml
@@ -0,0 +1,5 @@
+bugfixes:
+ - port_breakout - Fixed a bug in formulation of port breakout REST APIs (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88).
+
+minor_changes:
+ - Added an execution-environment.yml file to the "meta" directory to enable use of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/90-aaa-and-users-bugfix.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/90-aaa-and-users-bugfix.yaml
new file mode 100644
index 00000000..c61d09ad
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/90-aaa-and-users-bugfix.yaml
@@ -0,0 +1,6 @@
+bugfixes:
+ - aaa - Fixed a bug in facts gathering by providing required conditional branching (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90)
+ - users - Fixed a bug in facts gathering (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90).
+
+minor_changes:
+ - workflow - Added stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/98-vlans-description.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/98-vlans-description.yaml
new file mode 100644
index 00000000..0895b5ea
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/98-vlans-description.yaml
@@ -0,0 +1,2 @@
+minor_changes:
+ - vlans - Added support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/99-ntp-resource-module.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/99-ntp-resource-module.yaml
new file mode 100644
index 00000000..f858bf8b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/archive_fragments/2.0.0/99-ntp-resource-module.yaml
@@ -0,0 +1,2 @@
+major_changes:
+ - Added a resource module for NTP support (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/99).
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml
new file mode 100644
index 00000000..0ce34f5e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/changelog.yaml
@@ -0,0 +1,197 @@
+ancestor: null
+releases:
+ 1.0.0:
+ modules:
+ - description: Perform REST operations through the Management Framework REST API.
+ name: sonic_api
+ namespace: ''
+ - description: BGP resource module.
+ name: sonic_bgp
+ namespace: ''
+ - description: BGP AF resource module.
+ name: sonic_bgp_af
+ namespace: ''
+ - description: BGP AS path resource module.
+ name: sonic_bgp_as_paths
+ namespace: ''
+ - description: BGP communities resource module.
+ name: sonic_bgp_communities
+ namespace: ''
+ - description: BGP Ext communities resource module.
+ name: sonic_bgp_ext_communities
+ namespace: ''
+ - description: BGP neighbors resource module.
+ name: sonic_bgp_neighbors
+ namespace: ''
+ - description: BGP neighbors AF resource module.
+ name: sonic_bgp_neighbors_af
+ namespace: ''
+ - description: Run commands through Management Framework CLI.
+ name: sonic_command
+ namespace: ''
+ - description: Manage configuration through the Management Framework CLI.
+ name: sonic_config
+ namespace: ''
+ - description: Interface resource module.
+ name: sonic_interfaces
+ namespace: ''
+ - description: Layer 2 interface resource module.
+ name: sonic_l2_interfaces
+ namespace: ''
+ - description: Layer 3 interface resource module.
+ name: sonic_l3_interfaces
+ namespace: ''
+ - description: Link aggregation (LAG) resource module.
+ name: sonic_lag_interfaces
+ namespace: ''
+ - description: MCLAG resource module.
+ name: sonic_mclag
+ namespace: ''
+ - description: port breakout resource module.
+ name: sonic_port_breakout
+ namespace: ''
+ - description: USERS resource module.
+ name: sonic_users
+ namespace: ''
+ - description: VLAN resource module.
+ name: sonic_vlans
+ namespace: ''
+ - description: VRF resource module.
+ name: sonic_vrfs
+ namespace: ''
+ - description: VxLAN EVPN resource module.
+ name: sonic_vxlans
+ namespace: ''
+ plugins:
+ cliconf:
+ - description: Use Ansible CLICONF to run commands on Enterprise SONiC.
+ name: sonic
+ namespace: null
+ httpapi:
+ - description: Use Ansible HTTPAPI to run commands on Enterprise SONiC.
+ name: sonic
+ namespace: null
+ release_date: '2020-12-07'
+ 1.1.0:
+ modules:
+ - description: AAA resource module.
+ name: sonic_aaa
+ namespace: ''
+ - description: RADIUS resource module.
+ name: sonic_radius_server
+ namespace: ''
+ - description: SYSTEM resource module.
+ name: sonic_system
+ namespace: ''
+ - description: TACACS Server resource module.
+ name: sonic_tacacs_server
+ namespace: ''
+ release_date: '2021-05-28'
+ 2.0.0:
+ changes:
+ breaking_changes:
+ - bgp_af - Add the route_advertise_list dictionary to the argspec to replace
+ the deleted, obsolete advertise_prefix attribute used for SONiC 3.x images
+ on the 1.x branch of this collection. This change corresponds to a SONiC 4.0
+ OC YANG REST compliance change for the BGP AF REST API. It enables specification
+ of a route map in conjunction with each route advertisement prefix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/63).
+ - bgp_af - remove the obsolete 'advertise_prefix' attribute from argspec and
+ config code. This and subsequent co-req replacement with the new route advertise
+ list argument structure require corresponding changes in playbooks previoulsly
+ used for configuring route advertise prefixes for SONiC 3.x images. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60)
+ - bgp_neighbors - Replace the previously defined standalone "bfd" attribute
+ with a bfd dictionary containing multiple attributes. This change corresponds
+ to the revised SONiC 4.x implementation of OC YANG compatible REST APIs. Playbooks
+ previously using the bfd attributes for SONiC 3.x images must be modified
+ for useon SONiC 4.0 images to use the new definition for the bfd attribute
+ argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72).
+ - bgp_neighbors - Replace, for BGP peer groups, the previously defined standalone
+ "bfd" attribute with a bfd dictionary containing multiple attributes. This
+ change corresponds to the revised SONiC 4.x implementation of OC YANG compatible
+ REST APIs. Playbooks previously using the bfd attributes for SONiC 3.x images
+ must be modified for useon SONiC 4.0 images to use the new definition for
+ the bfd attribute argspec structure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81).
+ bugfixes:
+ - Fixed regression test bugs in multiple modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103).
+ - Fixed regression test sequencing and other regression test bugs in multiple
+ modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/85).
+ - aaa - Fixed a bug in facts gathering by providing required conditional branching
+ (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90)
+ - aaa - Modify regression test sequencing to enable correct testing of the functionality
+ for this module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/78).
+ - bgp_neighbors - remove string conversion of timer attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/60)
+ - port_breakout - Fixed a bug in formulation of port breakout REST APIs (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88).
+ - sonic - Fix a bug in handling of interface names in standard interface naming
+ mode (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/103).
+ - sonic_command - Fix bugs in handling of CLI commands involving a prompt and
+ answer sequence (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/76/files).
+ - users - Fixed a bug in facts gathering (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90).
+ - vxlan - update Vxlan test cases to comply with SONiC behavior (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/105).
+ major_changes:
+ - Added 'static_routes' module to collection (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/82).
+ - Added a resource module for NTP support (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/99).
+ - Added a resource module for support of prefix lists (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/100).
+ - Updated backend REST API request formats in all applicable modules for compatibility
+ with SONiC 4.x openconfig YANG compliant REST APIs. (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/53)
+ minor_changes:
+ - Added an execution-environment.yml file to the "meta" directory to enable
+ use of Ansible execution environment infrastructure (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/88).
+ - bgp_af - Added support for BGP options to configure usage and advertisement
+ of vxlan primary IP address related attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/62).
+ - bgp_as_paths - updated module examples with 'permit' attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102)
+ - bgp_neighbors - Add BGP peer group support for multiple attributes. The added
+ attributes correspond to the same set of attributes added for BGP neighbors
+ with PR 72 (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/81).
+ - bgp_neighbors - Add support for multiple attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/72).
+ - bgp_neighbors - add an auth_pwd dictionary and nbr_description attribute to
+ the argspec (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/67).
+ - bgp_neighbors - added prefix-list related peer-group attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101).
+ - bgp_neighbors_af - added prefix-list related neighbor attributes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/101).
+ - playbook - updated examples to reflect module changes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/102)
+ - sonic_vxlans - Add configuration capability for the primary IP address of
+ a vxlan vtep to facilitate vxlan path redundundancy (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/58).
+ - vlans - Added support for the vlan "description" attribute (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/98).
+ - workflow - Added stable-2.13 to the sanity test matrix (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/90).
+ release_summary: 'This release provides Dell SONiC Enterprise Ansible Collection
+ support for SONiC 4.x images. It is the first release for the 2.x branch of
+ the collection. Subsequent enhancements for support of SONiC 4.x images will
+ also be provided as needed on the 2.x branch. This release also contains bugfixes
+ and enhancements to supplement the Ansible functionality provided previously
+ for SONiC 3.x images. The changelog describes changes made to the modules
+ and plugins included in this collection since release 1.1.0.
+
+ '
+ fragments:
+ - 100-prefix_lists-resource-module.yaml
+ - 101-bgp-prefix_lists.yaml
+ - 102-playbook-example-updates.yaml
+ - 103-standard-interface-naming-and-other-bugfixes.yaml
+ - 105-vxlan-regression-test-fix.yaml
+ - 2.0.0.yaml
+ - 53-oc-yang-compliance.yaml
+ - 58-vxlan-primary-ip.yaml
+ - 60-bgp-regression-test_fixes.yaml
+ - 62-bgp-vxlan-primary-ip.yaml
+ - 63-bgp_af-route_advertise_list.yaml
+ - 67-bgp_neighbors-auth_pwd-and-neighbor_description.yaml
+ - 72-bgp_neighbors-add-attributes.yaml
+ - 76-CLI-command-prompt-answer-fix.yaml
+ - 78-aaa-regression-fix.yaml
+ - 81-add-bgp-peer-group-attributes.yaml
+ - 82-static_routes.yaml
+ - 85-regression-test-fixes.yaml
+ - 88-port_breakout-and-execution-environment.yaml
+ - 90-aaa-and-users-bugfix.yaml
+ - 98-vlans-description.yaml
+ - 99-ntp-resource-module.yaml
+ modules:
+ - description: Manage NTP configuration on SONiC.
+ name: sonic_ntp
+ namespace: ''
+ - description: prefix list configuration handling for SONiC
+ name: sonic_prefix_lists
+ namespace: ''
+ - description: Manage static routes configuration on SONiC
+ name: sonic_static_routes
+ namespace: ''
+ release_date: '2022-09-02'
diff --git a/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml b/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml
new file mode 100644
index 00000000..b857f936
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/changelogs/config.yaml
@@ -0,0 +1,33 @@
+changelog_filename_template: CHANGELOG.rst
+changelog_filename_version_depth: 0
+changes_file: changelog.yaml
+changes_format: combined
+ignore_other_fragment_extensions: true
+keep_fragments: false
+archive_path_template: changelogs/archive_fragments/{version}
+mention_ancestor: true
+new_plugins_after_name: removed_features
+notesdir: fragments
+prelude_section_name: release_summary
+prelude_section_title: Release Summary
+sanitize_changelog: true
+sections:
+- - major_changes
+ - Major Changes
+- - minor_changes
+ - Minor Changes
+- - breaking_changes
+ - Breaking Changes / Porting Guide
+- - deprecated_features
+ - Deprecated Features
+- - removed_features
+ - Removed Features (previously deprecated)
+- - security_fixes
+ - Security Fixes
+- - bugfixes
+ - Bugfixes
+- - known_issues
+ - Known Issues
+title: Dellemc.Enterprise_Sonic
+trivial_section_name: trivial
+use_fqcn: true
diff --git a/ansible_collections/dellemc/enterprise_sonic/meta/execution-environment.yml b/ansible_collections/dellemc/enterprise_sonic/meta/execution-environment.yml
new file mode 100644
index 00000000..283dbe33
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/meta/execution-environment.yml
@@ -0,0 +1,3 @@
+dependencies:
+ python: requirements.txt
+version: 1
diff --git a/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml b/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml
new file mode 100644
index 00000000..d4f511c1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/meta/runtime.yml
@@ -0,0 +1,53 @@
+requires_ansible: '>=2.9.10'
+plugin_routing:
+ action:
+ sonic_config:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_command:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_api:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_facts:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_interfaces:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_lag_interfaces:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_l2_interfaces:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_l3_interfaces:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_vlans:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp_af:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp_neighbors:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp_neighbors_af:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp_as_paths:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp_communities:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_bgp_ext_communities:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_vxlans:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_mclag:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_users:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_port_breakout:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_vrfs:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_tacacs_server:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_radius_server:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_aaa:
+ redirect: dellemc.enterprise_sonic.sonic
+ sonic_system:
+ redirect: dellemc.enterprise_sonic.sonic
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/clos-fabric.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/clos-fabric.yaml
new file mode 100644
index 00000000..27e20c08
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/clos-fabric.yaml
@@ -0,0 +1,43 @@
+---
+- hosts: datacenter
+ connection: httpapi
+ gather_facts: no
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: Create Interfaces
+ sonic_interfaces:
+ config: "{{sonic_interfaces}}"
+ when: sonic_interfaces is defined and sonic_interfaces
+
+ - name: Create L3 Interfaces
+ sonic_l3_interfaces:
+ config: "{{sonic_l3_interfaces}}"
+ when: sonic_l3_interfaces is defined and sonic_l3_interfaces
+
+ - name: "Push configs for clos-fabric usecase"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ src: clos_fabric_cfg.j2
+ register: result
+
+ - name: Create BGP configuration
+ sonic_bgp:
+ config: "{{sonic_bgp}}"
+ when: sonic_bgp is defined and sonic_bgp
+
+ - name: Create BGP AF configuration
+ sonic_bgp_af:
+ config: "{{sonic_bgp_af}}"
+ when: sonic_bgp_af is defined and sonic_bgp_af
+
+ - name: Create BGP Neighbors configuration
+ sonic_bgp_neighbors:
+ config: "{{sonic_bgp_neighbors}}"
+ when: sonic_bgp_neighbors is defined and sonic_bgp_neighbors
+
+ - name: Create BGP Neighbors AF configuration
+ sonic_bgp_neighbors_af:
+ config: "{{sonic_bgp_neighbors_af}}"
+ when: sonic_bgp_neighbors_af is defined and sonic_bgp_neighbors_af
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/all.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/all.yaml
new file mode 100644
index 00000000..526f29e2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/all.yaml
@@ -0,0 +1,49 @@
+ansible_user: admin
+ansible_password: admin
+ansible_network_os: dellemc.enterprise_sonic.sonic
+
+# Changing the interface name here will be reflected in all places
+# Spine to Leaf interface connections
+spine_leaf1_inf1: Eth1/1
+spine_leaf2_inf2: Eth1/2
+spine_leaf25_inf3: Eth1/25
+spine_leaf26_inf4: Eth1/26
+
+# Leaf to spine interface connections
+leaf_spine1_inf1: Eth1/14
+leaf_spine2_inf2: Eth1/15
+
+# Logical interface defintions
+loopback_inf0: Loopback0
+loopback_inf1: Loopback1
+
+host_data:
+ leaf1:
+ loopback0_ip: 10.0.2.1/32
+ loopback1_ip: 10.10.10.1/32
+ bgp_asn: 65001
+ leaf2:
+ loopback0_ip: 10.0.2.2/32
+ loopback1_ip: 10.10.10.2/32
+ bgp_asn: 65001
+ leaf25:
+ loopback0_ip: 10.0.2.25/32
+ loopback1_ip: 10.10.10.25/32
+ bgp_asn: 65025
+ leaf26:
+ loopback0_ip: 10.0.2.26/32
+ loopback1_ip: 10.10.10.26/32
+ bgp_asn: 65025
+ spine1:
+ loopback0_ip: 10.0.1.1/32
+ bgp_asn: 65000
+ spine2:
+ loopback0_ip: 10.0.1.2/32
+ bgp_asn: 65000
+
+ansible_httpapi_use_ssl: true
+ansible_httpapi_validate_certs: false
+
+loopback0_ip: "{{ host_data[inventory_hostname].loopback0_ip }}"
+loopback1_ip: "{{host_data[inventory_hostname].loopback1_ip}}"
+bgp_asn: "{{ host_data[inventory_hostname].bgp_asn }}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/leaf.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/leaf.yaml
new file mode 100644
index 00000000..ad524d42
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/leaf.yaml
@@ -0,0 +1,103 @@
+sonic_interfaces:
+ - name: "{{ leaf_spine1_inf1 }}"
+ description: "{{ leaf_spine1_inf1 }} Spine1"
+ enabled: true
+ - name: "{{ leaf_spine2_inf2 }}"
+ description: "{{ leaf_spine2_inf2 }} Spine2"
+ enabled: true
+ - name: "{{ loopback_inf0 }}"
+ - name: "{{ loopback_inf1 }}"
+
+sonic_l3_interfaces:
+ - name: "{{ loopback_inf0 }}"
+ ipv4:
+ addresses:
+ - address: "{{ loopback0_ip }}"
+ - name: "{{ loopback_inf1 }}"
+ ipv4:
+ addresses:
+ - address: "{{ loopback1_ip }}"
+ - name: "{{ leaf_spine1_inf1 }}"
+ ipv6:
+ enabled: true
+ - name: "{{ leaf_spine2_inf2 }}"
+ ipv6:
+ enabled: true
+
+sonic_route_maps_cli:
+ - name: REDISTU
+ permit: true
+ seq: 10
+ match_ip_prefix_list: UNDERLAY
+ - name: REDISTL3
+ permit: true
+ seq: 10
+ match_ip_prefix_list: L3OVERLAY
+
+sonic_prefix_list_cli:
+ - name: UNDERLAY
+ permit: true
+ entries:
+ - ip: 10.0.2.0/24
+ condition: ge 32 le 32
+ seq: 1
+ - ip: 10.10.10.0/24
+ condition: ge 32 le 32
+ seq: 2
+ - name: L3OVERLAY
+ permit: true
+ entries:
+ - ip: 192.168.50.0/24
+ seq: 3
+
+sonic_bgp:
+ - bgp_as: "{{ bgp_asn }}"
+ router_id: "{{ loopback0_ip.split('/')[0] }}"
+ bestpath:
+ as_path:
+ multipath_relax: true
+
+sonic_bgp_af:
+ - bgp_as: "{{ bgp_asn }}"
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ advertise_all_vni: true
+ - afi: ipv4
+ safi: unicast
+ redistribute:
+ - protocol: connected
+ route_map: REDISTU
+
+sonic_bgp_neighbors:
+ - bgp_as: "{{ bgp_asn }}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_type: external
+ advertisement_interval: 5
+ timers:
+ keepalive: 3
+ holdtime: 9
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile 1'
+ capability:
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ activate: true
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ allowas_in:
+ value: 2
+ neighbors:
+ - neighbor: "{{ leaf_spine1_inf1 }}"
+ peer_group: SPINE
+ - neighbor: "{{ leaf_spine2_inf2 }}"
+ peer_group: SPINE
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/spine.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/spine.yaml
new file mode 100644
index 00000000..e3116407
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/group_vars/spine.yaml
@@ -0,0 +1,87 @@
+loopback_inf0: Loopback0
+
+sonic_interfaces:
+ - name: "{{ spine_leaf1_inf1 }}"
+ description: "{{ spine_leaf1_inf1 }} Leaf1"
+ enabled: true
+ - name: "{{ spine_leaf2_inf2 }}"
+ description: "{{ spine_leaf2_inf2 }} Leaf2"
+ enabled: true
+ - name: "{{ spine_leaf25_inf3 }}"
+ description: "{{ spine_leaf25_inf3 }} Leaf25"
+ enabled: true
+ - name: "{{ spine_leaf26_inf4 }}"
+ description: "{{ spine_leaf26_inf4 }} Leaf26"
+ enabled: true
+ - name: "{{ loopback_inf0 }}"
+
+sonic_l3_interfaces:
+ - name: "{{ loopback_inf0 }}"
+ ipv4:
+ addresses:
+ - address: "{{ loopback0_ip }}"
+ - name: "{{ spine_leaf1_inf1 }}"
+ ipv6:
+ enabled: true
+ - name: "{{ spine_leaf2_inf2 }}"
+ ipv6:
+ enabled: true
+ - name: "{{ spine_leaf25_inf3 }}"
+ ipv6:
+ enabled: true
+ - name: "{{ spine_leaf26_inf4 }}"
+ ipv6:
+ enabled: true
+
+sonic_bgp:
+ - bgp_as: "{{ bgp_asn }}"
+ router_id: "{{ loopback0_ip.split('/')[0] }}"
+ bestpath:
+ as_path:
+ multipath_relax: true
+
+sonic_bgp_af:
+ - bgp_as: "{{ bgp_asn }}"
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ advertise_all_vni: true
+ - afi: ipv4
+ safi: unicast
+ redistribute:
+ - protocol: connected
+
+sonic_bgp_neighbors:
+ - bgp_as: "{{ bgp_asn }}"
+ peer_group:
+ - name: LEAF
+ remote_as:
+ peer_type: external
+ advertisement_interval: 5
+ timers:
+ keepalive: 3
+ holdtime: 9
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile1'
+ capability:
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ activate: true
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ neighbors:
+ - neighbor: "{{ spine_leaf1_inf1 }}"
+ peer_group: LEAF
+ - neighbor: "{{ spine_leaf2_inf2 }}"
+ peer_group: LEAF
+ - neighbor: "{{ spine_leaf25_inf3 }}"
+ peer_group: LEAF
+ - neighbor: "{{ spine_leaf26_inf4 }}"
+ peer_group: LEAF
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/inventory.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/inventory.yaml
new file mode 100644
index 00000000..26845da3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/inventory.yaml
@@ -0,0 +1,20 @@
+spine1 ansible_host=100.94.81.19
+spine2 ansible_host=100.10.10.2
+leaf1 ansible_host=100.94.81.17
+leaf2 ansible_host=100.10.10.4
+leaf25 ansible_host=100.10.10.5
+leaf26 ansible_host=100.10.10.6
+
+[leaf]
+leaf1
+leaf2
+leaf25
+leaf26
+
+[spine]
+spine1
+spine2
+
+[datacenter:children]
+leaf
+spine
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/templates/clos_fabric_cfg.j2 b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/templates/clos_fabric_cfg.j2
new file mode 100644
index 00000000..cbb4732f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/bgp_l3_fabric/templates/clos_fabric_cfg.j2
@@ -0,0 +1,25 @@
+#jinja2: trim_blocks: True, lstrip_blocks: True
+{#######################################
+
+#Purpose:
+BGP L3 Fabric usecase configuration for SONiC devices
+
+#######################################}
+{# prefix list start #}
+{% if sonic_prefix_list_cli is defined and sonic_prefix_list_cli %}
+ {% for sonic_prefix in sonic_prefix_list_cli %}
+ {% for prefix_entires in sonic_prefix.entries %}
+ip prefix-list {{ sonic_prefix.name }} seq {{ prefix_entires.seq }} {{ 'permit' if sonic_prefix.permit else 'deny' }} {{ prefix_entires.ip }} {{ prefix_entires.condition if prefix_entires.condition is defined else ''}}
+ {% endfor %}
+ {% endfor %}
+{% endif %}
+{# prefix list end #}
+
+{# Route map configuration start #}
+{% if sonic_route_maps_cli is defined and sonic_route_maps_cli %}
+ {% for sonic_route_map in sonic_route_maps_cli %}
+route-map {{ sonic_route_map.name }} {{ 'permit' if sonic_route_map.permit else 'deny' }} {{ sonic_route_map.seq }}
+ match ip address prefix-list {{ sonic_route_map.match_ip_prefix_list }}
+ {% endfor %}
+{% endif %}
+{# Route map configuration start #}
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/hosts b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/hosts
new file mode 100644
index 00000000..b1214247
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/hosts
@@ -0,0 +1,12 @@
+sonic1 ansible_host=100.94.81.17 ansible_user=admin ansible_password=admin
+sonic2 ansible_host=100.94.81.19 ansible_user=admin ansible_password=admin
+
+[datacenter]
+sonic1
+sonic2
+
+[datacenter:vars]
+ansible_network_os=dellemc.enterprise_sonic.sonic
+ansible_python_interpreter=/usr/bin/python3
+ansible_httpapi_use_ssl=true
+ansible_httpapi_validate_certs=false
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/interface_naming.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/interface_naming.yaml
new file mode 100644
index 00000000..36901572
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/interface_naming.yaml
@@ -0,0 +1,32 @@
+---
+- hosts: datacenter
+ connection: network_cli
+ gather_facts: no
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: "Configure description for interface in native name"
+ sonic_config:
+ commands:
+ - description Ethernet0
+ parents:
+ - interface Ethernet0
+
+ # Same prompt and answer for cli "no interface-naming standard"
+ - name: "Configure cli with prompts on SONiC device"
+ sonic_config:
+ commands:
+ - command: "interface-naming standard"
+ prompt: "Broadcast message: Interface naming mode has changed. Users running 'sonic-cli' are required to restart your session."
+ answer: "\n"
+
+ - name: "reset ssh connection"
+ meta: reset_connection
+
+ - name: "Configure description for interface in standard name"
+ sonic_config:
+ commands:
+ - description Eth1/1
+ parents:
+ - interface Eth1/1
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/patch.txt b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/patch.txt
new file mode 100644
index 00000000..94a5a760
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/patch.txt
@@ -0,0 +1,8 @@
+{"openconfig-if-ip:ipv4": {
+ "addresses": {
+ "address": [
+ {
+ "ip": "131.1.1.1",
+ "config": {"ip": "131.1.1.1", "prefix-length": 24}
+ }]}}
+ }
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_aaa.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_aaa.yaml
new file mode 100644
index 00000000..7d968b83
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_aaa.yaml
@@ -0,0 +1,79 @@
+---
+- name: Ansible resource module example for sonic_aaa
+ hosts: datacenter
+ gather_facts: False
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: delete all users
+ sonic_users:
+ config:
+ state: deleted
+ - name: delete all aaa
+ sonic_aaa:
+ config:
+ state: deleted
+ - name: delete all tacacs servers
+ sonic_tacacs_server:
+ config:
+ state: deleted
+ - name: delete all radius servers
+ sonic_radius_server:
+ config:
+ state: deleted
+ - name: Merge users configurations
+ sonic_users:
+ config:
+ - name: sysadmin
+ role: admin
+ password: admin
+ update_password: always
+ - name: sysoperator
+ role: operator
+ password: operator
+ update_password: always
+ state: merged
+ - name: Test SONiC aaa
+ sonic_aaa:
+ config:
+ authentication:
+ data:
+ fail_through: true
+ group: tacacs+
+ local: true
+ state: merged
+ - name: Merge tacacs configurations
+ sonic_tacacs_server:
+ config:
+ auth_type: pap
+ key: pap
+ source_interface: Eth 1/2
+ timeout: 10
+ servers:
+ host:
+ - name: 1.2.3.4
+ auth_type: pap
+ key: 1234
+ state: merged
+ - name: Merge radius configurations
+ sonic_radius_server:
+ config:
+ auth_type: chap
+ key: chap
+ nas_ip: 1.2.3.4
+ statistics: true
+ timeout: 10
+ retransmit: 3
+ servers:
+ host:
+ - name: localhost
+ auth_type: mschapv2
+ key: local
+ priority: 2
+ port: 52
+ retransmit: 2
+ timeout: 20
+ source_interface: Eth 1/2
+ vrf: mgmt
+ state: merged
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_api.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_api.yaml
new file mode 100644
index 00000000..4c1c07cf
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_api.yaml
@@ -0,0 +1,37 @@
+---
+- name: Ansible resource module example
+ hosts: datacenter
+ gather_facts: False
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: "Test patch_api"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Eth1%2f26/config/description
+ method: "PATCH"
+ status_code: 204
+ body: {"openconfig-interfaces:description": "hi "}
+ - name: "Test patch_api"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Eth1%2f26/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/
+ method: "PATCH"
+ status_code: 204
+ body: "{{ lookup('file','patch.txt') }}"
+ - name: "Test put_api"
+ sonic_api:
+ url: data/openconfig-network-instance:network-instances/network-instance=Vlan100
+ method: "PUT"
+ body: {"openconfig-network-instance:network-instance": [{"name": "Vlan100", "config": {"name": "Vlan100"}}]}
+ status_code: 204
+ - name: "Test get_api"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Eth1%2f26
+ method: "GET"
+ status_code: 200
+ - name: "Test delete_api"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Eth1%2f26/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses/address=131.1.1.1/config/prefix-length
+ method: "DELETE"
+ status_code: 204
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp.yaml
new file mode 100644
index 00000000..c3784f98
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp.yaml
@@ -0,0 +1,321 @@
+---
+- name: Ansible Enterprise SONiC BGP resource module example
+ hosts: datacenter
+ gather_facts: False
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ vars:
+ bgp_as1: 4
+ bgp_as2: 10
+ bgp_as3: 11
+ vrf1: VrfCheck1
+ tasks:
+ - name: "Configure route map configurations"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands:
+ - route-map aa permit 11
+ - route-map bb permit 22
+ - ip prefix-list p1 seq 1 permit 1.1.1.0/24
+ - ip prefix-list p2 seq 2 permit 2.2.2.128/25
+ save: yes
+ - name: Configure VRFs
+ sonic_vrfs:
+ config:
+ - name: "{{vrf1}}"
+ - name: "Test sonic_bgp"
+ sonic_bgp:
+ config:
+ - bgp_as: "{{bgp_as1}}"
+ router_id: 10.2.2.4
+ log_neighbor_changes: False
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: False
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ max_med:
+ on_startup:
+ timer: 667
+ med_val: 7878
+ - bgp_as: "{{bgp_as2}}"
+ log_neighbor_changes: True
+ vrf_name: "{{vrf1}}"
+ bestpath:
+ as_path:
+ confed: False
+ ignore: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ max_med:
+ on_startup:
+ timer: 332
+ med_val: 9987
+ state: merged
+ - name: "Test sonic_bgp_af merge 01"
+ sonic_bgp_af:
+ config:
+ - bgp_as: "{{bgp_as1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ advertise_all_vni: false
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: aa
+ - metric: "26"
+ protocol: ospf
+ route_map: bb
+ - metric: "25"
+ protocol: static
+ route_map: aa
+ - afi: ipv6
+ safi: unicast
+ advertise_all_vni: true
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: bb
+ - metric: "27"
+ protocol: ospf
+ route_map: aa
+ - metric: "28"
+ protocol: static
+ route_map: aa
+ - afi: l2vpn
+ safi: evpn
+ advertise_all_vni: True
+ advertise_default_gw: True
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: aa
+ - advertise_afi: ipv6
+ route_map: bb
+ - bgp_as: "{{bgp_as2}}"
+ vrf_name: "{{vrf1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: aa
+ - metric: "26"
+ protocol: ospf
+ route_map: bb
+ - metric: "25"
+ protocol: static
+ route_map: aa
+ - afi: ipv6
+ safi: unicast
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: bb
+ - metric: "27"
+ protocol: ospf
+ route_map: aa
+ - metric: "28"
+ protocol: static
+ route_map: aa
+ - afi: l2vpn
+ safi: evpn
+ advertise_default_gw: True
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: aa
+ - advertise_afi: ipv6
+ route_map: bb
+ state: merged
+ - name: "Test sonic_bgp_neighbors merged state"
+ sonic_bgp_neighbors:
+ config:
+ - bgp_as: "{{bgp_as1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_type: internal
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile 1'
+ advertisement_interval: 15
+ timers:
+ keepalive: 50
+ holdtime: 40
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: aa
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 200
+ prevent_teardown: false
+ warning_threshold: 88
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - name: SPINE2
+ neighbors:
+ - neighbor: Eth1/1
+ remote_as:
+ peer_type: internal
+ peer_group: SPINE
+ advertisement_interval: 10
+ timers:
+ keepalive: 40
+ holdtime: 50
+ bfd:
+ enabled: false
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ - neighbor: 192.168.1.4
+ - neighbor: Eth1/2
+ remote_as:
+ peer_as: 3
+ peer_group: SPINE2
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as2}}"
+ vrf_name: "{{vrf1}}"
+ peer_group:
+ - name: SPINE3
+ remote_as:
+ peer_type: internal
+ bfd:
+ check_failure: false
+ enabled: true
+ profile: 'profile 3'
+ advertisement_interval: 15
+ timers:
+ keepalive: 50
+ holdtime: 40
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ neighbors:
+ - neighbor: Eth1/11
+ peer_group: SPINE3
+ - neighbor: Eth1/12
+ state: merged
+ - name: "Test sonic_bgp_neighbors_af merge 01"
+ sonic_bgp_neighbors_af:
+ config:
+ - bgp_as: "{{bgp_as1}}"
+ neighbors:
+ - neighbor: Eth1/1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ allowas_in:
+ value: 6
+ route_map:
+ - name: aa
+ direction: out
+ - name: bb
+ direction: in
+ route_reflector_client: false
+ route_server_client: true
+ - neighbor: Eth1/2
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ allowas_in:
+ origin: true
+ route_map:
+ - name: aa
+ direction: out
+ - name: bb
+ direction: in
+ route_reflector_client: true
+ route_server_client: true
+ ip_afi:
+ default_policy_name: bb
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 100
+ prevent_teardown: true
+ warning_threshold: 80
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - bgp_as: "{{bgp_as2}}"
+ vrf_name: "{{vrf1}}"
+ neighbors:
+ - neighbor: Eth1/11
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ allowas_in:
+ value: 6
+ route_map:
+ - name: aa
+ direction: out
+ - name: bb
+ direction: in
+ route_reflector_client: false
+ route_server_client: true
+ - afi: ipv6
+ safi: unicast
+ activate: true
+ allowas_in:
+ value: 6
+ route_map:
+ - name: aa
+ direction: out
+ - name: bb
+ direction: in
+ route_reflector_client: false
+ route_server_client: true
+ - neighbor: Eth1/12
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ allowas_in:
+ origin: true
+ route_map:
+ - name: aa
+ direction: out
+ - name: bb
+ direction: in
+ route_reflector_client: true
+ route_server_client: true
+ - afi: ipv6
+ safi: unicast
+ activate: true
+ allowas_in:
+ value: 2
+ route_map:
+ - name: aa
+ direction: out
+ - name: bb
+ direction: in
+ route_reflector_client: true
+ route_server_client: true
+ state: merged
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp_communities.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp_communities.yaml
new file mode 100644
index 00000000..1cc31e75
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_bgp_communities.yaml
@@ -0,0 +1,66 @@
+---
+- name: "Test Enterprise SONiC BGP Community configurations"
+ hosts: datacenter
+ gather_facts: no
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: Add as_path_list configuration
+ sonic_bgp_as_paths:
+ config:
+ - name: test
+ members:
+ - "11"
+ - "22"
+ - "33"
+ permit: true
+ - name: test_1
+ members:
+ - "101.101"
+ - "201.201"
+ - "301.301"
+ permit: false
+ state: merged
+ - name: Add bgp_community configuration
+ sonic_bgp_communities:
+ config:
+ - name: test
+ type: expanded
+ permit: true
+ members:
+ regex:
+ - "11"
+ - "22"
+ - "33"
+ - name: test_1
+ type: expanded
+ permit: true
+ members:
+ regex:
+ - "101.101"
+ - "201.201"
+ - "301.301"
+ state: merged
+ - name: Add bgp_extcommunity configuration
+ sonic_bgp_ext_communities:
+ config:
+ - name: exp1
+ type: expanded
+ permit: true
+ members:
+ regex:
+ - "11"
+ - "22"
+ - "33"
+ - name: std1
+ type: standard
+ permit: true
+ members:
+ route_target:
+ - "101.101"
+ - "201.201"
+ route_origin:
+ - "301.301"
+ - "401.401"
+ state: merged
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_command.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_command.yaml
new file mode 100644
index 00000000..ca637ca9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_command.yaml
@@ -0,0 +1,58 @@
+---
+
+- name: "Test SONiC CLI"
+ hosts: datacenter
+ gather_facts: no
+ connection: network_cli
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+
+ - name: Test SONiC single command
+ sonic_command:
+ commands: 'show interface status'
+ register: cmd_op
+ - name: Test SONiC single command with wait_for
+ sonic_command:
+ commands: 'show version'
+ wait_for:
+ - result[0] contains Del
+ register: cmd_op
+ - name: Test SONiC multiple command with wait_for
+ sonic_command:
+ commands:
+ - 'show version'
+ - 'show system'
+ wait_for:
+ - result[0] contains Dell
+ - result[1] contains Hostname
+ register: cmd_op
+ - name: Test SONiC commands with wait_for negative case
+ sonic_command:
+ commands:
+ - 'show version'
+ - 'show system'
+ wait_for:
+ - result[0] contains Fel
+ - result[1] contains Hostname
+ register: cmd_op
+ ignore_errors: yes
+ - name: Test SONiC commands with wait_for and match=any
+ sonic_command:
+ commands:
+ - 'show version'
+ - 'show system'
+ wait_for:
+ - result[0] contains Fel
+ - result[1] contains Hostname
+ match: any
+ retries: 3
+ interval: 2
+ register: cmd_op
+ - name: Test SONiC command with prompt handling
+ sonic_command:
+ commands:
+ - command: 'image remove all'
+ prompt: '\[y/N\]:$'
+ answer: 'N'
+ register: cmd_op
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_config.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_config.yaml
new file mode 100644
index 00000000..a368e0b6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_config.yaml
@@ -0,0 +1,42 @@
+---
+- hosts: datacenter
+ connection: network_cli
+ gather_facts: no
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: "Configure SNMP community for SONiC device along with 'save'"
+ sonic_config:
+ commands: ['snmp-server community public group ro']
+ save: yes
+
+ - name: "Configure interface description using 'parents' option on SONiC device"
+ sonic_config:
+ lines:
+ - description hi
+ parents: ['interface Eth1/3']
+
+ - name: "Configure bgp using multiple level 'parents' on SONiC device"
+ sonic_config:
+ lines:
+ - maximum-paths 4
+ parents: ['router bgp 4', 'address-family ipv4 unicast']
+
+ - name: "Configure ip access-list using 'before' and 'after' option on SONiC device"
+ sonic_config:
+ lines:
+ - seq 1 permit tcp any any ack
+ parents: ['ip access-list test']
+ before: ['no ip access-list test']
+ after: ['no ip access-list test']
+
+ - name: "Configure cli using source file"
+ sonic_config:
+ src: src.txt
+
+ - name: "Configure cli with prompts on SONiC device"
+ sonic_config:
+ commands:
+ - command: 'do image remove all'
+ prompt: '\[y/N\]:$'
+ answer: 'N'
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_facts.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_facts.yaml
new file mode 100644
index 00000000..57f91103
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_facts.yaml
@@ -0,0 +1,22 @@
+---
+- name: Ansible resource module facts example
+ hosts: datacenter
+ gather_facts: True
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: "Gather facts"
+ sonic_facts:
+ gather_subset: min
+ gather_network_resources:
+ - interfaces
+ - l3_interfaces
+ - lag_interfaces
+ - vlans
+ - bgp
+ register: result
+
+ - name: "debug facts"
+ debug:
+ msg: "{{ result.ansible_facts.ansible_network_resources }}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_interfaces_config.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_interfaces_config.yaml
new file mode 100644
index 00000000..9debc1fb
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_interfaces_config.yaml
@@ -0,0 +1,77 @@
+---
+- name: Ansible resource module example
+ hosts: datacenter
+ gather_facts: False
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: sonic_interfaces configuration
+ sonic_interfaces:
+ config:
+ - name: Loopback91
+ - name: Eth1/12
+ description: "hi test"
+ enabled: False
+ - name: Eth1/13
+ description: "hi ans"
+ enabled: False
+ - name: Add VLANs
+ sonic_vlans:
+ config:
+ - vlan_id: 11
+ - vlan_id: 12
+ - vlan_id: 13
+ state: merged
+ - name: sonic_l2_interfaces configuration
+ sonic_l2_interfaces:
+ config:
+ - name: Eth1/12
+ access:
+ vlan: 12
+ - name: Eth1/13
+ access:
+ vlan: 11
+ trunk:
+ allowed_vlans:
+ - vlan: 12
+ - vlan: 13
+ state: merged
+ - name: sonic_lag_interfaces configuration
+ sonic_lag_interfaces:
+ config:
+ - name: PortChannel12
+ members:
+ interfaces:
+ - member: Eth1/20
+ - member: Eth1/21
+ - name: PortChannel1
+ members:
+ interfaces:
+ - member: Eth1/22
+ - name: PortChannel2
+ state: merged
+ - name: sonic_l3_interfaces configuration
+ sonic_l3_interfaces:
+ config:
+ - name: Loopback100
+ ipv4:
+ addresses:
+ - address: 101.1.1.1/32
+ - name: vlan 11
+ ipv6:
+ addresses:
+ - address: 150::1/16
+ - name: po 12
+ ipv4:
+ addresses:
+ - address: 180.1.1.1/16
+ - name: Eth1/24
+ ipv6:
+ enabled: true
+ - name: Merge port breakout configurations
+ sonic_port_breakout:
+ config:
+ - name: 1/11
+ mode: 1x100G
+ state: merged
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_system.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_system.yaml
new file mode 100644
index 00000000..8ca5601c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_system.yaml
@@ -0,0 +1,18 @@
+---
+- name: Ansible resource module example for sonic_system
+ hosts: datacenter
+ gather_facts: False
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: Test SONiC system
+ sonic_system:
+ config:
+ hostname: SONIC-test
+ interface_naming: standard
+ anycast_address:
+ ipv4: true
+ ipv6: true
+ mac_address: 00:09:5B:EC:EE:F2
+ state: merged
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_vxlans_config.yaml b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_vxlans_config.yaml
new file mode 100644
index 00000000..f978578c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/sonic_vxlans_config.yaml
@@ -0,0 +1,38 @@
+---
+- name: Ansible resource module example
+ hosts: datacenter
+ gather_facts: False
+ connection: httpapi
+ collections:
+ - dellemc.enterprise_sonic
+ tasks:
+ - name: Add VLANs configuration
+ sonic_vlans:
+ config:
+ - vlan_id: 11
+ - vlan_id: 12
+ - vlan_id: 13
+ state: merged
+ - name: configure VRFs
+ sonic_vrfs:
+ config:
+ - name: Vrfcheck1
+ - name: Vrfcheck2
+ - name: "sonic_vxlans configuration"
+ sonic_vxlans:
+ config:
+ - name: vteptest1
+ source_ip: 1.1.1.1
+ primary_ip: 2.2.2.2
+ evpn_nvo: nvo6
+ vlan_map:
+ - vni: 101
+ vlan: 11
+ - vni: 102
+ vlan: 12
+ vrf_map:
+ - vni: 101
+ vrf: Vrfcheck1
+ - vni: 102
+ vrf: Vrfcheck2
+ register: merged03_output
diff --git a/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/src.txt b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/src.txt
new file mode 100644
index 00000000..47636d04
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/playbooks/common_examples/src.txt
@@ -0,0 +1,4 @@
+interface Eth1/10
+ description hello
+ mtu 1800
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/action/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/action/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/action/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/action/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/action/sonic.py
new file mode 100644
index 00000000..5f7ac3a8
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/action/sonic.py
@@ -0,0 +1,51 @@
+#
+# (c) 2020 Red Hat Inc.
+#
+# (c) 2020 Dell 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
+
+
+from ansible_collections.ansible.netcommon.plugins.action.network import ActionModule as ActionNetworkModule
+
+from ansible.utils.display import Display
+
+display = Display()
+
+DOCUMENTATION = """
+short_description: Action plugin module for sonic CLI modules
+version_added: 1.0.0
+"""
+
+
+class ActionModule(ActionNetworkModule):
+
+ def run(self, task_vars=None):
+
+ module_name = self._task.action.split('.')[-1]
+ self._config_module = True if module_name == 'sonic_config' else False
+
+ if self._play_context.connection in ('network_cli', 'httpapi'):
+ provider = self._task.args.get('provider', {})
+ if any(provider.values()):
+ display.warning('provider is unnecessary when using network_cli and will be ignored')
+ del self._task.args['provider']
+
+ result = super(ActionModule, self).run(task_vars=task_vars)
+ return result
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py
new file mode 100644
index 00000000..37f1d872
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/cliconf/sonic.py
@@ -0,0 +1,118 @@
+#
+# (c) 2020 Red Hat Inc.
+#
+# (c) 2020 Dell 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 = """
+---
+name: sonic
+short_description: Use sonic cliconf to run command on Dell OS10 platform
+description:
+ - This sonic plugin provides low level abstraction apis for
+ sending and receiving CLI commands from Dell OS10 network devices.
+"""
+
+import json
+
+from itertools import chain
+
+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_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
+from ansible.plugins.cliconf import CliconfBase, enable_mode
+
+
+class Cliconf(CliconfBase):
+
+ def get_device_info(self):
+ device_info = {}
+ device_info['network_os'] = 'sonic'
+ return device_info
+
+ @enable_mode
+ def edit_config(self, command):
+ response = []
+ self.send_command("configure terminal")
+ for cmd in to_list(command):
+ if isinstance(cmd, dict):
+ resp = self.get(command=cmd["command"], prompt=cmd["prompt"], answer=cmd["answer"])
+ response.append(resp)
+ else:
+ response.append(self.send_command(to_bytes(cmd)))
+ self.send_command("end")
+ return response
+
+ @enable_mode
+ def get_config(self, source="running", flags=None, format=None):
+ if source not in ("running", "startup"):
+ raise ValueError(
+ "fetching configuration from %s is not supported" % source
+ )
+ if not flags:
+ flags = []
+ if source == "running":
+ cmd = "show running-config "
+ else:
+ cmd = "show startup-config "
+
+ cmd += " ".join(to_list(flags))
+ cmd = cmd.strip()
+ return self.send_command(cmd)
+
+ def get(self, command, prompt=None, answer=None, sendonly=False, newline=True, check_all=False):
+ return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all)
+
+ def get_capabilities(self):
+ result = super(Cliconf, self).get_capabilities()
+ return json.dumps(result)
+
+ 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:
+ raise ValueError("'output' value %s is not supported for run_commands" % output)
+
+ try:
+ out = self.send_command(**cmd)
+ except AnsibleConnectionFailure as e:
+ if check_rc:
+ raise
+ out = getattr(e, 'err', to_text(e))
+
+ responses.append(out)
+
+ return responses
+
+ def set_cli_prompt_context(self):
+ """
+ Make sure we are in the operational cli mode
+ :return: None
+ """
+ if self._connection.connected:
+ self._update_cli_prompt_context(config_context=')#')
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/sonic.py
new file mode 100644
index 00000000..4745e2e9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/httpapi/sonic.py
@@ -0,0 +1,113 @@
+# (c) 2019 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 = """
+---
+name: sonic
+short_description: HttpApi Plugin for devices supporting Restconf SONIC API
+description:
+ - This HttpApi plugin provides methods to connect to Restconf SONIC API endpoints.
+version_added: 1.0.0
+options:
+ root_path:
+ type: str
+ description:
+ - Specifies the location of the Restconf root.
+ default: '/restconf'
+ vars:
+ - name: ansible_httpapi_restconf_root
+"""
+
+import json
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.connection import ConnectionError
+from ansible.module_utils.six.moves.urllib.error import HTTPError
+from ansible.plugins.httpapi import HttpApiBase
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
+
+CONTENT_TYPE = 'application/yang-data+json'
+
+
+class HttpApi(HttpApiBase):
+ def send_request(self, data, **message_kwargs):
+ if data:
+ data = json.dumps(data)
+
+ path = '/'.join([self.get_option('root_path').rstrip('/'), message_kwargs.get('path', '').lstrip('/')])
+
+ headers = {
+ 'Content-Type': message_kwargs.get('content_type') or CONTENT_TYPE,
+ 'Accept': message_kwargs.get('accept') or CONTENT_TYPE,
+ }
+ response, response_data = self.connection.send(path, data, headers=headers, method=message_kwargs.get('method'))
+
+ return handle_response(response, response_data, message_kwargs)
+
+ def get(self, command):
+ return self.send_request(path=command, data=None, method='get')
+
+ def edit_config(self, requests):
+ """Send a list of http requests to remote device and return results
+ """
+ if requests is None:
+ raise ValueError("'requests' value is required")
+
+ responses = list()
+ for req in to_list(requests):
+ try:
+ response = self.send_request(**req)
+ except ConnectionError as exc:
+ raise ConnectionError(to_text(exc, errors='surrogate_then_replace'))
+ responses.append(response)
+ return responses
+
+ def get_capabilities(self):
+ result = {}
+ result['rpc'] = []
+ result['network_api'] = 'sonic_rest'
+
+ return json.dumps(result)
+
+
+def handle_response(response, response_data, request_data):
+ response_data = response_data.read()
+ try:
+ if not response_data:
+ response_data = ""
+ else:
+ response_data = json.loads(response_data.decode('utf-8'))
+ except ValueError:
+ pass
+
+ if isinstance(response, HTTPError):
+ if response_data:
+ if 'errors' in response_data:
+ errors = response_data['errors']['error']
+ error_text = '\n'.join((error['error-message'] for error in errors))
+ else:
+ error_text = response_data
+ error_text.update({u'code': response.code})
+ error_text.update({u'request_data': request_data})
+ raise ConnectionError(error_text, code=response.code)
+ raise ConnectionError(to_text(response), code=response.code)
+ return response.getcode(), response_data
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py
new file mode 100644
index 00000000..86040892
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/aaa/aaa.py
@@ -0,0 +1,66 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_aaa module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class AaaArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_aaa module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'options': {
+ 'authentication': {
+ 'options': {
+ 'data': {
+ 'options': {
+ 'fail_through': {'type': 'bool'},
+ 'group': {
+ 'choices': ['ldap', 'radius', 'tacacs+'],
+ 'type': 'str'
+ },
+ 'local': {'type': 'bool'}
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged', 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.py
new file mode 100644
index 00000000..fb761813
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp/bgp.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)
+
+#############################################
+# 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 sonic_bgp module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class BgpArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'bestpath': {
+ 'options': {
+ 'as_path': {
+ 'options': {
+ 'confed': {'type': 'bool'},
+ 'ignore': {'type': 'bool'},
+ 'multipath_relax': {'type': 'bool'},
+ 'multipath_relax_as_set': {'type': 'bool'}
+ },
+ 'type': 'dict'
+ },
+ 'compare_routerid': {'type': 'bool'},
+ 'med': {
+ 'options': {
+ 'confed': {'type': 'bool'},
+ 'missing_as_worst': {'type': 'bool'},
+ 'always_compare_med': {'type': 'bool'}
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'bgp_as': {'required': True, 'type': 'str'},
+ 'log_neighbor_changes': {'type': 'bool'},
+ 'router_id': {'type': 'str'},
+ "max_med": {
+ "options": {
+ "on_startup": {
+ "options": {
+ "timer": {"type": "int"},
+ "med_val": {"type": "int"}
+ },
+ "type": "dict"
+ }
+ },
+ "type": "dict"
+ },
+ 'timers': {
+ 'options': {
+ 'holdtime': {'type': 'int'},
+ 'keepalive_interval': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'vrf_name': {'default': 'default', 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py
new file mode 100644
index 00000000..ac22210e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_af/bgp_af.py
@@ -0,0 +1,117 @@
+#
+# -*- 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 sonic_bgp_af module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Bgp_afArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp_af module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'address_family': {
+ 'options': {
+ 'afis': {
+ 'elements': 'dict',
+ 'options': {
+ 'advertise_pip': {'type': 'bool'},
+ 'advertise_pip_ip': {'type': 'str'},
+ 'advertise_pip_peer_ip': {'type': 'str'},
+ 'advertise_svi_ip': {'type': 'bool'},
+ 'route_advertise_list': {
+ 'elements': 'dict',
+ 'options': {
+ 'advertise_afi': {
+ 'choices': ['ipv4', 'ipv6'],
+ 'required': True,
+ 'type': 'str'
+ },
+ 'route_map': {
+ 'type': 'str'
+ }
+ },
+ 'type': 'list'
+ },
+ 'advertise_all_vni': {'type': 'bool'},
+ 'advertise_default_gw': {'type': 'bool'},
+ 'afi': {
+ 'choices': ['ipv4', 'ipv6', 'l2vpn'],
+ 'required': True,
+ 'type': 'str'
+ },
+ 'max_path': {
+ 'options': {
+ 'ebgp': {'type': 'int'},
+ 'ibgp': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'network': {'type': 'list', 'elements': 'str'},
+ 'dampening': {'type': 'bool'},
+ 'redistribute': {
+ 'elements': 'dict',
+ 'options': {
+ 'metric': {'type': 'str'},
+ 'protocol': {
+ 'choices': ['ospf', 'static', 'connected'],
+ 'required': True,
+ 'type': 'str'
+ },
+ 'route_map': {'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'safi': {
+ 'choices': ['unicast', 'evpn'],
+ 'default': 'unicast',
+ 'type': 'str'
+ }
+ },
+ 'required_together': [['afi', 'safi']],
+ 'type': 'list'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'bgp_as': {'required': True, 'type': 'str'},
+ 'vrf_name': {'default': 'default', 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py
new file mode 100644
index 00000000..dec9b930
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_as_paths/bgp_as_paths.py
@@ -0,0 +1,48 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_as_paths module
+"""
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Bgp_as_pathsArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp_as_paths module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {'config': {'elements': 'dict',
+ 'options': {'permit': {'required': False, 'type': 'bool'},
+ 'members': {'elements': 'str',
+ 'required': False,
+ 'type': 'list'},
+ 'name': {'required': True, 'type': 'str'}},
+ 'type': 'list'},
+ 'state': {'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'}} # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py
new file mode 100644
index 00000000..867e5520
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py
@@ -0,0 +1,59 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_communities module
+"""
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Bgp_communitiesArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp_communities module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {'config': {'elements': 'dict',
+ 'options': {'aann': {'type': 'str'},
+ 'local_as': {'type': 'bool'},
+ 'match': {'choices': ['ALL', 'ANY'],
+ 'default': 'ANY',
+ 'type': 'str'},
+ 'members': {'options': {'regex': {'elements': 'str',
+ 'type': 'list'}},
+ 'type': 'dict'},
+ 'name': {'required': True, 'type': 'str'},
+ 'no_advertise': {'type': 'bool'},
+ 'no_export': {'type': 'bool'},
+ 'no_peer': {'type': 'bool'},
+ 'permit': {'type': 'bool'},
+ 'type': {'choices': ['standard', 'expanded'],
+ 'default': 'standard',
+ 'type': 'str'}},
+ 'type': 'list'},
+ 'state': {'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'}} # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py
new file mode 100644
index 00000000..aec0f364
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py
@@ -0,0 +1,75 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_ext_communities module
+"""
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Bgp_ext_communitiesArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp_ext_communities module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'match': {
+ 'choices': ['all', 'any'],
+ 'default': 'any',
+ 'type': 'str'
+ },
+ 'members': {
+ 'mutually_exclusive': [
+ ['regex', 'route_origin'],
+ ['regex', 'route_target']
+ ],
+ 'options': {
+ 'regex': {'elements': 'str', 'type': 'list'},
+ 'route_origin': {'elements': 'str', 'type': 'list'},
+ 'route_target': {'elements': 'str', 'type': 'list'}
+ },
+ 'type': 'dict'
+ },
+ 'name': {'required': True, 'type': 'str'},
+ 'permit': {'type': 'bool'},
+ 'type': {
+ 'choices': ['standard', 'expanded'],
+ 'default': 'standard',
+ 'type': 'str'
+ }
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/bgp_neighbors.py
new file mode 100644
index 00000000..02e695fb
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors/bgp_neighbors.py
@@ -0,0 +1,249 @@
+#
+# -*- 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 sonic_bgp_neighbors module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Bgp_neighborsArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp_neighbors module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'bgp_as': {'required': True, 'type': 'str'},
+ 'neighbors': {
+ 'elements': 'dict',
+ 'options': {
+ 'neighbor': {'required': True, 'type': 'str'},
+ 'remote_as': {
+ 'mutually_exclusive': [['peer_type', 'peer_as']],
+ 'options': {
+ 'peer_type': {'type': 'str', 'choices': ['internal', 'external']},
+ 'peer_as': {'type': 'int'},
+ },
+ 'type': 'dict'
+ },
+ 'peer_group': {'type': 'str'},
+ 'bfd': {
+ 'options': {
+ 'enabled': {'type': 'bool'},
+ 'check_failure': {'type': 'bool'},
+ 'profile': {'type': 'str'}
+ },
+ 'type': 'dict'
+ },
+ 'advertisement_interval': {'type': 'int'},
+ 'timers': {
+ 'options': {
+ 'holdtime': {'type': 'int'},
+ 'keepalive': {'type': 'int'},
+ 'connect_retry': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'capability': {
+ 'options': {
+ 'dynamic': {'type': 'bool'},
+ 'extended_nexthop': {'type': 'bool'},
+ },
+ 'type': 'dict'
+ },
+ 'auth_pwd': {
+ 'options': {
+ 'pwd': {'required': True, 'type': 'str'},
+ 'encrypted': {'default': 'False', 'type': 'bool'},
+ },
+ 'type': 'dict'
+ },
+ 'nbr_description': {'type': 'str'},
+ 'disable_connected_check': {'type': 'bool'},
+ 'dont_negotiate_capability': {'type': 'bool'},
+ 'ebgp_multihop': {
+ 'options': {
+ 'enabled': {'default': 'False', 'type': 'bool'},
+ 'multihop_ttl': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'enforce_first_as': {'type': 'bool'},
+ 'enforce_multihop': {'type': 'bool'},
+ 'local_address': {'type': 'str'},
+ 'local_as': {
+ 'options': {
+ 'as': {'required': True, 'type': 'int'},
+ 'no_prepend': {'type': 'bool'},
+ 'replace_as': {'type': 'bool'},
+ },
+ 'type': 'dict'
+ },
+ 'override_capability': {'type': 'bool'},
+ 'passive': {'default': 'False', 'type': 'bool'},
+ 'port': {'type': 'int'},
+ 'shutdown_msg': {'type': 'str'},
+ 'solo': {'type': 'bool'},
+ 'strict_capability_match': {'type': 'bool'},
+ 'ttl_security': {'type': 'int'},
+ 'v6only': {'type': 'bool'}
+ },
+ 'type': 'list'
+ },
+ 'peer_group': {
+ 'elements': 'dict',
+ 'options': {
+ 'name': {'required': True, 'type': 'str'},
+ 'remote_as': {
+ 'mutually_exclusive': [['peer_type', 'peer_as']],
+ 'options': {
+ 'peer_type': {'type': 'str', 'choices': ['internal', 'external']},
+ 'peer_as': {'type': 'int'},
+ },
+ 'type': 'dict'
+ },
+ 'address_family': {
+ 'options': {
+ 'afis': {
+ 'elements': 'dict',
+ 'options': {
+ 'activate': {'type': 'bool'},
+ 'afi': {
+ 'choices': ['ipv4', 'ipv6', 'l2vpn'],
+ 'type': 'str'
+ },
+ 'allowas_in': {
+ 'mutually_exclusive': [['origin', 'value']],
+ 'options': {
+ 'origin': {'type': 'bool'},
+ 'value': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'ip_afi': {
+ 'options': {
+ 'default_policy_name': {'type': 'str'},
+ 'send_default_route': {'default': False, 'type': 'bool'}
+ },
+ 'type': 'dict'
+ },
+ 'prefix_limit': {
+ 'options': {
+ 'max_prefixes': {'type': 'int'},
+ 'prevent_teardown': {'default': False, 'type': 'bool'},
+ 'warning_threshold': {'type': 'int'},
+ 'restart_timer': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'prefix_list_in': {'type': 'str'},
+ 'prefix_list_out': {'type': 'str'},
+ 'safi': {
+ 'choices': ['unicast', 'evpn'],
+ 'type': 'str'
+ },
+ },
+ 'required_together': [['afi', 'safi']],
+ 'type': 'list'
+ },
+ },
+ 'type': 'dict'
+ },
+ 'bfd': {
+ 'options': {
+ 'enabled': {'type': 'bool'},
+ 'check_failure': {'type': 'bool'},
+ 'profile': {'type': 'str'}
+ },
+ 'type': 'dict'
+ },
+ 'advertisement_interval': {'type': 'int'},
+ 'timers': {
+ 'options': {
+ 'holdtime': {'type': 'int'},
+ 'keepalive': {'type': 'int'},
+ 'connect_retry': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'capability': {
+ 'options': {
+ 'dynamic': {'type': 'bool'},
+ 'extended_nexthop': {'type': 'bool'},
+ },
+ 'type': 'dict'
+ },
+ 'auth_pwd': {
+ 'options': {
+ 'pwd': {'required': True, 'type': 'str'},
+ 'encrypted': {'default': 'False', 'type': 'bool'},
+ },
+ 'type': 'dict'
+ },
+ 'pg_description': {'type': 'str'},
+ 'disable_connected_check': {'type': 'bool'},
+ 'dont_negotiate_capability': {'type': 'bool'},
+ 'ebgp_multihop': {
+ 'options': {
+ 'enabled': {'default': 'False', 'type': 'bool'},
+ 'multihop_ttl': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'enforce_first_as': {'type': 'bool'},
+ 'enforce_multihop': {'type': 'bool'},
+ 'local_address': {'type': 'str'},
+ 'local_as': {
+ 'options': {
+ 'as': {'required': True, 'type': 'int'},
+ 'no_prepend': {'type': 'bool'},
+ 'replace_as': {'type': 'bool'},
+ },
+ 'type': 'dict'
+ },
+ 'override_capability': {'type': 'bool'},
+ 'passive': {'default': 'False', 'type': 'bool'},
+ 'shutdown_msg': {'type': 'str'},
+ 'solo': {'type': 'bool'},
+ 'strict_capability_match': {'type': 'bool'},
+ 'ttl_security': {'type': 'int'}
+ },
+ 'type': 'list'
+ },
+ 'vrf_name': {'default': 'default', 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/bgp_neighbors_af.py
new file mode 100644
index 00000000..6cafc922
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/bgp_neighbors_af/bgp_neighbors_af.py
@@ -0,0 +1,114 @@
+#
+# -*- 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 sonic_bgp_neighbors_af module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Bgp_neighbors_afArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_bgp_neighbors_af module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'bgp_as': {'required': True, 'type': 'str'},
+ 'neighbors': {
+ 'elements': 'dict',
+ 'options': {
+ 'address_family': {
+ 'elements': 'dict',
+ 'options': {
+ 'activate': {'type': 'bool'},
+ 'afi': {
+ 'choices': ['ipv4', 'ipv6', 'l2vpn'],
+ 'required': True,
+ 'type': 'str'
+ },
+ 'allowas_in': {
+ 'mutually_exclusive': [['origin', 'value']],
+ 'options': {
+ 'origin': {'type': 'bool'},
+ 'value': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'ip_afi': {
+ 'options': {
+ 'default_policy_name': {'type': 'str'},
+ 'send_default_route': {'default': False, 'type': 'bool'}
+ },
+ 'type': 'dict'
+ },
+ 'prefix_limit': {
+ 'options': {
+ 'max_prefixes': {'type': 'int'},
+ 'prevent_teardown': {'default': False, 'type': 'bool'},
+ 'warning_threshold': {'type': 'int'},
+ 'restart_timer': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'prefix_list_in': {'type': 'str'},
+ 'prefix_list_out': {'type': 'str'},
+ 'route_map': {
+ 'elements': 'dict',
+ 'options': {
+ 'direction': {'type': 'str'},
+ 'name': {'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'route_reflector_client': {'type': 'bool'},
+ 'route_server_client': {'type': 'bool'},
+ 'safi': {
+ 'choices': ['unicast', 'evpn'],
+ 'default': 'unicast',
+ 'type': 'str'
+ }
+ },
+ 'required_together': [['afi', 'safi']],
+ 'type': 'list'
+ },
+ 'neighbor': {'required': True, 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'vrf_name': {'default': 'default', 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py
new file mode 100644
index 00000000..3a4d0298
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/facts/facts.py
@@ -0,0 +1,53 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The arg spec for the sonic facts module.
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class FactsArgs(object): # pylint: disable=R0903
+
+ """ The arg spec for the sonic facts module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ choices = [
+ 'all',
+ 'vlans',
+ 'interfaces',
+ 'l2_interfaces',
+ 'l3_interfaces',
+ 'lag_interfaces',
+ 'bgp',
+ 'bgp_af',
+ 'bgp_neighbors',
+ 'bgp_neighbors_af',
+ 'bgp_as_paths',
+ 'bgp_communities',
+ 'bgp_ext_communities',
+ 'mclag',
+ 'prefix_lists',
+ 'vrfs',
+ 'vxlans',
+ 'users',
+ 'system',
+ 'port_breakout',
+ 'aaa',
+ 'tacacs_server',
+ 'radius_server',
+ 'static_routes',
+ 'ntp'
+ ]
+
+ argument_spec = {
+ 'gather_subset': dict(default=['!config'], type='list', elements='str'),
+ 'gather_network_resources': dict(choices=choices, type='list', elements='str'),
+ }
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py
new file mode 100644
index 00000000..76c36a90
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/interfaces/interfaces.py
@@ -0,0 +1,56 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_interfaces module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class InterfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "description": {"type": "str"},
+ "enabled": {"type": "bool"},
+ "mtu": {"type": "int"},
+ "name": {"required": True, "type": "str"}
+ },
+ "type": "list"
+ },
+ "state": {
+ "choices": ["merged", "deleted"],
+ "default": "merged",
+ "type": "str"
+ }
+ }
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py
new file mode 100644
index 00000000..bbebe2d5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,71 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_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 sonic_l2_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'access': {
+ 'options': {
+ 'vlan': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'name': {'required': True, 'type': 'str'},
+ 'trunk': {
+ 'options': {
+ 'allowed_vlans': {
+ 'elements': 'dict',
+ 'options': {
+ 'vlan': {'type': 'int'}
+ },
+ 'type': 'list'
+ }
+ },
+ 'type': 'dict'
+ }
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ }
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py
new file mode 100644
index 00000000..6e83289c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/l3_interfaces/l3_interfaces.py
@@ -0,0 +1,81 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_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 sonic_l3_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'ipv4': {
+ 'mutually_exclusive': [['addresses', 'anycast_addresses']],
+ 'options': {
+ 'addresses': {
+ 'elements': 'dict',
+ 'options': {
+ 'address': {'type': 'str'},
+ 'secondary': {'default': 'False', 'type': 'bool'}
+ },
+ 'type': 'list'
+ },
+ 'anycast_addresses': {'elements': 'str', 'type': 'list'},
+ },
+ 'type': 'dict'
+ },
+ 'ipv6': {
+ 'options': {
+ 'addresses': {
+ 'elements': 'dict',
+ 'options': {
+ 'address': {'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'enabled': {'type': 'bool'}
+ },
+ 'type': 'dict'
+ },
+ 'name': {'required': True, 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py
new file mode 100644
index 00000000..867d61a2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/lag_interfaces/lag_interfaces.py
@@ -0,0 +1,67 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_lag_interfaces module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Lag_interfacesArgs(object): # pylint: disable=R0903
+
+ """The arg spec for the sonic_lag_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "members": {
+ "options": {
+ "interfaces": {
+ "elements": "dict",
+ "options": {
+ "member": {"type": "str"}
+ },
+ "type": "list"
+ }
+ },
+ "type": "dict"
+ },
+ "name": {"required": True, "type": "str"},
+ "mode": {"type": "str", "choices": ["static", "lacp"]}
+ },
+ "type": "list"
+ },
+ "state": {
+ "choices": ["merged", "deleted"],
+ "default": "merged",
+ "type": "str"
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py
new file mode 100644
index 00000000..be3c38ca
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/mclag/mclag.py
@@ -0,0 +1,82 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_mclag module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class MclagArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_mclag module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'options': {
+ 'domain_id': {'required': True, 'type': 'int'},
+ 'keepalive': {'type': 'int'},
+ 'peer_address': {'type': 'str'},
+ 'peer_link': {'type': 'str'},
+ 'members': {
+ 'options': {
+ 'portchannels': {
+ 'elements': 'dict',
+ 'options': {
+ 'lag': {'type': 'str'}
+ },
+ 'type': 'list'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'session_timeout': {'type': 'int'},
+ 'source_address': {'type': 'str'},
+ 'system_mac': {'type': 'str'},
+ 'unique_ip': {
+ 'options': {
+ 'vlans': {
+ 'elements': 'dict',
+ 'options': {
+ 'vlan': {'type': 'str'}
+ },
+ 'type': 'list'
+ }
+ },
+ 'type': 'dict'
+ },
+ },
+ 'type': 'dict'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py
new file mode 100644
index 00000000..062520af
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/ntp/ntp.py
@@ -0,0 +1,89 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_ntp module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class NtpArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_ntp module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'options': {
+ 'enable_ntp_auth': {'type': 'bool'},
+ 'ntp_keys': {
+ 'elements': 'dict',
+ 'options': {
+ 'encrypted': {'type': 'bool'},
+ 'key_id': {'required': True,
+ 'type': 'int',
+ 'no_log': True},
+ 'key_type': {'type': 'str',
+ 'choices': ['NTP_AUTH_SHA1',
+ 'NTP_AUTH_MD5',
+ 'NTP_AUTH_SHA2_256']},
+ 'key_value': {'type': 'str', 'no_log': True}
+ },
+ 'type': 'list',
+ 'no_log': True
+ },
+ 'servers': {
+ 'elements': 'dict',
+ 'options': {
+ 'address': {'required': True,
+ 'type': 'str'},
+ 'key_id': {'type': 'int', 'no_log': True},
+ 'maxpoll': {'type': 'int'},
+ 'minpoll': {'type': 'int'}
+ },
+ 'type': 'list'
+ },
+ 'source_interfaces': {
+ 'elements': 'str',
+ 'type': 'list'
+ },
+ 'trusted_keys': {
+ 'elements': 'int',
+ 'type': 'list',
+ 'no_log': True
+ },
+ 'vrf': {'type': 'str'}
+ },
+ 'type': 'dict'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py
new file mode 100644
index 00000000..3b8f4a5a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/port_breakout/port_breakout.py
@@ -0,0 +1,57 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_port_breakout module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Port_breakoutArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_port_breakout module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'mode': {
+ 'choices': ['1x100G', '1x400G', '1x40G', '2x100G', '2x200G',
+ '2x50G', '4x100G', '4x10G', '4x25G', '4x50G'],
+ 'type': 'str'
+ },
+ 'name': {'required': True, 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py
new file mode 100644
index 00000000..d043ae6f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/prefix_lists/prefix_lists.py
@@ -0,0 +1,71 @@
+#
+# -*- 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 sonic_prefix_lists module
+"""
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class Prefix_listsArgs: # pylint: disable=R0903
+ """The arg spec for the sonic_prefix_lists module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'afi': {
+ 'choices': ['ipv4', 'ipv6'],
+ 'default': 'ipv4',
+ 'type': 'str'
+ },
+ 'name': {'required': True, 'type': 'str'},
+ 'prefixes': {
+ 'elements': 'dict',
+ 'options': {
+ 'action': {
+ 'choices': ['permit', 'deny'],
+ 'required': True,
+ 'type': 'str'
+ },
+ 'ge': {'type': 'int'},
+ 'le': {'type': 'int'},
+ 'prefix': {'required': True, 'type': 'str'},
+ 'sequence': {'required': True, 'type': 'int'}},
+ 'type': 'list'
+ }
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py
new file mode 100644
index 00000000..a56147a5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/radius_server/radius_server.py
@@ -0,0 +1,83 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_radius_server module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Radius_serverArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_radius_server module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'options': {
+ 'auth_type': {
+ 'choices': ['pap', 'chap', 'mschapv2'],
+ 'default': 'pap',
+ 'type': 'str'
+ },
+ 'key': {'type': 'str', 'no_log': True},
+ 'nas_ip': {'type': 'str'},
+ 'retransmit': {'type': 'int'},
+ 'servers': {
+ 'options': {
+ 'host': {
+ 'elements': 'dict',
+ 'options': {
+ 'auth_type': {
+ 'choices': ['pap', 'chap', 'mschapv2'],
+ 'type': 'str'
+ },
+ 'key': {'type': 'str', 'no_log': True},
+ 'name': {'type': 'str'},
+ 'port': {'type': 'int'},
+ 'priority': {'type': 'int'},
+ 'retransmit': {'type': 'int'},
+ 'source_interface': {'type': 'str'},
+ 'timeout': {'type': 'int'},
+ 'vrf': {'type': 'str'}
+ },
+ 'type': 'list'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'statistics': {'type': 'bool'},
+ 'timeout': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py
new file mode 100644
index 00000000..a146f1ec
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/static_routes/static_routes.py
@@ -0,0 +1,79 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_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 sonic_static_routes module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'static_list': {
+ 'elements': 'dict',
+ 'options': {
+ 'next_hops': {
+ 'elements': 'dict',
+ 'options': {
+ 'index': {
+ 'required': True,
+ 'options': {
+ 'blackhole': {'type': 'bool', 'default': False},
+ 'interface': {'type': 'str'},
+ 'nexthop_vrf': {'type': 'str'},
+ 'next_hop': {'type': 'str'}
+ },
+ 'type': 'dict'
+ },
+ 'metric': {'type': 'int'},
+ 'tag': {'type': 'int'},
+ 'track': {'type': 'int'}
+ },
+ 'type': 'list'
+ },
+ 'prefix': {'required': True, 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'vrf_name': {'required': True, 'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py
new file mode 100644
index 00000000..b08c5f4b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/system/system.py
@@ -0,0 +1,64 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_system module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class SystemArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_system module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'options': {
+ 'anycast_address': {
+ 'options': {
+ 'ipv4': {'type': 'bool'},
+ 'ipv6': {'type': 'bool'},
+ 'mac_address': {'type': 'str'}
+ },
+ 'type': 'dict'
+ },
+ 'hostname': {'type': 'str'},
+ 'interface_naming': {
+ 'choices': ['standard', 'native'],
+ 'type': 'str'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py
new file mode 100644
index 00000000..aad1746d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/tacacs_server/tacacs_server.py
@@ -0,0 +1,80 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_tacacs_server module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class Tacacs_serverArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_tacacs_server module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'options': {
+ 'auth_type': {
+ 'choices': ['pap', 'chap', 'mschap', 'login'],
+ 'default': 'pap',
+ 'type': 'str'
+ },
+ 'key': {'type': 'str', 'no_log': True},
+ 'servers': {
+ 'options': {
+ 'host': {
+ 'elements': 'dict',
+ 'options': {
+ 'auth_type': {
+ 'choices': ['pap', 'chap', 'mschap', 'login'],
+ 'default': 'pap',
+ 'type': 'str'
+ },
+ 'key': {'type': 'str', 'no_log': True},
+ 'name': {'type': 'str'},
+ 'port': {'default': 49, 'type': 'int'},
+ 'priority': {'default': 1, 'type': 'int'},
+ 'timeout': {'default': 5, 'type': 'int'},
+ 'vrf': {'default': 'default', 'type': 'str'}
+ },
+ 'type': 'list'
+ }
+ },
+ 'type': 'dict'
+ },
+ 'source_interface': {'type': 'str'},
+ 'timeout': {'type': 'int'}
+ },
+ 'type': 'dict'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py
new file mode 100644
index 00000000..db23d78e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/users/users.py
@@ -0,0 +1,62 @@
+#
+# -*- 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 sonic_users module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class UsersArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_users module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'name': {'required': True, 'type': 'str'},
+ 'password': {'type': 'str', 'no_log': True},
+ 'role': {
+ 'choices': ['admin', 'operator'],
+ 'type': 'str'
+ },
+ 'update_password': {
+ 'choices': ['always', 'on_create'],
+ 'default': 'always',
+ 'type': 'str'
+ }
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py
new file mode 100644
index 00000000..971fc857
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vlans/vlans.py
@@ -0,0 +1,54 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_vlans module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class VlansArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_vlans module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'vlan_id': {'required': True, 'type': 'int'},
+ 'description': {'type': 'str'}
+ },
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ }
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py
new file mode 100644
index 00000000..e074936a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vrfs/vrfs.py
@@ -0,0 +1,66 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_vrfs module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class VrfsArgs(object): # pylint: disable=R0903
+
+ """The arg spec for the sonic_vrfs module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ "config": {
+ "elements": "dict",
+ "options": {
+ "members": {
+ "options": {
+ "interfaces": {
+ "elements": "dict",
+ "options": {
+ "name": {"type": "str"}
+ },
+ "type": "list"
+ }
+ },
+ "type": "dict"
+ },
+ "name": {"required": True, "type": "str"}
+ },
+ "type": "list"
+ },
+ "state": {
+ "choices": ["merged", "deleted"],
+ "default": "merged",
+ "type": "str"
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py
new file mode 100644
index 00000000..dd475b78
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/argspec/vxlans/vxlans.py
@@ -0,0 +1,73 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_vxlans module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class VxlansArgs(object): # pylint: disable=R0903
+ """The arg spec for the sonic_vxlans module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'evpn_nvo': {'type': 'str'},
+ 'name': {'required': True, 'type': 'str'},
+ 'source_ip': {'type': 'str'},
+ 'primary_ip': {'type': 'str'},
+ 'vlan_map': {
+ 'elements': 'dict',
+ 'options': {
+ 'vlan': {'type': 'int'},
+ 'vni': {'required': True, 'type': 'int'}
+ },
+ 'type': 'list'
+ },
+ 'vrf_map': {
+ 'elements': 'dict',
+ 'options': {
+ 'vni': {'required': True, 'type': 'int'},
+ 'vrf': {'type': 'str'}
+ },
+ 'type': 'list'
+ }
+ },
+ 'required_together': [['source_ip', 'evpn_nvo']],
+ 'type': 'list'
+ },
+ 'state': {
+ 'choices': ['merged', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'
+ }
+ } # pylint: disable=C0301
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py
new file mode 100644
index 00000000..85f93bc7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/aaa/aaa.py
@@ -0,0 +1,236 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_aaa 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+
+PATCH = 'patch'
+DELETE = 'delete'
+
+
+class Aaa(ConfigBase):
+ """
+ The sonic_aaa class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'aaa',
+ ]
+
+ def __init__(self, module):
+ super(Aaa, self).__init__(module)
+
+ def get_aaa_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)
+ aaa_facts = facts['ansible_network_resources'].get('aaa')
+ if not aaa_facts:
+ return []
+ return aaa_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_aaa_facts = self.get_aaa_facts()
+ commands, requests = self.set_config(existing_aaa_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ self.edit_config(requests)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_aaa_facts = self.get_aaa_facts()
+
+ result['before'] = existing_aaa_facts
+ if result['changed']:
+ result['after'] = changed_aaa_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def edit_config(self, requests):
+ try:
+ response = edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ def set_config(self, existing_aaa_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_aaa_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 not want:
+ want = {}
+
+ if state == 'deleted':
+ commands = self._state_deleted(want, have)
+ elif state == 'merged':
+ diff = get_diff(want, have)
+ commands = self._state_merged(want, have, diff)
+ return commands
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = []
+ requests = []
+ if diff:
+ requests = self.get_create_aaa_request(diff)
+ if len(requests) > 0:
+ commands = update_states(diff, "merged")
+ return commands, requests
+
+ 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 = []
+ requests = []
+ if not want:
+ if have:
+ requests = self.get_delete_all_aaa_request(have)
+ if len(requests) > 0:
+ commands = update_states(have, "deleted")
+ else:
+ want = utils.remove_empties(want)
+ new_have = self.remove_default_entries(have)
+ d_diff = get_diff(want, new_have, is_skeleton=True)
+ diff_want = get_diff(want, d_diff, is_skeleton=True)
+ if diff_want:
+ requests = self.get_delete_all_aaa_request(diff_want)
+ if len(requests) > 0:
+ commands = update_states(diff_want, "deleted")
+ return commands, requests
+
+ def get_create_aaa_request(self, commands):
+ requests = []
+ aaa_path = 'data/openconfig-system:system/aaa'
+ method = PATCH
+ aaa_payload = self.build_create_aaa_payload(commands)
+ if aaa_payload:
+ request = {'path': aaa_path, 'method': method, 'data': aaa_payload}
+ requests.append(request)
+ return requests
+
+ def build_create_aaa_payload(self, commands):
+ payload = {}
+ if "authentication" in commands and commands["authentication"]:
+ payload = {"openconfig-system:aaa": {"authentication": {"config": {"authentication-method": []}}}}
+ if "local" in commands["authentication"]["data"] and commands["authentication"]["data"]["local"]:
+ payload['openconfig-system:aaa']['authentication']['config']['authentication-method'].append("local")
+ if "group" in commands["authentication"]["data"] and commands["authentication"]["data"]["group"]:
+ auth_method = commands["authentication"]["data"]["group"]
+ payload['openconfig-system:aaa']['authentication']['config']['authentication-method'].append(auth_method)
+ if "fail_through" in commands["authentication"]["data"]:
+ cfg = {'failthrough': str(commands["authentication"]["data"]["fail_through"])}
+ payload['openconfig-system:aaa']['authentication']['config'].update(cfg)
+ return payload
+
+ def remove_default_entries(self, data):
+ new_data = {}
+ if not data:
+ return new_data
+ else:
+ new_data = {'authentication': {'data': {}}}
+ local = data['authentication']['data'].get('local', None)
+ if local is not None:
+ new_data["authentication"]["data"]["local"] = local
+ group = data['authentication']['data'].get('group', None)
+ if group is not None:
+ new_data["authentication"]["data"]["group"] = group
+ fail_through = data['authentication']['data'].get('fail_through', None)
+ if fail_through is not None:
+ new_data["authentication"]["data"]["fail_through"] = fail_through
+ return new_data
+
+ def get_delete_all_aaa_request(self, have):
+ requests = []
+ if "authentication" in have and have["authentication"]:
+ if "local" in have["authentication"]["data"] or "group" in have["authentication"]["data"]:
+ request = self.get_authentication_method_delete_request()
+ requests.append(request)
+ if "fail_through" in have["authentication"]["data"]:
+ request = self.get_failthrough_delete_request()
+ requests.append(request)
+ return requests
+
+ def get_authentication_method_delete_request(self):
+ path = 'data/openconfig-system:system/aaa/authentication/config/authentication-method'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ return request
+
+ def get_failthrough_delete_request(self):
+ path = 'data/openconfig-system:system/aaa/authentication/config/failthrough'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ return request
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py
new file mode 100644
index 00000000..fd4d5c57
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp/bgp.py
@@ -0,0 +1,598 @@
+#
+# -*- 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 sonic_bgp 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
+
+try:
+ from urllib import quote
+except ImportError:
+ from urllib.parse import quote
+
+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,
+ search_obj_in_list,
+ remove_empties
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ dict_to_set,
+ update_states,
+ get_diff,
+ remove_empties_from_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+POST = 'post'
+DELETE = 'delete'
+PUT = 'put'
+
+TEST_KEYS = [{'config': {'vrf_name': '', 'bgp_as': ''}}]
+
+
+class Bgp(ConfigBase):
+ """
+ The sonic_bgp class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp',
+ ]
+
+ network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+ protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp'
+ log_neighbor_changes_path = 'logging-options/config/log-neighbor-state-changes'
+ holdtime_path = 'config/hold-time'
+ keepalive_path = 'config/keepalive-interval'
+
+ def __init__(self, module):
+ super(Bgp, self).__init__(module)
+
+ def get_bgp_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)
+ bgp_facts = facts['ansible_network_resources'].get('bgp')
+ if not bgp_facts:
+ bgp_facts = []
+ return bgp_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_bgp_facts = self.get_bgp_facts()
+ commands, requests = self.set_config(existing_bgp_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_facts = self.get_bgp_facts()
+
+ result['before'] = existing_bgp_facts
+ if result['changed']:
+ result['after'] = changed_bgp_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_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_bgp_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 = []
+ requests = []
+ state = self._module.params['state']
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_bgp_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ is_delete_all = False
+ # if want is none, then delete all the bgps
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_bgp_requests(commands, have, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_delete_single_bgp_request(self, vrf_name):
+ delete_path = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ return ({'path': delete_path, 'method': DELETE})
+
+ def get_delete_max_med_requests(self, vrf_name, max_med, match):
+ requests = []
+
+ match_max_med = match.get('max_med', None)
+ if not max_med or not match_max_med:
+ return requests
+
+ generic_del_path = '%s=%s/%s/global/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+
+ match_max_med_on_startup = match.get('max_med', {}).get('on_startup')
+ if match_max_med_on_startup:
+ requests.append({'path': generic_del_path + "max-med/config/time", 'method': DELETE})
+ requests.append({'path': generic_del_path + "max-med/config/max-med-val", 'method': DELETE})
+
+ return requests
+
+ def get_delete_bestpath_requests(self, vrf_name, bestpath, match):
+ requests = []
+
+ match_bestpath = match.get('bestpath', None)
+ if not bestpath or not match_bestpath:
+ return requests
+
+ route_selection_del_path = '%s=%s/%s/global/route-selection-options/config/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ multi_paths_del_path = '%s=%s/%s/global/use-multiple-paths/ebgp/config/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ generic_del_path = '%s=%s/%s/global/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+
+ if bestpath.get('compare_routerid', None) and match_bestpath.get('compare_routerid', None):
+ url = '%s=%s/%s/global/route-selection-options' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ route_selection_cfg = {}
+ route_selection_cfg['external-compare-router-id'] = False
+ payload = {'route-selection-options': {'config': route_selection_cfg}}
+ requests.append({'path': url, 'data': payload, 'method': PATCH})
+ # requests.append({'path': route_selection_del_path + "external-compare-router-id", 'method': DELETE})
+
+ match_as_path = match_bestpath.get('as_path', None)
+ as_path = bestpath.get('as_path', None)
+ if as_path and match_as_path:
+ if as_path.get('confed', None) is not None and match_as_path.get('confed', None):
+ requests.append({'path': route_selection_del_path + "compare-confed-as-path", 'method': DELETE})
+ if as_path.get('ignore', None) is not None and match_as_path.get('ignore', None):
+ requests.append({'path': route_selection_del_path + "ignore-as-path-length", 'method': DELETE})
+ if as_path.get('multipath_relax', None) is not None and match_as_path.get('multipath_relax', None):
+ requests.append({'path': multi_paths_del_path + "allow-multiple-as", 'method': DELETE})
+ if as_path.get('multipath_relax_as_set', None) is not None and match_as_path.get('multipath_relax_as_set', None):
+ requests.append({'path': multi_paths_del_path + "as-set", 'method': DELETE})
+
+ match_med = match_bestpath.get('med', None)
+ med = bestpath.get('med', None)
+ if med and match_med:
+ if med.get('confed', None) is not None and match_med.get('confed', None):
+ requests.append({'path': route_selection_del_path + "med-confed", 'method': DELETE})
+ if med.get('missing_as_worst', None) is not None and match_med.get('missing_as_worst', None):
+ requests.append({'path': route_selection_del_path + "med-missing-as-worst", 'method': DELETE})
+ if med.get('always_compare_med', None) is not None and match_med.get('always_compare_med', None):
+ requests.append({'path': route_selection_del_path + "always-compare-med", 'method': DELETE})
+ if med.get('max_med_val', None) is not None and match_med.get('max_med_val', None):
+ requests.append({'path': generic_del_path + "max-med/config/admin-max-med-val", 'method': DELETE})
+
+ return requests
+
+ def get_delete_all_bgp_requests(self, commands):
+ requests = []
+ for cmd in commands:
+ requests.append(self.get_delete_single_bgp_request(cmd['vrf_name']))
+ return requests
+
+ def get_delete_specific_bgp_param_request(self, command, match):
+ vrf_name = command['vrf_name']
+ requests = []
+
+ router_id = command.get('router_id', None)
+ timers = command.get('timers', None)
+ holdtime = None
+ keepalive = None
+ if timers:
+ holdtime = command['timers'].get('holdtime', None)
+ keepalive = command['timers'].get('keepalive_interval', None)
+ log_neighbor_changes = command.get('log_neighbor_changes', None)
+ bestpath = command.get('bestpath', None)
+
+ if router_id and match.get('router_id', None):
+ url = '%s=%s/%s/global/config/router-id' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ requests.append({"path": url, "method": DELETE})
+
+ if holdtime and match['timers'].get('holdtime', None) != 180:
+ url = '%s=%s/%s/global/config/hold-time' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ requests.append({"path": url, "method": DELETE})
+
+ if keepalive and match['timers'].get('keepalive_interval', None) != 60:
+ url = '%s=%s/%s/global/config/keepalive-interval' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ requests.append({"path": url, "method": DELETE})
+
+ # Delete the log_neighbor_changes only when existing values is True.
+ if log_neighbor_changes is not None and match.get('log_neighbor_changes', None):
+ del_log_neighbor_req = self.get_modify_log_change_request(vrf_name, False)
+ if del_log_neighbor_req:
+ requests.append(del_log_neighbor_req)
+
+ bestpath_del_reqs = self.get_delete_bestpath_requests(vrf_name, bestpath, match)
+ if bestpath_del_reqs:
+ requests.extend(bestpath_del_reqs)
+
+ max_med = command.get('max_med', None)
+ max_med_del_reqs = self.get_delete_max_med_requests(vrf_name, max_med, match)
+ if max_med_del_reqs:
+ requests.extend(max_med_del_reqs)
+
+ return requests
+
+ def get_delete_bgp_requests(self, commands, have, is_delete_all):
+ requests = []
+ if is_delete_all:
+ requests = self.get_delete_all_bgp_requests(commands)
+ else:
+ for cmd in commands:
+ vrf_name = cmd['vrf_name']
+ as_val = cmd['bgp_as']
+
+ match = next((cfg for cfg in have if cfg['vrf_name'] == vrf_name and cfg['bgp_as'] == as_val), None)
+ if not match:
+ continue
+ # if there is specific parameters to delete then delete those alone
+ if cmd.get('router_id', None) or cmd.get('log_neighbor_changes', None) or cmd.get('bestpath', None):
+ requests.extend(self.get_delete_specific_bgp_param_request(cmd, match))
+ else:
+ # delete entire bgp
+ requests.append(self.get_delete_single_bgp_request(vrf_name))
+
+ if requests:
+ # reorder the requests to get default vrfs at end of the requests. so deletion will get success
+ default_vrf_reqs = []
+ other_vrf_reqs = []
+ for req in requests:
+ if '=default/' in req['path']:
+ default_vrf_reqs.append(req)
+ else:
+ other_vrf_reqs.append(req)
+ requests.clear()
+ requests.extend(other_vrf_reqs)
+ requests.extend(default_vrf_reqs)
+
+ return requests
+
+ def get_modify_multi_paths_req(self, vrf_name, as_path):
+ request = None
+ if not as_path:
+ return request
+
+ method = PATCH
+ multipath_cfg = {}
+
+ as_path_multipath_relax = as_path.get('multipath_relax', None)
+ as_path_multipath_relax_as_set = as_path.get('multipath_relax_as_set', None)
+
+ if as_path_multipath_relax is not None:
+ multipath_cfg['allow-multiple-as'] = as_path_multipath_relax
+ if as_path_multipath_relax_as_set is not None:
+ multipath_cfg['as-set'] = as_path_multipath_relax_as_set
+
+ payload = {"openconfig-network-instance:config": multipath_cfg}
+ if payload:
+ url = '%s=%s/%s/global/use-multiple-paths/ebgp/config' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ request = {"path": url, "method": method, "data": payload}
+
+ return request
+
+ def get_modify_route_selection_req(self, vrf_name, compare_routerid, as_path, med):
+ requests = []
+ if compare_routerid is None and not as_path and not med:
+ return requests
+
+ route_selection_cfg = {}
+
+ as_path_confed = None
+ as_path_ignore = None
+
+ med_confed = None
+ med_missing_as_worst = None
+ always_compare_med = None
+
+ if compare_routerid is not None:
+ route_selection_cfg['external-compare-router-id'] = compare_routerid
+
+ if as_path:
+ as_path_confed = as_path.get('confed', None)
+ as_path_ignore = as_path.get('ignore', None)
+ if as_path_confed is not None:
+ route_selection_cfg['compare-confed-as-path'] = as_path_confed
+ if as_path_ignore is not None:
+ route_selection_cfg['ignore-as-path-length'] = as_path_ignore
+
+ if med:
+ med_confed = med.get('confed', None)
+ med_missing_as_worst = med.get('missing_as_worst', None)
+ always_compare_med = med.get('always_compare_med', None)
+ if med_confed is not None:
+ route_selection_cfg['med-confed'] = med_confed
+ if med_missing_as_worst is not None:
+ route_selection_cfg['med-missing-as-worst'] = med_missing_as_worst
+ if always_compare_med is not None:
+ route_selection_cfg['always-compare-med'] = always_compare_med
+ method = PATCH
+ payload = {'route-selection-options': {'config': route_selection_cfg}}
+
+ if payload:
+ url = '%s=%s/%s/global/route-selection-options' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_modify_bestpath_requests(self, vrf_name, bestpath):
+ requests = []
+ if not bestpath:
+ return requests
+
+ compare_routerid = bestpath.get('compare_routerid', None)
+ as_path = bestpath.get('as_path', None)
+ med = bestpath.get('med', None)
+
+ route_selection_req = self.get_modify_route_selection_req(vrf_name, compare_routerid, as_path, med)
+ if route_selection_req:
+ requests.extend(route_selection_req)
+
+ multi_paths_req = self.get_modify_multi_paths_req(vrf_name, as_path)
+ if multi_paths_req:
+ requests.append(multi_paths_req)
+
+ return requests
+
+ def get_modify_max_med_requests(self, vrf_name, max_med):
+ request = None
+ method = PATCH
+ payload = {}
+ on_startup_time = max_med.get('on_startup', {}).get('timer')
+ on_startup_med = max_med.get('on_startup', {}).get('med_val')
+
+ if on_startup_med is not None:
+ payload = {
+ 'max-med': {
+ 'config': {
+ 'max-med-val': on_startup_med,
+ 'time': on_startup_time
+ }
+ }
+ }
+
+ if payload:
+ url = '%s=%s/%s/global/max-med' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ request = {"path": url, "method": method, "data": payload}
+
+ return [request]
+
+ def get_modify_log_change_request(self, vrf_name, log_neighbor_changes):
+ request = None
+ method = PATCH
+ payload = {}
+
+ if log_neighbor_changes is not None:
+ payload['log-neighbor-state-changes'] = log_neighbor_changes
+
+ if payload:
+ url = '%s=%s/%s/global/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.log_neighbor_changes_path)
+ request = {"path": url, "method": method, "data": payload}
+
+ return request
+
+ def get_modify_holdtime_request(self, vrf_name, holdtime):
+ request = None
+ method = PATCH
+ payload = {}
+
+ if holdtime is not None:
+ payload['hold-time'] = str(holdtime)
+
+ if payload:
+ url = '%s=%s/%s/global/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.holdtime_path)
+ request = {"path": url, "method": method, "data": payload}
+
+ return request
+
+ def get_modify_keepalive_request(self, vrf_name, keepalive_interval):
+ request = None
+ method = PATCH
+ payload = {}
+
+ if keepalive_interval is not None:
+ payload['keepalive-interval'] = str(keepalive_interval)
+
+ if payload:
+ url = '%s=%s/%s/global/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.keepalive_path)
+ request = {"path": url, "method": method, "data": payload}
+
+ return request
+
+ def get_new_bgp_request(self, vrf_name, as_val):
+ request = None
+ url = None
+ method = PATCH
+ payload = {}
+
+ cfg = {}
+ if as_val:
+ as_cfg = {'config': {'as': float(as_val)}}
+ global_cfg = {'global': as_cfg}
+ cfg = {'bgp': global_cfg}
+ cfg['name'] = "bgp"
+ cfg['identifier'] = "openconfig-policy-types:BGP"
+
+ if cfg:
+ payload['openconfig-network-instance:protocol'] = [cfg]
+ url = '%s=%s/protocols/protocol/' % (self.network_instance_path, vrf_name)
+ request = {"path": url, "method": method, "data": payload}
+
+ return request
+
+ def get_modify_global_config_request(self, vrf_name, router_id, as_val):
+ request = None
+ method = PATCH
+ payload = {}
+
+ cfg = {}
+ if router_id:
+ cfg['router-id'] = router_id
+ if as_val:
+ cfg['as'] = float(as_val)
+
+ if cfg:
+ payload['openconfig-network-instance:config'] = cfg
+ url = '%s=%s/%s/global/config' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ request = {"path": url, "method": method, "data": payload}
+
+ return request
+
+ def get_modify_bgp_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ # Create URL and payload
+ for conf in commands:
+ vrf_name = conf['vrf_name']
+ as_val = None
+ router_id = None
+ log_neighbor_changes = None
+ bestpath = None
+ max_med = None
+ holdtime = None
+ keepalive_interval = None
+
+ if 'bgp_as' in conf:
+ as_val = conf['bgp_as']
+ if 'router_id' in conf:
+ router_id = conf['router_id']
+ if 'log_neighbor_changes' in conf:
+ log_neighbor_changes = conf['log_neighbor_changes']
+ if 'bestpath' in conf:
+ bestpath = conf['bestpath']
+ if 'max_med' in conf:
+ max_med = conf['max_med']
+ if 'timers' in conf and conf['timers']:
+ if 'holdtime' in conf['timers']:
+ holdtime = conf['timers']['holdtime']
+ if 'keepalive_interval' in conf['timers']:
+ keepalive_interval = conf['timers']['keepalive_interval']
+
+ if not any(cfg for cfg in have if cfg['vrf_name'] == vrf_name and (cfg['bgp_as'] == as_val)):
+ new_bgp_req = self.get_new_bgp_request(vrf_name, as_val)
+ if new_bgp_req:
+ requests.append(new_bgp_req)
+
+ global_req = self.get_modify_global_config_request(vrf_name, router_id, as_val)
+ if global_req:
+ requests.append(global_req)
+
+ log_neighbor_changes_req = self.get_modify_log_change_request(vrf_name, log_neighbor_changes)
+ if log_neighbor_changes_req:
+ requests.append(log_neighbor_changes_req)
+
+ if holdtime:
+ holdtime_req = self.get_modify_holdtime_request(vrf_name, holdtime)
+ if holdtime_req:
+ requests.append(holdtime_req)
+
+ if keepalive_interval:
+ keepalive_req = self.get_modify_keepalive_request(vrf_name, keepalive_interval)
+ if keepalive_req:
+ requests.append(keepalive_req)
+
+ bestpath_reqs = self.get_modify_bestpath_requests(vrf_name, bestpath)
+ if bestpath_reqs:
+ requests.extend(bestpath_reqs)
+ if max_med:
+ max_med_reqs = self.get_modify_max_med_requests(vrf_name, max_med)
+ if max_med_reqs:
+ requests.extend(max_med_reqs)
+
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py
new file mode 100644
index 00000000..2a5c4cfc
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_af/bgp_af.py
@@ -0,0 +1,848 @@
+#
+# -*- 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 sonic_bgp_af 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
+
+try:
+ from urllib import quote
+except ImportError:
+ from urllib.parse import quote
+
+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,
+ search_obj_in_list,
+ remove_empties
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ dict_to_set,
+ update_states,
+ get_diff,
+ remove_empties_from_list,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ validate_bgps,
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'delete'
+TEST_KEYS = [
+ {'config': {'vrf_name': '', 'bgp_as': ''}},
+ {'afis': {'afi': '', 'safi': ''}},
+ {'redistribute': {'protocol': ''}},
+ {'route_advertise_list': {'advertise_afi': ''}}
+]
+
+
+class Bgp_af(ConfigBase):
+ """
+ The sonic_bgp_af class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp_af',
+ ]
+
+ network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+ protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp'
+ l2vpn_evpn_config_path = 'l2vpn-evpn/openconfig-bgp-evpn-ext:config'
+ l2vpn_evpn_route_advertise_path = 'l2vpn-evpn/openconfig-bgp-evpn-ext:route-advertise'
+ afi_safi_path = 'global/afi-safis/afi-safi'
+ table_connection_path = 'table-connections/table-connection'
+
+ def __init__(self, module):
+ super(Bgp_af, self).__init__(module)
+
+ def get_bgp_af_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)
+ bgp_af_facts = facts['ansible_network_resources'].get('bgp_af')
+ if not bgp_af_facts:
+ bgp_af_facts = []
+ return bgp_af_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_bgp_af_facts = self.get_bgp_af_facts()
+ commands, requests = self.set_config(existing_bgp_af_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_af_facts = self.get_bgp_af_facts()
+
+ result['before'] = existing_bgp_af_facts
+ if result['changed']:
+ result['after'] = changed_bgp_af_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_af_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_bgp_af_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 = []
+ requests = []
+ state = self._module.params['state']
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ validate_bgps(self._module, commands, have)
+ requests = self.get_modify_bgp_af_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the bgp_afs
+ is_delete_all = False
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_bgp_af_requests(commands, have, is_delete_all)
+ requests.extend(self.get_delete_route_advertise_requests(commands, have, is_delete_all))
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+ return commands, requests
+
+ def get_modify_address_family_request(self, vrf_name, conf_afi, conf_safi):
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/global' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ afi_safi_load = {'afi-safi-name': ("openconfig-bgp-types:%s" % (afi_safi))}
+ afi_safis_load = {'afi-safis': {'afi-safi': [afi_safi_load]}}
+ pay_load = {'openconfig-network-instance:global': afi_safis_load}
+
+ return ({"path": url, "method": PATCH, "data": pay_load})
+
+ def get_modify_advertise_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam):
+ request = None
+ conf_adv_pip = conf_addr_fam.get('advertise_pip', None)
+ conf_adv_pip_ip = conf_addr_fam.get('advertise_pip_ip', None)
+ conf_adv_pip_peer_ip = conf_addr_fam.get('advertise_pip_peer_ip', None)
+ conf_adv_svi_ip = conf_addr_fam.get('advertise_svi_ip', None)
+ conf_adv_all_vni = conf_addr_fam.get('advertise_all_vni', None)
+ conf_adv_default_gw = conf_addr_fam.get('advertise_default_gw', None)
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ evpn_cfg = {}
+
+ if conf_adv_pip:
+ evpn_cfg['advertise-pip'] = conf_adv_pip
+
+ if conf_adv_pip_ip:
+ evpn_cfg['advertise-pip-ip'] = conf_adv_pip_ip
+
+ if conf_adv_pip_peer_ip:
+ evpn_cfg['advertise-pip-peer-ip'] = conf_adv_pip_peer_ip
+
+ if conf_adv_svi_ip:
+ evpn_cfg['advertise-svi-ip'] = conf_adv_svi_ip
+
+ if conf_adv_all_vni:
+ evpn_cfg['advertise-all-vni'] = conf_adv_all_vni
+
+ if conf_adv_default_gw:
+ evpn_cfg['advertise-default-gw'] = conf_adv_default_gw
+
+ if evpn_cfg:
+ url = '%s=%s/%s/global' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ afi_safi_load = {'afi-safi-name': ("openconfig-bgp-types:%s" % (afi_safi))}
+ afi_safi_load['l2vpn-evpn'] = {'openconfig-bgp-evpn-ext:config': evpn_cfg}
+ afi_safis_load = {'afi-safis': {'afi-safi': [afi_safi_load]}}
+ pay_load = {'openconfig-network-instance:global': afi_safis_load}
+ request = {"path": url, "method": PATCH, "data": pay_load}
+
+ return request
+
+ def get_modify_route_advertise_list_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam):
+ request = []
+ route_advertise = []
+ afi_safi = ('%s_%s' % (conf_afi, conf_safi)).upper()
+ route_advertise_list = conf_addr_fam.get('route_advertise_list', [])
+ if route_advertise_list:
+ for rt_adv in route_advertise_list:
+ advertise_afi = rt_adv.get('advertise_afi', None)
+ route_map = rt_adv.get('route_map', None)
+ if advertise_afi:
+ advertise_afi_safi = '%s_UNICAST' % advertise_afi.upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=%s/%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_route_advertise_path)
+ cfg = None
+ if route_map:
+ route_map_list = [route_map]
+ cfg = {'advertise-afi-safi': advertise_afi_safi, 'route-map': route_map_list}
+ else:
+ cfg = {'advertise-afi-safi': advertise_afi_safi}
+ route_advertise.append({'advertise-afi-safi': advertise_afi_safi, 'config': cfg})
+ pay_load = {'openconfig-bgp-evpn-ext:route-advertise': {'route-advertise-list': route_advertise}}
+ request = {"path": url, "method": PATCH, "data": pay_load}
+ return request
+
+ def get_modify_redistribute_requests(self, vrf_name, conf_afi, conf_safi, conf_redis_arr):
+ requests = []
+ url = "%s=%s/table-connections" % (self.network_instance_path, vrf_name)
+ cfgs = []
+ for conf_redis in conf_redis_arr:
+ conf_metric = conf_redis.get('metric', None)
+ if conf_metric is not None:
+ conf_metric = float(conf_redis['metric'])
+
+ afi_cfg = "openconfig-types:%s" % (conf_afi.upper())
+ cfg_data = {'address-family': afi_cfg}
+ cfg_data['dst-protocol'] = "openconfig-policy-types:BGP"
+ conf_protocol = conf_redis['protocol'].upper()
+ if conf_protocol == 'CONNECTED':
+ conf_protocol = "DIRECTLY_CONNECTED"
+ cfg_data['src-protocol'] = "openconfig-policy-types:%s" % (conf_protocol)
+ cfg_data['config'] = {'address-family': afi_cfg}
+ if conf_metric is not None:
+ cfg_data['config']['metric'] = conf_metric
+
+ conf_route_map = conf_redis.get('route_map', None)
+ if conf_route_map:
+ cfg_data['config']['import-policy'] = [conf_route_map]
+
+ cfgs.append(cfg_data)
+
+ if cfgs:
+ pay_load = {'openconfig-network-instance:table-connections': {'table-connection': cfgs}}
+ requests.append({"path": url, "method": PATCH, "data": pay_load})
+ return requests
+
+ def get_modify_max_path_request(self, vrf_name, conf_afi, conf_safi, conf_max_path):
+ request = None
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '%s=%s/use-multiple-paths' % (self.afi_safi_path, afi_safi)
+ conf_ebgp = conf_max_path.get('ebgp', None)
+ conf_ibgp = conf_max_path.get('ibgp', None)
+ max_path_load = {}
+ if conf_ebgp:
+ max_path_load['ebgp'] = {'config': {'maximum-paths': conf_ebgp}}
+ if conf_ibgp:
+ max_path_load['ibgp'] = {'config': {'maximum-paths': conf_ibgp}}
+
+ pay_load = {}
+ if max_path_load:
+ pay_load['openconfig-network-instance:use-multiple-paths'] = max_path_load
+
+ request = {"path": url, "method": PATCH, "data": pay_load}
+ return request
+
+ def get_modify_network_request(self, vrf_name, conf_afi, conf_safi, conf_network):
+ request = None
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '%s=%s/network-config' % (self.afi_safi_path, afi_safi)
+ network_payload = []
+ for each in conf_network:
+ payload = {}
+ payload = {'config': {'prefix': each}, 'prefix': each}
+ network_payload.append(payload)
+ if network_payload:
+ new_payload = {'network-config': {'network': network_payload}}
+
+ request = {"path": url, "method": PATCH, "data": new_payload}
+ return request
+
+ def get_modify_dampening_request(self, vrf_name, conf_afi, conf_safi, conf_dampening):
+ request = None
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '%s=%s/route-flap-damping' % (self.afi_safi_path, afi_safi)
+ damp_payload = {'route-flap-damping': {'config': {'enabled': conf_dampening}}}
+ if damp_payload:
+ request = {"path": url, "method": PATCH, "data": damp_payload}
+ return request
+
+ def get_modify_single_af_request(self, vrf_name, conf_afi, conf_safi, conf_addr_fam):
+ requests = []
+
+ requests.append(self.get_modify_address_family_request(vrf_name, conf_afi, conf_safi))
+ if conf_afi == 'ipv4' and conf_safi == 'unicast':
+ conf_dampening = conf_addr_fam.get('dampening', None)
+ if conf_dampening:
+ request = self.get_modify_dampening_request(vrf_name, conf_afi, conf_safi, conf_dampening)
+ if request:
+ requests.append(request)
+ if conf_afi in ['ipv4', 'ipv6'] and conf_safi == 'unicast':
+ conf_redis_arr = conf_addr_fam.get('redistribute', [])
+ if conf_redis_arr:
+ requests.extend(self.get_modify_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr))
+ conf_max_path = conf_addr_fam.get('max_path', None)
+ if conf_max_path:
+ request = self.get_modify_max_path_request(vrf_name, conf_afi, conf_safi, conf_max_path)
+ if request:
+ requests.append(request)
+ conf_network = conf_addr_fam.get('network', [])
+ if conf_network:
+ request = self.get_modify_network_request(vrf_name, conf_afi, conf_safi, conf_network)
+ if request:
+ requests.append(request)
+ elif conf_afi == "l2vpn" and conf_safi == 'evpn':
+ adv_req = self.get_modify_advertise_request(vrf_name, conf_afi, conf_safi, conf_addr_fam)
+ if adv_req:
+ requests.append(adv_req)
+ return requests
+
+ def get_modify_all_af_requests(self, conf_addr_fams, vrf_name):
+ requests = []
+ for conf_addr_fam in conf_addr_fams:
+ conf_afi = conf_addr_fam.get('afi', None)
+ conf_safi = conf_addr_fam.get('safi', None)
+ if conf_afi and conf_safi:
+ requests.extend(self.get_modify_single_af_request(vrf_name, conf_afi, conf_safi, conf_addr_fam))
+ return requests
+
+ def get_modify_requests(self, conf, match, vrf_name):
+ requests = []
+ payload = {}
+ conf_addr_fams = conf.get('address_family', None)
+ if conf_addr_fams:
+ conf_addr_fams = conf_addr_fams.get('afis', [])
+
+ mat_addr_fams = []
+ if match:
+ mat_addr_fams = match.get('address_family', None)
+ if mat_addr_fams:
+ mat_addr_fams = mat_addr_fams.get('afis', [])
+
+ if conf_addr_fams and not mat_addr_fams:
+ requests.extend(self.get_modify_all_af_requests(conf_addr_fams, vrf_name))
+ else:
+ for conf_addr_fam in conf_addr_fams:
+ conf_afi = conf_addr_fam.get('afi', None)
+ conf_safi = conf_addr_fam.get('safi', None)
+
+ if conf_afi is None or conf_safi is None:
+ continue
+
+ mat_addr_fam = next((e_addr_fam for e_addr_fam in mat_addr_fams if (e_addr_fam['afi'] == conf_afi and e_addr_fam['safi'] == conf_safi)), None)
+
+ if mat_addr_fam is None:
+ requests.extend(self.get_modify_single_af_request(vrf_name, conf_afi, conf_safi, conf_addr_fam))
+ continue
+
+ if conf_afi == 'ipv4' and conf_safi == 'unicast':
+ conf_dampening = conf_addr_fam.get('dampening', None)
+ if conf_dampening:
+ request = self.get_modify_dampening_request(vrf_name, conf_afi, conf_safi, conf_dampening)
+ if request:
+ requests.append(request)
+
+ if conf_afi == "l2vpn" and conf_safi == "evpn":
+ adv_req = self.get_modify_advertise_request(vrf_name, conf_afi, conf_safi, conf_addr_fam)
+ rt_adv_req = self.get_modify_route_advertise_list_request(vrf_name, conf_afi, conf_safi, conf_addr_fam)
+ if adv_req:
+ requests.append(adv_req)
+ if rt_adv_req:
+ requests.append(rt_adv_req)
+
+ elif conf_afi in ["ipv4", "ipv6"] and conf_safi == "unicast":
+ conf_redis_arr = conf_addr_fam.get('redistribute', [])
+ conf_max_path = conf_addr_fam.get('max_path', None)
+ conf_network = conf_addr_fam.get('network', [])
+ if not conf_redis_arr and not conf_max_path and not conf_network:
+ continue
+
+ url = "%s=%s/table-connections" % (self.network_instance_path, vrf_name)
+ pay_loads = []
+ modify_redis_arr = []
+ for conf_redis in conf_redis_arr:
+ conf_metric = conf_redis.get('metric', None)
+ if conf_metric is not None:
+ conf_metric = float(conf_redis['metric'])
+
+ conf_route_map = conf_redis.get('route_map', None)
+
+ have_redis_arr = mat_addr_fam.get('redistribute', [])
+ have_redis = None
+ have_route_map = None
+ # Check the route_map, if existing route_map is different from required route_map, delete the existing route map
+ if conf_route_map and have_redis_arr:
+ have_redis = next((redis_cfg for redis_cfg in have_redis_arr if conf_redis['protocol'] == redis_cfg['protocol']), None)
+ if have_redis:
+ have_route_map = have_redis.get('route_map', None)
+ if have_route_map and have_route_map != conf_route_map:
+ requests.append(self.get_delete_route_map_request(vrf_name, conf_afi, have_redis, have_route_map))
+
+ modify_redis = {}
+ if conf_metric is not None:
+ modify_redis['metric'] = conf_metric
+ if conf_route_map:
+ modify_redis['route_map'] = conf_route_map
+
+ if modify_redis:
+ modify_redis['protocol'] = conf_redis['protocol']
+ modify_redis_arr.append(modify_redis)
+
+ if modify_redis_arr:
+ requests.extend(self.get_modify_redistribute_requests(vrf_name, conf_afi, conf_safi, modify_redis_arr))
+ if conf_max_path:
+ max_path_req = self.get_modify_max_path_request(vrf_name, conf_afi, conf_safi, conf_max_path)
+ if max_path_req:
+ requests.append(max_path_req)
+
+ if conf_network:
+ network_req = self.get_modify_network_request(vrf_name, conf_afi, conf_safi, conf_network)
+ if network_req:
+ requests.append(network_req)
+
+ return requests
+
+ def get_modify_bgp_af_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ # Create URL and payload
+ for conf in commands:
+ vrf_name = conf['vrf_name']
+ as_val = conf['bgp_as']
+
+ match = next((cfg for cfg in have if (cfg['vrf_name'] == vrf_name and (cfg['bgp_as'] == as_val))), None)
+ modify_reqs = self.get_modify_requests(conf, match, vrf_name)
+ if modify_reqs:
+ requests.extend(modify_reqs)
+
+ return requests
+
+ def get_delete_advertise_attribute_request(self, vrf_name, conf_afi, conf_safi, attr):
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=%s/%s/%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_config_path, attr)
+
+ return ({"path": url, "method": DELETE})
+
+ def get_delete_route_advertise_request(self, vrf_name, conf_afi, conf_safi):
+ afi_safi = ('%s_%s' % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=%s/%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_route_advertise_path)
+
+ return ({'path': url, 'method': DELETE})
+
+ def get_delete_route_advertise_list_request(self, vrf_name, conf_afi, conf_safi, advertise_afi):
+ afi_safi = ('%s_%s' % (conf_afi, conf_safi)).upper()
+ advertise_afi_safi = '%s_UNICAST' % advertise_afi.upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=%s/%s/route-advertise-list=%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_route_advertise_path, advertise_afi_safi)
+
+ return ({'path': url, 'method': DELETE})
+
+ def get_delete_route_advertise_route_map_request(self, vrf_name, conf_afi, conf_safi, advertise_afi, route_map):
+ afi_safi = ('%s_%s' % (conf_afi, conf_safi)).upper()
+ advertise_afi_safi = '%s_UNICAST' % advertise_afi.upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=%s/%s/route-advertise-list=%s' % (self.afi_safi_path, afi_safi, self.l2vpn_evpn_route_advertise_path, advertise_afi_safi)
+ url += '/config/route-map=%s' % route_map
+
+ return ({'path': url, 'method': DELETE})
+
+ def get_delete_route_advertise_requests(self, commands, have, is_delete_all):
+ requests = []
+ if not is_delete_all:
+ for cmd in commands:
+ vrf_name = cmd['vrf_name']
+ addr_fams = cmd.get('address_family', None)
+ if addr_fams:
+ addr_fams = addr_fams.get('afis', [])
+ if not addr_fams:
+ return requests
+ for addr_fam in addr_fams:
+ afi = addr_fam.get('afi', None)
+ safi = addr_fam.get('safi', None)
+ route_advertise_list = addr_fam.get('route_advertise_list', [])
+ if route_advertise_list:
+ for rt_adv in route_advertise_list:
+ advertise_afi = rt_adv.get('advertise_afi', None)
+ route_map = rt_adv.get('route_map', None)
+ # Check if the commands to be deleted are configured
+ for conf in have:
+ conf_vrf_name = conf['vrf_name']
+ conf_addr_fams = conf.get('address_family', None)
+ if conf_addr_fams:
+ conf_addr_fams = conf_addr_fams.get('afis', [])
+ for conf_addr_fam in conf_addr_fams:
+ conf_afi = conf_addr_fam.get('afi', None)
+ conf_safi = conf_addr_fam.get('safi', None)
+ conf_route_advertise_list = conf_addr_fam.get('route_advertise_list', [])
+ if conf_route_advertise_list:
+ for conf_rt_adv in conf_route_advertise_list:
+ conf_advertise_afi = conf_rt_adv.get('advertise_afi', None)
+ conf_route_map = conf_rt_adv.get('route_map', None)
+ # Deletion at route-advertise level
+ if (not advertise_afi and vrf_name == conf_vrf_name and afi == conf_afi and safi == conf_safi):
+ requests.append(self.get_delete_route_advertise_request(vrf_name, afi, safi))
+ # Deletion at advertise-afi-safi level
+ if (advertise_afi and not route_map and vrf_name == conf_vrf_name and afi == conf_afi and safi ==
+ conf_safi and advertise_afi == conf_advertise_afi):
+ requests.append(self.get_delete_route_advertise_list_request(vrf_name, afi, safi, advertise_afi))
+ # Deletion at route-map level
+ if (route_map and vrf_name == conf_vrf_name and afi == conf_afi and safi == conf_safi
+ and advertise_afi == conf_advertise_afi and route_map == conf_route_map):
+ requests.append(self.get_delete_route_advertise_route_map_request(vrf_name, afi, safi,
+ advertise_afi, route_map))
+
+ return requests
+
+ def get_delete_dampening_request(self, vrf_name, conf_afi, conf_safi):
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=%s/route-flap-damping/config/enabled' % (self.afi_safi_path, afi_safi)
+
+ return ({"path": url, "method": DELETE})
+
+ def get_delete_address_family_request(self, vrf_name, conf_afi, conf_safi):
+ request = None
+
+ if conf_afi != "l2vpn":
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '/%s=openconfig-bgp-types:%s' % (self.afi_safi_path, afi_safi)
+ request = {"path": url, "method": DELETE}
+
+ return request
+
+ def get_delete_single_bgp_af_request(self, conf, is_delete_all, match=None):
+ requests = []
+ vrf_name = conf['vrf_name']
+
+ conf_addr_fams = conf.get('address_family', None)
+ if conf_addr_fams is None:
+ return requests
+
+ conf_addr_fams = conf_addr_fams.get('afis', [])
+
+ if match and not conf_addr_fams:
+ conf_addr_fams = match.get('address_family', None)
+ if conf_addr_fams:
+ conf_addr_fams = conf_addr_fams.get('afis', [])
+ conf_addr_fams = [{'afi': af['afi'], 'safi': af['safi']} for af in conf_addr_fams]
+
+ if not conf_addr_fams:
+ return requests
+
+ for conf_addr_fam in conf_addr_fams:
+ conf_afi = conf_addr_fam.get('afi', None)
+ conf_safi = conf_addr_fam.get('safi', None)
+ if not conf_afi or not conf_safi:
+ continue
+ conf_redis_arr = conf_addr_fam.get('redistribute', [])
+ conf_adv_pip = conf_addr_fam.get('advertise_pip', None)
+ conf_adv_pip_ip = conf_addr_fam.get('advertise_pip_ip', None)
+ conf_adv_pip_peer_ip = conf_addr_fam.get('advertise_pip_peer_ip', None)
+ conf_adv_svi_ip = conf_addr_fam.get('advertise_svi_ip', None)
+ conf_adv_all_vni = conf_addr_fam.get('advertise_all_vni', None)
+ conf_adv_default_gw = conf_addr_fam.get('advertise_default_gw', None)
+ conf_max_path = conf_addr_fam.get('max_path', None)
+ conf_dampening = conf_addr_fam.get('dampening', None)
+ conf_network = conf_addr_fam.get('network', [])
+ if is_delete_all:
+ if conf_adv_pip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip'))
+ if conf_adv_pip_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-ip'))
+ if conf_adv_pip_peer_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-peer-ip'))
+ if conf_adv_svi_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-svi-ip'))
+ if conf_adv_all_vni:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-all-vni'))
+ if conf_dampening:
+ requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi))
+ if conf_network:
+ requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, conf_network, is_delete_all, None))
+ if conf_adv_default_gw:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-default-gw'))
+ if conf_redis_arr:
+ requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr, is_delete_all, None))
+ if conf_max_path:
+ requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, None))
+ addr_family_del_req = self.get_delete_address_family_request(vrf_name, conf_afi, conf_safi)
+ if addr_family_del_req:
+ requests.append(addr_family_del_req)
+ elif match:
+ match_addr_fams = match.get('address_family', None)
+ if match_addr_fams:
+ match_addr_fams = match_addr_fams.get('afis', [])
+ if not match_addr_fams:
+ continue
+ for match_addr_fam in match_addr_fams:
+ mat_afi = match_addr_fam.get('afi', None)
+ mat_safi = match_addr_fam.get('safi', None)
+ if mat_afi and mat_safi and mat_afi == conf_afi and mat_safi == conf_safi:
+ mat_advt_pip = match_addr_fam.get('advertise_pip', None)
+ mat_advt_pip_ip = match_addr_fam.get('advertise_pip_ip', None)
+ mat_advt_pip_peer_ip = match_addr_fam.get('advertise_pip_peer_ip', None)
+ mat_advt_svi_ip = match_addr_fam.get('advertise_svi_ip', None)
+ mat_advt_all_vni = match_addr_fam.get('advertise_all_vni', None)
+ mat_redis_arr = match_addr_fam.get('redistribute', [])
+ mat_advt_defaut_gw = match_addr_fam.get('advertise_default_gw', None)
+ mat_max_path = match_addr_fam.get('max_path', None)
+ mat_dampening = match_addr_fam.get('dampening', None)
+ mat_network = match_addr_fam.get('network', [])
+
+ if (conf_adv_pip is None and conf_adv_pip_ip is None and conf_adv_pip_peer_ip is None and conf_adv_svi_ip is None
+ and conf_adv_all_vni is None and not conf_redis_arr and conf_adv_default_gw is None
+ and not conf_max_path and conf_dampening is None and not conf_network):
+ if mat_advt_pip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip'))
+ if mat_advt_pip_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-ip'))
+ if mat_advt_pip_peer_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-peer-ip'))
+ if mat_advt_svi_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-svi-ip'))
+ if mat_advt_all_vni is not None:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-all-vni'))
+ if mat_dampening is not None:
+ requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi))
+ if mat_advt_defaut_gw:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-default-gw'))
+ if mat_redis_arr:
+ requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, mat_redis_arr, False, mat_redis_arr))
+ if mat_max_path:
+ requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, mat_max_path, is_delete_all, mat_max_path))
+ if mat_network:
+ requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, mat_network, False, mat_network))
+ addr_family_del_req = self.get_delete_address_family_request(vrf_name, conf_afi, conf_safi)
+ if addr_family_del_req:
+ requests.append(addr_family_del_req)
+ else:
+ if conf_adv_pip and mat_advt_pip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip'))
+ if conf_adv_pip_ip and mat_advt_pip_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-ip'))
+ if conf_adv_pip_peer_ip and mat_advt_pip_peer_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-pip-peer-ip'))
+ if conf_adv_svi_ip and mat_advt_svi_ip:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-svi-ip'))
+ if conf_adv_all_vni and mat_advt_all_vni:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-all-vni'))
+ if conf_dampening and mat_dampening:
+ requests.append(self.get_delete_dampening_request(vrf_name, conf_afi, conf_safi))
+ if conf_adv_default_gw and mat_advt_defaut_gw:
+ requests.append(self.get_delete_advertise_attribute_request(vrf_name, conf_afi, conf_safi, 'advertise-default-gw'))
+ if conf_redis_arr and mat_redis_arr:
+ requests.extend(self.get_delete_redistribute_requests(vrf_name, conf_afi, conf_safi, conf_redis_arr, False, mat_redis_arr))
+ if conf_max_path and mat_max_path:
+ requests.extend(self.get_delete_max_path_requests(vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, mat_max_path))
+ if conf_network and mat_network:
+ requests.extend(self.get_delete_network_request(vrf_name, conf_afi, conf_safi, conf_network, False, mat_network))
+ break
+
+ return requests
+
+ def get_delete_network_request(self, vrf_name, conf_afi, conf_safi, conf_network, is_delete_all, mat_network):
+ requests = []
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '%s=%s/network-config/network=' % (self.afi_safi_path, afi_safi)
+ mat_list = []
+ for conf in conf_network:
+ if mat_network:
+ mat_prefix = next((pre for pre in mat_network if pre == conf), None)
+ if mat_prefix:
+ mat_list.append(mat_prefix)
+ if not is_delete_all and mat_list:
+ for each in mat_list:
+ tmp = each.replace('/', '%2f')
+ requests.append({'path': url + tmp, 'method': DELETE})
+ elif is_delete_all:
+ for each in conf_network:
+ tmp = each.replace('/', '%2f')
+ requests.append({'path': url + tmp, 'method': DELETE})
+ return requests
+
+ def get_delete_max_path_requests(self, vrf_name, conf_afi, conf_safi, conf_max_path, is_delete_all, mat_max_path):
+ requests = []
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ url += '%s=%s/use-multiple-paths/' % (self.afi_safi_path, afi_safi)
+
+ conf_ebgp = conf_max_path.get('ebgp', None)
+ conf_ibgp = conf_max_path.get('ibgp', None)
+ mat_ebgp = None
+ mat_ibgp = None
+ if mat_max_path:
+ mat_ebgp = mat_max_path.get('ebgp', None)
+ mat_ibgp = mat_max_path.get('ibgp', None)
+
+ if (conf_ebgp and mat_ebgp) or is_delete_all:
+ requests.append({'path': url + 'ebgp', 'method': DELETE})
+ if (conf_ibgp and mat_ibgp) or is_delete_all:
+ requests.append({'path': url + 'ibgp', 'method': DELETE})
+
+ return requests
+
+ def get_delete_route_map_request(self, vrf_name, conf_afi, conf_redis, conf_route_map):
+ addr_family = "openconfig-types:%s" % (conf_afi.upper())
+ conf_protocol = conf_redis['protocol'].upper()
+ if conf_protocol == 'CONNECTED':
+ conf_protocol = "DIRECTLY_CONNECTED"
+ src_protocol = "openconfig-policy-types:%s" % (conf_protocol)
+ dst_protocol = "openconfig-policy-types:BGP"
+ url = '%s=%s/%s=' % (self.network_instance_path, vrf_name, self.table_connection_path)
+ url += '%s,%s,%s/config/import-policy=%s' % (src_protocol, dst_protocol, addr_family, conf_route_map)
+ return ({'path': url, 'method': DELETE})
+
+ def get_delete_redistribute_requests(self, vrf_name, conf_afi, conf_safi, conf_redis_arr, is_delete_all, mat_redis_arr):
+ requests = []
+ for conf_redis in conf_redis_arr:
+ addr_family = "openconfig-types:%s" % (conf_afi.upper())
+ conf_protocol = conf_redis['protocol'].upper()
+
+ ext_metric_flag = False
+ ext_route_flag = False
+ mat_redis = None
+ mat_metric = None
+ mat_route_map = None
+ if not is_delete_all:
+ mat_redis = next((redis_cfg for redis_cfg in mat_redis_arr if redis_cfg['protocol'].upper() == conf_protocol), None)
+ if mat_redis:
+ mat_metric = mat_redis.get('metric', None)
+ mat_route_map = mat_redis.get('route_map', None)
+ if mat_metric:
+ ext_metric_flag = True
+ if mat_route_map:
+ ext_route_flag = True
+
+ if conf_protocol == 'CONNECTED':
+ conf_protocol = "DIRECTLY_CONNECTED"
+
+ src_protocol = "openconfig-policy-types:%s" % (conf_protocol)
+ dst_protocol = "openconfig-policy-types:BGP"
+
+ conf_route_map = conf_redis.get('route_map', None)
+ conf_metric = conf_redis.get('metric', None)
+ if conf_metric is not None:
+ conf_metric = float(conf_redis['metric'])
+
+ url = '%s=%s/%s=' % (self.network_instance_path, vrf_name, self.table_connection_path)
+
+ new_metric_flag = conf_metric is not None
+ new_route_flag = conf_route_map is not None
+ is_delete_protocol = False
+ if is_delete_all:
+ is_delete_protocol = True
+ else:
+ is_delete_protocol = (new_metric_flag == ext_metric_flag) and (new_route_flag == ext_route_flag)
+
+ if is_delete_protocol:
+ url += '%s,%s,%s' % (src_protocol, dst_protocol, addr_family)
+ requests.append({'path': url, 'method': DELETE})
+ continue
+
+ if new_metric_flag and ext_metric_flag:
+ url += '%s,%s,%s/config/metric' % (src_protocol, dst_protocol, addr_family)
+ requests.append({'path': url, 'method': DELETE})
+
+ if new_route_flag and ext_route_flag:
+ url += '%s,%s,%s/config/import-policy=%s' % (src_protocol, dst_protocol, addr_family, conf_route_map)
+ requests.append({'path': url, 'method': DELETE})
+
+ return requests
+
+ def get_delete_bgp_af_requests(self, commands, have, is_delete_all):
+ requests = []
+ for cmd in commands:
+ vrf_name = cmd['vrf_name']
+ as_val = cmd['bgp_as']
+ match_cfg = None
+ if not is_delete_all:
+ match_cfg = next((have_cfg for have_cfg in have if have_cfg['vrf_name'] == vrf_name and have_cfg['bgp_as'] == as_val), None)
+ requests.extend(self.get_delete_single_bgp_af_request(cmd, is_delete_all, match_cfg))
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py
new file mode 100644
index 00000000..dc2b023b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py
@@ -0,0 +1,304 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_bgp_as_paths 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+try:
+ from urllib.parse import urlencode
+except Exception:
+ from urllib import urlencode
+
+
+class Bgp_as_paths(ConfigBase):
+ """
+ The sonic_bgp_as_paths class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp_as_paths',
+ ]
+
+ def __init__(self, module):
+ super(Bgp_as_paths, self).__init__(module)
+
+ def get_bgp_as_paths_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)
+ bgp_as_paths_facts = facts['ansible_network_resources'].get('bgp_as_paths')
+ if not bgp_as_paths_facts:
+ return []
+ return bgp_as_paths_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_bgp_as_paths_facts = self.get_bgp_as_paths_facts()
+ commands, requests = self.set_config(existing_bgp_as_paths_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_as_paths_facts = self.get_bgp_as_paths_facts()
+
+ result['before'] = existing_bgp_as_paths_facts
+ if result['changed']:
+ result['after'] = changed_bgp_as_paths_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_as_paths_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_bgp_as_paths_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 = []
+ requests = []
+ state = self._module.params['state']
+ for i in want:
+ if i.get('members'):
+ temp = []
+ for j in i['members']:
+ temp.append(j.replace('\\\\', '\\'))
+ i['members'] = temp
+ diff = get_diff(want, have)
+ for i in want:
+ if i.get('members'):
+ temp = []
+ for j in i['members']:
+ temp.append(j.replace('\\', '\\\\'))
+ i['members'] = temp
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ @staticmethod
+ def _state_replaced(**kwargs):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ return commands
+
+ @staticmethod
+ def _state_overridden(**kwargs):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ return commands
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_as_path_list_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # To Delete a single member
+ # data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=xyz/config/as-path-set-member=11
+ # This will delete the as path and its all members
+ # data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set=xyz
+ # This will delete ALL as path completely
+ # data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets
+
+ is_delete_all = False
+ # if want is none, then delete ALL
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_as_path_requests(commands, have, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_new_add_request(self, conf):
+ request = None
+ members = conf.get('members', None)
+ permit = conf.get('permit', None)
+ permit_str = ""
+ if permit:
+ permit_str = "PERMIT"
+ else:
+ permit_str = "DENY"
+ if members:
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets"
+ method = "PATCH"
+ cfg = {'as-path-set-name': conf['name'], 'as-path-set-member': members, 'openconfig-bgp-policy-ext:action': permit_str}
+ as_path_set = {'as-path-set-name': conf['name'], 'config': cfg}
+ payload = {'openconfig-bgp-policy:as-path-sets': {'as-path-set': [as_path_set]}}
+ request = {"path": url, "method": method, "data": payload}
+ return request
+
+ def get_delete_all_as_path_requests(self, commands):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets"
+ method = "DELETE"
+ requests = []
+ if commands:
+ request = {"path": url, "method": method}
+ requests.append(request)
+ return requests
+
+ def get_delete_single_as_path_member_requests(self, name, members):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:"
+ url = url + "bgp-defined-sets/as-path-sets/as-path-set={name}/config/{members_param}"
+ method = "DELETE"
+ members_params = {'as-path-set-member': ','.join(members)}
+ members_str = urlencode(members_params)
+ request = {"path": url.format(name=name, members_param=members_str), "method": method}
+ return request
+
+ def get_delete_single_as_path_requests(self, name):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set={}"
+ method = "DELETE"
+ request = {"path": url.format(name), "method": method}
+ return request
+
+ def get_delete_single_as_path_action_requests(self, name):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set={}"
+ url = url + "/openconfig-bgp-policy-ext:action"
+ method = "DELETE"
+ request = {"path": url.format(name), "method": method}
+ return request
+
+ def get_delete_as_path_requests(self, commands, have, is_delete_all):
+ requests = []
+ if is_delete_all:
+ requests = self.get_delete_all_as_path_requests(commands)
+ else:
+ for cmd in commands:
+ name = cmd['name']
+ members = cmd['members']
+ permit = cmd['permit']
+ if members:
+ diff_members = []
+ for item in have:
+ if item['name'] == name:
+ for member_want in cmd['members']:
+ if item['members']:
+ if str(member_want) in item['members']:
+ diff_members.append(member_want)
+ if diff_members:
+ requests.append(self.get_delete_single_as_path_member_requests(name, diff_members))
+
+ elif permit:
+ for item in have:
+ if item['name'] == name:
+ requests.append(self.get_delete_single_as_path_action_requests(name))
+ else:
+ for item in have:
+ if item['name'] == name:
+ requests.append(self.get_delete_single_as_path_requests(name))
+
+ return requests
+
+ def get_modify_as_path_list_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ for conf in commands:
+ new_req = self.get_new_add_request(conf)
+ if new_req:
+ requests.append(new_req)
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py
new file mode 100644
index 00000000..670fb26d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py
@@ -0,0 +1,368 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_bgp_communities 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+import json
+from ansible.module_utils._text import to_native
+import traceback
+try:
+ import jinja2
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+try:
+ from urllib.parse import urlencode
+except Exception:
+ from urllib import urlencode
+
+
+class Bgp_communities(ConfigBase):
+ """
+ The sonic_bgp_communities class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp_communities',
+ ]
+
+ def __init__(self, module):
+ super(Bgp_communities, self).__init__(module)
+
+ def get_bgp_communities_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)
+ bgp_communities_facts = facts['ansible_network_resources'].get('bgp_communities')
+ if not bgp_communities_facts:
+ return []
+ return bgp_communities_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_bgp_communities_facts = self.get_bgp_communities_facts()
+ commands, requests = self.set_config(existing_bgp_communities_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_communities_facts = self.get_bgp_communities_facts()
+
+ result['before'] = existing_bgp_communities_facts
+ if result['changed']:
+ result['after'] = changed_bgp_communities_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_communities_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_bgp_communities_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 = []
+ requests = []
+ state = self._module.params['state']
+ diff = get_diff(want, have)
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('comm: want: ' + str(want) + '\n')
+ # fp.write('comm: have: ' + str(have) + '\n')
+ # fp.write('comm: diff: ' + str(diff) + '\n')
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ @staticmethod
+ def _state_replaced(**kwargs):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ return commands
+
+ @staticmethod
+ def _state_overridden(**kwargs):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ return commands
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_bgp_community_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # Delete a community
+ # https://100.94.81.19/restconf/data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=extest
+ # Delete all members but not community
+ # https://100.94.81.19/restconf/data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=extest/config/community-member
+ # Dete a memeber from the expanded community
+ # https://100.94.81.19/restconf/data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=extest/config/community-member=REGEX%3A100.100
+ # Delete ALL Bgp_communities and its members
+ # https://100.94.81.19/restconf/data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets
+ is_delete_all = False
+ # if want is none, then delete ALL
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_bgp_communities(commands, have, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_delete_single_bgp_community_member_requests(self, name, type, members):
+ requests = []
+ for member in members:
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:"
+ url = url + "bgp-defined-sets/community-sets/community-set={name}/config/{members_param}"
+ method = "DELETE"
+ memberstr = member
+ if type == 'expanded':
+ memberstr = 'REGEX:' + member
+ members_params = {'community-member': memberstr}
+ members_str = urlencode(members_params)
+ request = {"path": url.format(name=name, members_param=members_str), "method": method}
+ requests.append(request)
+ return requests
+
+ def get_delete_all_members_bgp_community_requests(self, name):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:"
+ url = url + "bgp-defined-sets/community-sets/community-set={}/config/community-member"
+ method = "DELETE"
+ request = {"path": url.format(name), "method": method}
+ return request
+
+ def get_delete_single_bgp_community_requests(self, name):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set={}"
+ method = "DELETE"
+ request = {"path": url.format(name), "method": method}
+ return request
+
+ def get_delete_all_bgp_communities(self, commands):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets"
+ method = "DELETE"
+ requests = []
+ if commands:
+ request = {"path": url, "method": method}
+ requests.append(request)
+ return requests
+
+ def get_delete_bgp_communities(self, commands, have, is_delete_all):
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('bgp_commmunities: delete requests ************** \n')
+ requests = []
+ if is_delete_all:
+ requests = self.get_delete_all_bgp_communities(commands)
+ else:
+ for cmd in commands:
+ name = cmd['name']
+ type = cmd['type']
+ members = cmd['members']
+ if members:
+ if members['regex']:
+ diff_members = []
+ for item in have:
+ if item['name'] == name and item['members']:
+ for member_want in members['regex']:
+ if str(member_want) in item['members']['regex']:
+ diff_members.append(member_want)
+ if diff_members:
+ requests.extend(self.get_delete_single_bgp_community_member_requests(name, type, diff_members))
+ else:
+ for item in have:
+ if item['name'] == name:
+ if item['members']:
+ requests.append(self.get_delete_all_members_bgp_community_requests(name))
+ else:
+ for item in have:
+ if item['name'] == name:
+ requests.append(self.get_delete_single_bgp_community_requests(name))
+
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('bgp_commmunities: delete requests' + str(requests) + '\n')
+ return requests
+
+ def get_new_add_request(self, conf):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets"
+ method = "PATCH"
+ # members = conf['members']
+ # members_str = ', '.join(members)
+ # members_list = list()
+ # for member in members.split(','):
+ # members_list.append(str(member))
+
+ if 'match' not in conf:
+ conf['match'] = "ANY"
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('bgp_communities: conf' + str(conf) + '\n')
+ if 'local_as' in conf and conf['local_as']:
+ conf['members']['regex'].append("NO_EXPORT_SUBCONFED")
+ if 'no_peer' in conf and conf['no_peer']:
+ conf['members']['regex'].append("NOPEER")
+ if 'no_export' in conf and conf['no_export']:
+ conf['members']['regex'].append("NO_EXPORT")
+ if 'no_advertise' in conf and conf['no_advertise']:
+ conf['members']['regex'].append("NO_ADVERTISE")
+ input_data = {'name': conf['name'], 'members_list': conf['members']['regex'], 'match': conf['match']}
+ if conf['type'] == 'expanded':
+ input_data['regex'] = "REGEX:"
+ else:
+ input_data['regex'] = ""
+ if conf['permit']:
+ input_data['permit'] = "PERMIT"
+ else:
+ input_data['permit'] = "DENY"
+ payload_template = """
+ {
+ "openconfig-bgp-policy:community-sets": {
+ "community-set": [
+ {
+ "community-set-name": "{{name}}",
+ "config": {
+ "community-set-name": "{{name}}",
+ "community-member": [
+ {% for member in members_list %}"{{regex}}{{member}}"{%- if not loop.last -%},{% endif %}{%endfor%}
+ ],
+ "openconfig-bgp-policy-ext:action": "{{permit}}",
+ "match-set-options": "{{match}}"
+ }
+ }
+ ]
+ }
+ }"""
+ env = jinja2.Environment(autoescape=False)
+ t = env.from_string(payload_template)
+ intended_payload = t.render(input_data)
+ ret_payload = json.loads(intended_payload)
+ request = {"path": url, "method": method, "data": ret_payload}
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('bgp_communities: request' + str(request) + '\n')
+ return request
+
+ def get_modify_bgp_community_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ for conf in commands:
+ for item in have:
+ if item['name'] == conf['name']:
+ if 'type' not in conf:
+ conf['type'] = item['type']
+ if 'permit' not in conf:
+ conf['permit'] = item['permit']
+ if 'match' not in conf:
+ conf['match'] = item['match']
+ if 'members' not in conf:
+ conf['members'] = item['members']
+ new_req = self.get_new_add_request(conf)
+ if new_req:
+ requests.append(new_req)
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py
new file mode 100644
index 00000000..751f88e4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py
@@ -0,0 +1,371 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_bgp_ext_communities 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+import json
+from ansible.module_utils._text import to_native
+from ansible.module_utils.connection import ConnectionError
+import traceback
+try:
+ import jinja2
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+try:
+ from urllib.parse import urlencode
+except Exception:
+ from urllib import urlencode
+
+
+class Bgp_ext_communities(ConfigBase):
+ """
+ The sonic_bgp_ext_communities class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp_ext_communities',
+ ]
+
+ def __init__(self, module):
+ super(Bgp_ext_communities, self).__init__(module)
+
+ def get_bgp_ext_communities_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)
+ bgp_ext_communities_facts = facts['ansible_network_resources'].get('bgp_ext_communities')
+ if not bgp_ext_communities_facts:
+ return []
+ return bgp_ext_communities_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_bgp_ext_communities_facts = self.get_bgp_ext_communities_facts()
+ commands, requests = self.set_config(existing_bgp_ext_communities_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_ext_communities_facts = self.get_bgp_ext_communities_facts()
+
+ result['before'] = existing_bgp_ext_communities_facts
+ if result['changed']:
+ result['after'] = changed_bgp_ext_communities_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_ext_communities_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_bgp_ext_communities_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 = []
+ requests = []
+ state = self._module.params['state']
+ new_want = self.validate_type(want)
+ diff = get_diff(new_want, have)
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ @staticmethod
+ def _state_replaced(**kwargs):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ return commands
+
+ @staticmethod
+ def _state_overridden(**kwargs):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ return commands
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_bgp_ext_community_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ is_delete_all = False
+ # if want is none, then delete ALL
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_bgp_ext_communities(commands, have, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_delete_single_bgp_ext_community_member_requests(self, name, type, members):
+ requests = []
+ for member in members:
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:"
+ url = url + "bgp-defined-sets/ext-community-sets/ext-community-set={name}/config/{members_param}"
+ method = "DELETE"
+ members_params = {'ext-community-member': member}
+ members_str = urlencode(members_params)
+ request = {"path": url.format(name=name, members_param=members_str), "method": method}
+ requests.append(request)
+ return requests
+
+ def get_delete_all_members_bgp_ext_community_requests(self, name):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:"
+ url = url + "bgp-defined-sets/ext-community-sets/ext-community-set={}/config/ext-community-member"
+ method = "DELETE"
+ request = {"path": url.format(name), "method": method}
+ return request
+
+ def get_delete_single_bgp_ext_community_requests(self, name):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set={}"
+ method = "DELETE"
+ request = {"path": url.format(name), "method": method}
+ return request
+
+ def get_delete_all_bgp_ext_communities(self, commands):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets"
+ method = "DELETE"
+ requests = []
+ if commands:
+ request = {"path": url, "method": method}
+ requests.append(request)
+ return requests
+
+ def get_delete_bgp_ext_communities(self, commands, have, is_delete_all):
+ requests = []
+ if is_delete_all:
+ requests = self.get_delete_all_bgp_ext_communities(commands)
+ else:
+ for cmd in commands:
+ name = cmd['name']
+ type = cmd['type']
+ members = cmd['members']
+ if members:
+ if members['regex'] or members['route_origin'] or members['route_target']:
+ diff_members = []
+ for item in have:
+ if item['name'] == name and item['members']:
+ if members['regex']:
+ for member_want in members['regex']:
+ if str(member_want) in item['members']['regex']:
+ diff_members.append('REGEX:' + str(member_want))
+ if members['route_origin']:
+ for member_want in members['route_origin']:
+ if str(member_want) in item['members']['route_origin']:
+ diff_members.append("route-origin:" + str(member_want))
+ if members['route_target']:
+ for member_want in members['route_target']:
+ if str(member_want) in item['members']['route_target']:
+ diff_members.append("route-target:" + str(member_want))
+ if diff_members:
+ requests.extend(self.get_delete_single_bgp_ext_community_member_requests(name, type, diff_members))
+ else:
+ for item in have:
+ if item['name'] == name:
+ if item['members']:
+ requests.append(self.get_delete_all_members_bgp_ext_community_requests(name))
+ else:
+ for item in have:
+ if item['name'] == name:
+ requests.append(self.get_delete_single_bgp_ext_community_requests(name))
+
+ return requests
+
+ def get_new_add_request(self, conf):
+
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets"
+ method = "PATCH"
+ members = conf.get('members', None)
+ if 'match' not in conf:
+ conf['match'] = "ANY"
+ else:
+ conf['match'] = conf['match'].upper()
+ input_data = {'name': conf['name'], 'match': conf['match']}
+
+ input_data['members_list'] = list()
+ if members:
+ regex = members.get('regex', None)
+ if regex:
+ input_data['members_list'].extend(["REGEX:" + cfg for cfg in regex])
+ else:
+ route_target = members.get('route_target', None)
+ if route_target:
+ input_data['members_list'].extend(["route-target:" + cfg for cfg in route_target])
+ route_origin = members.get('route_origin', None)
+ if route_origin:
+ input_data['members_list'].extend(["route-origin:" + cfg for cfg in route_origin])
+
+ if conf['type'] == 'expanded':
+ input_data['regex'] = "REGEX:"
+ else:
+ input_data['regex'] = ""
+ if conf['permit']:
+ input_data['permit'] = "PERMIT"
+ else:
+ input_data['permit'] = "DENY"
+ payload_template = """
+ {
+ "openconfig-bgp-policy:ext-community-sets": {
+ "ext-community-set": [
+ {
+ "ext-community-set-name": "{{name}}",
+ "config": {
+ "ext-community-set-name": "{{name}}",
+ "ext-community-member": [
+ {% for member in members_list %}"{{member}}"{%- if not loop.last -%},{% endif %}{%endfor%}
+ ],
+ "openconfig-bgp-policy-ext:action": "{{permit}}",
+ "match-set-options": "{{match}}"
+ }
+ }
+ ]
+ }
+ }"""
+ env = jinja2.Environment(autoescape=False)
+ t = env.from_string(payload_template)
+ intended_payload = t.render(input_data)
+ ret_payload = json.loads(intended_payload)
+ request = {"path": url, "method": method, "data": ret_payload}
+ return request
+
+ def get_modify_bgp_ext_community_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ for conf in commands:
+ for item in have:
+ if item['name'] == conf['name']:
+ if 'type' not in conf:
+ conf['type'] = item['type']
+ if 'permit' not in conf:
+ conf['permit'] = item['permit']
+ if 'match' not in conf:
+ conf['match'] = item['match']
+ if 'members' not in conf:
+ conf['members'] = item['members']
+ break
+ new_req = self.get_new_add_request(conf)
+ if new_req:
+ requests.append(new_req)
+ return requests
+
+ def validate_type(self, want):
+ new_want = []
+ if want:
+ for conf in want:
+ cfg = conf.copy()
+ cfg['type'] = 'standard'
+ members = conf.get('members', None)
+ if members and members.get('regex', None):
+ cfg['type'] = 'expanded'
+
+ new_want.append(cfg)
+ return new_want
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py
new file mode 100644
index 00000000..31bbec78
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors/bgp_neighbors.py
@@ -0,0 +1,1100 @@
+#
+# -*- 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 sonic_bgp_neighbors 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ validate_bgps,
+ normalize_neighbors_interface_name,
+ get_ip_afi_cfg_payload,
+ get_prefix_limit_payload
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import to_request
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'delete'
+
+TEST_KEYS = [
+ {'config': {'vrf_name': '', 'bgp_as': ''}},
+ {'neighbors': {'neighbor': ''}},
+ {'peer_group': {'name': ''}},
+ {'afis': {'afi': '', 'safi': ''}},
+]
+
+
+class Bgp_neighbors(ConfigBase):
+ """
+ The sonic_bgp_neighbors class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp_neighbors',
+ ]
+
+ network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+ protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp'
+ neighbor_path = 'neighbors/neighbor'
+
+ def __init__(self, module):
+ super(Bgp_neighbors, self).__init__(module)
+
+ def get_bgp_neighbors_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)
+ bgp_facts = facts['ansible_network_resources'].get('bgp_neighbors')
+ if not bgp_facts:
+ bgp_facts = []
+ return bgp_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_bgp_facts = self.get_bgp_neighbors_facts()
+ commands, requests = self.set_config(existing_bgp_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_facts = self.get_bgp_neighbors_facts()
+
+ result['before'] = existing_bgp_facts
+ if result['changed']:
+ result['after'] = changed_bgp_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_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']
+ normalize_neighbors_interface_name(want, self._module)
+ have = existing_bgp_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 = []
+ requests = []
+ state = self._module.params['state']
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = []
+ requests = []
+ commands = diff
+ validate_bgps(self._module, commands, have)
+ requests = self.get_modify_bgp_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ is_delete_all = False
+ if not want:
+ is_delete_all = True
+ if is_delete_all:
+ commands = have
+ new_have = have
+ else:
+ new_have = self.remove_default_entries(have)
+ d_diff = get_diff(want, new_have, TEST_KEYS, is_skeleton=True)
+ delete_diff = get_diff(want, d_diff, TEST_KEYS, is_skeleton=True)
+ commands = delete_diff
+ requests = self.get_delete_bgp_neighbor_requests(commands, new_have, want, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+ return commands, requests
+
+ def remove_default_entries(self, data):
+ new_data = []
+ if not data:
+ return new_data
+ for conf in data:
+ new_conf = {}
+ as_val = conf['bgp_as']
+ vrf_name = conf['vrf_name']
+ new_conf['bgp_as'] = as_val
+ new_conf['vrf_name'] = vrf_name
+ peergroup = conf.get('peer_group', None)
+ new_peergroups = []
+ if peergroup is not None:
+ for pg in peergroup:
+ new_pg = {}
+ pg_val = pg.get('name', None)
+ new_pg['name'] = pg_val
+ remote_as = pg.get('remote_as', None)
+ new_remote = {}
+ if remote_as:
+ peer_as = remote_as.get('peer_as', None)
+ peer_type = remote_as.get('peer_type', None)
+ if peer_as is not None:
+ new_remote['peer_as'] = peer_as
+ if peer_type is not None:
+ new_remote['peer_type'] = peer_type
+ if new_remote:
+ new_pg['remote_as'] = new_remote
+ timers = pg.get('timers', None)
+ new_timers = {}
+ if timers:
+ keepalive = timers.get('keepalive', None)
+ holdtime = timers.get('holdtime', None)
+ connect_retry = timers.get('connect_retry', None)
+ if keepalive is not None and keepalive != 60:
+ new_timers['keepalive'] = keepalive
+ if holdtime is not None and holdtime != 180:
+ new_timers['holdtime'] = holdtime
+ if connect_retry is not None and connect_retry != 30:
+ new_timers['connect_retry'] = connect_retry
+ if new_timers:
+ new_pg['timers'] = new_timers
+ advertisement_interval = pg.get('advertisement_interval', None)
+ if advertisement_interval is not None and advertisement_interval != 30:
+ new_pg['advertisement_interval'] = advertisement_interval
+ bfd = pg.get('bfd', None)
+ if bfd is not None:
+ new_pg['bfd'] = bfd
+ capability = pg.get('capability', None)
+ if capability is not None:
+ new_pg['capability'] = capability
+ afi = []
+ address_family = pg.get('address_family', None)
+ if address_family:
+ if address_family.get('afis', None):
+ for each in address_family['afis']:
+ if each:
+ tmp = {}
+ if each.get('afi', None) is not None:
+ tmp['afi'] = each['afi']
+ if each.get('safi', None) is not None:
+ tmp['safi'] = each['safi']
+ if each.get('activate', None) is not None and each['activate'] is not False:
+ tmp['activate'] = each['activate']
+ if each.get('allowas_in', None) is not None:
+ tmp['allowas_in'] = each['allowas_in']
+ if each.get('ip_afi', None) is not None:
+ tmp['ip_afi'] = each['ip_afi']
+ if each.get('prefix_limit', None) is not None:
+ tmp['prefix_limit'] = each['prefix_limit']
+ if each.get('prefix_list_in', None) is not None:
+ tmp['prefix_list_in'] = each['prefix_list_in']
+ if each.get('prefix_list_out', None) is not None:
+ tmp['prefix_list_out'] = each['prefix_list_out']
+ afi.append(tmp)
+ if afi and len(afi) > 0:
+ afis = {}
+ afis.update({'afis': afi})
+ new_pg['address_family'] = afis
+ if new_pg:
+ new_peergroups.append(new_pg)
+ if new_peergroups:
+ new_conf['peer_group'] = new_peergroups
+ neighbors = conf.get('neighbors', None)
+ new_neighbors = []
+ if neighbors is not None:
+ for neighbor in neighbors:
+ new_neighbor = {}
+ neighbor_val = neighbor.get('neighbor', None)
+ new_neighbor['neighbor'] = neighbor_val
+ remote_as = neighbor.get('remote_as', None)
+ new_remote = {}
+ if remote_as:
+ peer_as = remote_as.get('peer_as', None)
+ peer_type = remote_as.get('peer_type', None)
+ if peer_as is not None:
+ new_remote['peer_as'] = peer_as
+ if peer_type is not None:
+ new_remote['peer_type'] = peer_type
+ if new_remote:
+ new_neighbor['remote_as'] = new_remote
+ peer_group = neighbor.get('peer_group', None)
+ if peer_group:
+ new_neighbor['peer_group'] = peer_group
+ timers = neighbor.get('timers', None)
+ new_timers = {}
+ if timers:
+ keepalive = timers.get('keepalive', None)
+ holdtime = timers.get('holdtime', None)
+ connect_retry = timers.get('connect_retry', None)
+ if keepalive is not None and keepalive != 60:
+ new_timers['keepalive'] = keepalive
+ if holdtime is not None and holdtime != 180:
+ new_timers['holdtime'] = holdtime
+ if connect_retry is not None and connect_retry != 30:
+ new_timers['connect_retry'] = connect_retry
+ if new_timers:
+ new_neighbor['timers'] = new_timers
+ advertisement_interval = neighbor.get('advertisement_interval', None)
+ if advertisement_interval is not None and advertisement_interval != 30:
+ new_neighbor['advertisement_interval'] = advertisement_interval
+ bfd = neighbor.get('bfd', None)
+ if bfd is not None:
+ new_neighbor['bfd'] = bfd
+ capability = neighbor.get('capability', None)
+ if capability is not None:
+ new_neighbor['capability'] = capability
+ if new_neighbor:
+ new_neighbors.append(new_neighbor)
+ if new_neighbors:
+ new_conf['neighbors'] = new_neighbors
+ if new_conf:
+ new_data.append(new_conf)
+ return new_data
+
+ def build_bgp_peer_groups_payload(self, cmd, have, bgp_as, vrf_name):
+ requests = []
+ bgp_peer_group_list = []
+ for peer_group in cmd:
+ if peer_group:
+ bgp_peer_group = {}
+ peer_group_cfg = {}
+ tmp_bfd = {}
+ tmp_ebgp = {}
+ tmp_timers = {}
+ tmp_capability = {}
+ tmp_remote = {}
+ tmp_transport = {}
+ afi = []
+ if peer_group.get('name', None) is not None:
+ peer_group_cfg.update({'peer-group-name': peer_group['name']})
+ bgp_peer_group.update({'peer-group-name': peer_group['name']})
+ if peer_group.get('bfd', None) is not None:
+ if peer_group['bfd'].get('enabled', None) is not None:
+ tmp_bfd.update({'enabled': peer_group['bfd']['enabled']})
+ if peer_group['bfd'].get('check_failure', None) is not None:
+ tmp_bfd.update({'check-control-plane-failure': peer_group['bfd']['check_failure']})
+ if peer_group['bfd'].get('profile', None) is not None:
+ tmp_bfd.update({'bfd-profile': peer_group['bfd']['profile']})
+ if peer_group.get('auth_pwd', None) is not None:
+ if (peer_group['auth_pwd'].get('pwd', None) is not None and
+ peer_group['auth_pwd'].get('encrypted', None) is not None):
+ bgp_peer_group.update({'auth-password': {'config': {'password': peer_group['auth_pwd']['pwd'],
+ 'encrypted': peer_group['auth_pwd']['encrypted']}}})
+ if peer_group.get('ebgp_multihop', None) is not None:
+ if peer_group['ebgp_multihop'].get('enabled', None) is not None:
+ tmp_ebgp.update({'enabled': peer_group['ebgp_multihop']['enabled']})
+ if peer_group['ebgp_multihop'].get('multihop_ttl', None) is not None:
+ tmp_ebgp.update({'multihop-ttl': peer_group['ebgp_multihop']['multihop_ttl']})
+ if peer_group.get('timers', None) is not None:
+ if peer_group['timers'].get('holdtime', None) is not None:
+ tmp_timers.update({'hold-time': peer_group['timers']['holdtime']})
+ if peer_group['timers'].get('keepalive', None) is not None:
+ tmp_timers.update({'keepalive-interval': peer_group['timers']['keepalive']})
+ if peer_group['timers'].get('connect_retry', None) is not None:
+ tmp_timers.update({'connect-retry': peer_group['timers']['connect_retry']})
+ if peer_group.get('capability', None) is not None:
+ if peer_group['capability'].get('dynamic', None) is not None:
+ tmp_capability.update({'capability-dynamic': peer_group['capability']['dynamic']})
+ if peer_group['capability'].get('extended_nexthop', None) is not None:
+ tmp_capability.update({'capability-extended-nexthop': peer_group['capability']['extended_nexthop']})
+ if peer_group.get('pg_description', None) is not None:
+ peer_group_cfg.update({'description': peer_group['pg_description']})
+ if peer_group.get('disable_connected_check', None) is not None:
+ peer_group_cfg.update({'disable-ebgp-connected-route-check': peer_group['disable_connected_check']})
+ if peer_group.get('dont_negotiate_capability', None) is not None:
+ peer_group_cfg.update({'dont-negotiate-capability': peer_group['dont_negotiate_capability']})
+ if peer_group.get('enforce_first_as', None) is not None:
+ peer_group_cfg.update({'enforce-first-as': peer_group['enforce_first_as']})
+ if peer_group.get('enforce_multihop', None) is not None:
+ peer_group_cfg.update({'enforce-multihop': peer_group['enforce_multihop']})
+ if peer_group.get('override_capability', None) is not None:
+ peer_group_cfg.update({'override-capability': peer_group['override_capability']})
+ if peer_group.get('shutdown_msg', None) is not None:
+ peer_group_cfg.update({'shutdown-message': peer_group['shutdown_msg']})
+ if peer_group.get('solo', None) is not None:
+ peer_group_cfg.update({'solo-peer': peer_group['solo']})
+ if peer_group.get('strict_capability_match', None) is not None:
+ peer_group_cfg.update({'strict-capability-match': peer_group['strict_capability_match']})
+ if peer_group.get('ttl_security', None) is not None:
+ peer_group_cfg.update({'ttl-security-hops': peer_group['ttl_security']})
+ if peer_group.get('local_as', None) is not None:
+ if peer_group['local_as'].get('as', None) is not None:
+ peer_group_cfg.update({'local-as': peer_group['local_as']['as']})
+ if peer_group['local_as'].get('no_prepend', None) is not None:
+ peer_group_cfg.update({'local-as-no-prepend': peer_group['local_as']['no_prepend']})
+ if peer_group['local_as'].get('replace_as', None) is not None:
+ peer_group_cfg.update({'local-as-replace-as': peer_group['local_as']['replace_as']})
+ if peer_group.get('local_address', None) is not None:
+ tmp_transport.update({'local-address': peer_group['local_address']})
+ if peer_group.get('passive', None) is not None:
+ tmp_transport.update({'passive-mode': peer_group['passive']})
+ if peer_group.get('advertisement_interval', None) is not None:
+ tmp_timers.update({'minimum-advertisement-interval': peer_group['advertisement_interval']})
+ if peer_group.get('remote_as', None) is not None:
+ have_nei = self.find_pg(have, bgp_as, vrf_name, peer_group)
+ if peer_group['remote_as'].get('peer_as', None) is not None:
+ if have_nei:
+ if have_nei.get("remote_as", None) is not None:
+ if have_nei["remote_as"].get("peer_type", None) is not None:
+ del_nei = {}
+ del_nei.update({'name': have_nei['name']})
+ del_nei.update({'remote_as': have_nei['remote_as']})
+ requests.extend(self.delete_specific_peergroup_param_request(vrf_name, del_nei))
+ tmp_remote.update({'peer-as': peer_group['remote_as']['peer_as']})
+ if peer_group['remote_as'].get('peer_type', None) is not None:
+ if have_nei:
+ if have_nei.get("remote_as", None) is not None:
+ if have_nei["remote_as"].get("peer_as", None) is not None:
+ del_nei = {}
+ del_nei.update({'name': have_nei['name']})
+ del_nei.update({'remote_as': have_nei['remote_as']})
+ requests.extend(self.delete_specific_peergroup_param_request(vrf_name, del_nei))
+ tmp_remote.update({'peer-type': peer_group['remote_as']['peer_type'].upper()})
+ if peer_group.get('address_family', None) is not None:
+ if peer_group['address_family'].get('afis', None) is not None:
+ for each in peer_group['address_family']['afis']:
+ samp = {}
+ afi_safi_cfg = {}
+ pfx_lmt_cfg = {}
+ pfx_lst_cfg = {}
+ ip_dict = {}
+ if each.get('afi', None) is not None and each.get('safi', None) is not None:
+ afi_safi = each['afi'].upper() + "_" + each['safi'].upper()
+ if afi_safi is not None:
+ afi_safi_name = 'openconfig-bgp-types:' + afi_safi
+ if afi_safi_name is not None:
+ samp.update({'afi-safi-name': afi_safi_name})
+ samp.update({'config': {'afi-safi-name': afi_safi_name}})
+ if each.get('prefix_limit', None) is not None:
+ pfx_lmt_cfg = get_prefix_limit_payload(each['prefix_limit'])
+ if pfx_lmt_cfg and afi_safi == 'L2VPN_EVPN':
+ samp.update({'l2vpn-evpn': {'prefix-limit': {'config': pfx_lmt_cfg}}})
+ else:
+ if each.get('ip_afi', None) is not None:
+ afi_safi_cfg = get_ip_afi_cfg_payload(each['ip_afi'])
+ if afi_safi_cfg:
+ ip_dict.update({'config': afi_safi_cfg})
+ if pfx_lmt_cfg:
+ ip_dict.update({'prefix-limit': {'config': pfx_lmt_cfg}})
+ if ip_dict and afi_safi == 'IPV4_UNICAST':
+ samp.update({'ipv4-unicast': ip_dict})
+ elif ip_dict and afi_safi == 'IPV6_UNICAST':
+ samp.update({'ipv6-unicast': ip_dict})
+ if each.get('activate', None) is not None:
+ enabled = each['activate']
+ if enabled is not None:
+ samp.update({'config': {'enabled': enabled}})
+ if each.get('allowas_in', None) is not None:
+ have_pg_af = self.find_af(have, bgp_as, vrf_name, peer_group, each['afi'], each['safi'])
+ if each['allowas_in'].get('origin', None) is not None:
+ if have_pg_af:
+ if have_pg_af.get('allowas_in', None) is not None:
+ if have_pg_af['allowas_in'].get('value', None) is not None:
+ del_nei = {}
+ del_nei.update({'name': peer_group['name']})
+ afis_list = []
+ temp_cfg = {'afi': each['afi'], 'safi': each['safi']}
+ temp_cfg['allowas_in'] = {'value': have_pg_af['allowas_in']['value']}
+ afis_list.append(temp_cfg)
+ del_nei.update({'address_family': {'afis': afis_list}})
+ requests.extend(self.delete_specific_peergroup_param_request(vrf_name, del_nei))
+ origin = each['allowas_in']['origin']
+ samp.update({'allow-own-as': {'config': {'origin': origin, "enabled": bool("true")}}})
+ if each['allowas_in'].get('value', None) is not None:
+ if have_pg_af:
+ if have_pg_af.get('allowas_in', None) is not None:
+ if have_pg_af['allowas_in'].get('origin', None) is not None:
+ del_nei = {}
+ del_nei.update({'name': peer_group['name']})
+ afis_list = []
+ temp_cfg = {'afi': each['afi'], 'safi': each['safi']}
+ temp_cfg['allowas_in'] = {'origin': have_pg_af['allowas_in']['origin']}
+ afis_list.append(temp_cfg)
+ del_nei.update({'address_family': {'afis': afis_list}})
+ requests.extend(self.delete_specific_peergroup_param_request(vrf_name, del_nei))
+ as_count = each['allowas_in']['value']
+ samp.update({'allow-own-as': {'config': {'as-count': as_count, "enabled": bool("true")}}})
+ if each.get('prefix_list_in', None) is not None:
+ prefix_list_in = each['prefix_list_in']
+ if prefix_list_in is not None:
+ pfx_lst_cfg.update({'import-policy': prefix_list_in})
+ if each.get('prefix_list_out', None) is not None:
+ prefix_list_out = each['prefix_list_out']
+ if prefix_list_out is not None:
+ pfx_lst_cfg.update({'export-policy': prefix_list_out})
+ if pfx_lst_cfg:
+ samp.update({'prefix-list': {'config': pfx_lst_cfg}})
+ if samp:
+ afi.append(samp)
+ if tmp_bfd:
+ bgp_peer_group.update({'enable-bfd': {'config': tmp_bfd}})
+ if tmp_ebgp:
+ bgp_peer_group.update({'ebgp-multihop': {'config': tmp_ebgp}})
+ if tmp_timers:
+ bgp_peer_group.update({'timers': {'config': tmp_timers}})
+ if tmp_transport:
+ bgp_peer_group.update({'transport': {'config': tmp_transport}})
+ if afi and len(afi) > 0:
+ bgp_peer_group.update({'afi-safis': {'afi-safi': afi}})
+ if tmp_capability:
+ peer_group_cfg.update(tmp_capability)
+ if tmp_remote:
+ peer_group_cfg.update(tmp_remote)
+ if peer_group_cfg:
+ bgp_peer_group.update({'config': peer_group_cfg})
+ if bgp_peer_group:
+ bgp_peer_group_list.append(bgp_peer_group)
+ payload = {'openconfig-network-instance:peer-groups': {'peer-group': bgp_peer_group_list}}
+ return payload, requests
+
+ def find_pg(self, have, bgp_as, vrf_name, peergroup):
+ mat_dict = next((m_peer for m_peer in have if m_peer['bgp_as'] == bgp_as and m_peer['vrf_name'] == vrf_name), None)
+ if mat_dict and mat_dict.get("peer_group", None) is not None:
+ mat_pg = next((m for m in mat_dict['peer_group'] if m["name"] == peergroup['name']), None)
+ return mat_pg
+
+ def find_af(self, have, bgp_as, vrf_name, peergroup, afi, safi):
+ mat_pg = self.find_pg(have, bgp_as, vrf_name, peergroup)
+ if mat_pg and mat_pg['address_family'].get('afis', None) is not None:
+ mat_af = next((af for af in mat_pg['address_family']['afis'] if af['afi'] == afi and af['safi'] == safi), None)
+ return mat_af
+
+ def find_nei(self, have, bgp_as, vrf_name, neighbor):
+ mat_dict = next((m_neighbor for m_neighbor in have if m_neighbor['bgp_as'] == bgp_as and m_neighbor['vrf_name'] == vrf_name), None)
+ if mat_dict and mat_dict.get("neighbors", None) is not None:
+ mat_neighbor = next((m for m in mat_dict['neighbors'] if m["neighbor"] == neighbor['neighbor']), None)
+ return mat_neighbor
+
+ def build_bgp_neighbors_payload(self, cmd, have, bgp_as, vrf_name):
+ bgp_neighbor_list = []
+ requests = []
+ for neighbor in cmd:
+ if neighbor:
+ bgp_neighbor = {}
+ neighbor_cfg = {}
+ tmp_bfd = {}
+ tmp_ebgp = {}
+ tmp_timers = {}
+ tmp_capability = {}
+ tmp_remote = {}
+ tmp_transport = {}
+ if neighbor.get('bfd', None) is not None:
+ if neighbor['bfd'].get('enabled', None) is not None:
+ tmp_bfd.update({'enabled': neighbor['bfd']['enabled']})
+ if neighbor['bfd'].get('check_failure', None) is not None:
+ tmp_bfd.update({'check-control-plane-failure': neighbor['bfd']['check_failure']})
+ if neighbor['bfd'].get('profile', None) is not None:
+ tmp_bfd.update({'bfd-profile': neighbor['bfd']['profile']})
+ if neighbor.get('auth_pwd', None) is not None:
+ if (neighbor['auth_pwd'].get('pwd', None) is not None and
+ neighbor['auth_pwd'].get('encrypted', None) is not None):
+ bgp_neighbor.update({'auth-password': {'config': {'password': neighbor['auth_pwd']['pwd'],
+ 'encrypted': neighbor['auth_pwd']['encrypted']}}})
+ if neighbor.get('ebgp_multihop', None) is not None:
+ if neighbor['ebgp_multihop'].get('enabled', None) is not None:
+ tmp_ebgp.update({'enabled': neighbor['ebgp_multihop']['enabled']})
+ if neighbor['ebgp_multihop'].get('multihop_ttl', None) is not None:
+ tmp_ebgp.update({'multihop-ttl': neighbor['ebgp_multihop']['multihop_ttl']})
+ if neighbor.get('timers', None) is not None:
+ if neighbor['timers'].get('holdtime', None) is not None:
+ tmp_timers.update({'hold-time': neighbor['timers']['holdtime']})
+ if neighbor['timers'].get('keepalive', None) is not None:
+ tmp_timers.update({'keepalive-interval': neighbor['timers']['keepalive']})
+ if neighbor['timers'].get('connect_retry', None) is not None:
+ tmp_timers.update({'connect-retry': neighbor['timers']['connect_retry']})
+ if neighbor.get('capability', None) is not None:
+ if neighbor['capability'].get('dynamic', None) is not None:
+ tmp_capability.update({'capability-dynamic': neighbor['capability']['dynamic']})
+ if neighbor['capability'].get('extended_nexthop', None) is not None:
+ tmp_capability.update({'capability-extended-nexthop': neighbor['capability']['extended_nexthop']})
+ if neighbor.get('advertisement_interval', None) is not None:
+ tmp_timers.update({'minimum-advertisement-interval': neighbor['advertisement_interval']})
+ if neighbor.get('neighbor', None) is not None:
+ bgp_neighbor.update({'neighbor-address': neighbor['neighbor']})
+ neighbor_cfg.update({'neighbor-address': neighbor['neighbor']})
+ if neighbor.get('peer_group', None) is not None:
+ neighbor_cfg.update({'peer-group': neighbor['peer_group']})
+ if neighbor.get('nbr_description', None) is not None:
+ neighbor_cfg.update({'description': neighbor['nbr_description']})
+ if neighbor.get('disable_connected_check', None) is not None:
+ neighbor_cfg.update({'disable-ebgp-connected-route-check': neighbor['disable_connected_check']})
+ if neighbor.get('dont_negotiate_capability', None) is not None:
+ neighbor_cfg.update({'dont-negotiate-capability': neighbor['dont_negotiate_capability']})
+ if neighbor.get('enforce_first_as', None) is not None:
+ neighbor_cfg.update({'enforce-first-as': neighbor['enforce_first_as']})
+ if neighbor.get('enforce_multihop', None) is not None:
+ neighbor_cfg.update({'enforce-multihop': neighbor['enforce_multihop']})
+ if neighbor.get('override_capability', None) is not None:
+ neighbor_cfg.update({'override-capability': neighbor['override_capability']})
+ if neighbor.get('port', None) is not None:
+ neighbor_cfg.update({'peer-port': neighbor['port']})
+ if neighbor.get('shutdown_msg', None) is not None:
+ neighbor_cfg.update({'shutdown-message': neighbor['shutdown_msg']})
+ if neighbor.get('solo', None) is not None:
+ neighbor_cfg.update({'solo-peer': neighbor['solo']})
+ if neighbor.get('strict_capability_match', None) is not None:
+ neighbor_cfg.update({'strict-capability-match': neighbor['strict_capability_match']})
+ if neighbor.get('ttl_security', None) is not None:
+ neighbor_cfg.update({'ttl-security-hops': neighbor['ttl_security']})
+ if neighbor.get('v6only', None) is not None:
+ neighbor_cfg.update({'openconfig-bgp-ext:v6only': neighbor['v6only']})
+ if neighbor.get('local_as', None) is not None:
+ if neighbor['local_as'].get('as', None) is not None:
+ neighbor_cfg.update({'local-as': neighbor['local_as']['as']})
+ if neighbor['local_as'].get('no_prepend', None) is not None:
+ neighbor_cfg.update({'local-as-no-prepend': neighbor['local_as']['no_prepend']})
+ if neighbor['local_as'].get('replace_as', None) is not None:
+ neighbor_cfg.update({'local-as-replace-as': neighbor['local_as']['replace_as']})
+ if neighbor.get('local_address', None) is not None:
+ tmp_transport.update({'local-address': neighbor['local_address']})
+ if neighbor.get('passive', None) is not None:
+ tmp_transport.update({'passive-mode': neighbor['passive']})
+ if neighbor.get('remote_as', None) is not None:
+ have_nei = self.find_nei(have, bgp_as, vrf_name, neighbor)
+ if neighbor['remote_as'].get('peer_as', None) is not None:
+ if have_nei:
+ if have_nei.get("remote_as", None) is not None:
+ if have_nei["remote_as"].get("peer_type", None) is not None:
+ del_nei = {}
+ del_nei.update({'neighbor': have_nei['neighbor']})
+ del_nei.update({'remote_as': have_nei['remote_as']})
+ requests.extend(self.delete_specific_param_request(vrf_name, del_nei))
+ tmp_remote.update({'peer-as': neighbor['remote_as']['peer_as']})
+ if neighbor['remote_as'].get('peer_type', None) is not None:
+ if have_nei:
+ if have_nei.get("remote_as", None) is not None:
+ if have_nei["remote_as"].get("peer_as", None) is not None:
+ del_nei = {}
+ del_nei.update({'neighbor': have_nei['neighbor']})
+ del_nei.update({'remote_as': have_nei['remote_as']})
+ requests.extend(self.delete_specific_param_request(vrf_name, del_nei))
+ tmp_remote.update({'peer-type': neighbor['remote_as']['peer_type'].upper()})
+ if tmp_bfd:
+ bgp_neighbor.update({'enable-bfd': {'config': tmp_bfd}})
+ if tmp_ebgp:
+ bgp_neighbor.update({'ebgp-multihop': {'config': tmp_ebgp}})
+ if tmp_timers:
+ bgp_neighbor.update({'timers': {'config': tmp_timers}})
+ if tmp_transport:
+ bgp_neighbor.update({'transport': {'config': tmp_transport}})
+ if tmp_capability:
+ neighbor_cfg.update(tmp_capability)
+ if tmp_remote:
+ neighbor_cfg.update(tmp_remote)
+ if neighbor_cfg:
+ bgp_neighbor.update({'config': neighbor_cfg})
+ if bgp_neighbor:
+ bgp_neighbor_list.append(bgp_neighbor)
+ payload = {'openconfig-network-instance:neighbors': {'neighbor': bgp_neighbor_list}}
+ return payload, requests
+
+ def get_modify_bgp_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ for cmd in commands:
+ edit_path = '%s=%s/%s' % (self.network_instance_path, cmd['vrf_name'], self.protocol_bgp_path)
+ if 'peer_group' in cmd and cmd['peer_group']:
+ edit_peer_groups_payload, edit_requests = self.build_bgp_peer_groups_payload(cmd['peer_group'], have, cmd['bgp_as'], cmd['vrf_name'])
+ edit_peer_groups_path = edit_path + '/peer-groups'
+ if edit_requests:
+ requests.extend(edit_requests)
+ requests.append({'path': edit_peer_groups_path, 'method': PATCH, 'data': edit_peer_groups_payload})
+ if 'neighbors' in cmd and cmd['neighbors']:
+ edit_neighbors_payload, edit_requests = self.build_bgp_neighbors_payload(cmd['neighbors'], have, cmd['bgp_as'], cmd['vrf_name'])
+ edit_neighbors_path = edit_path + '/neighbors'
+ if edit_requests:
+ requests.extend(edit_requests)
+ requests.append({'path': edit_neighbors_path, 'method': PATCH, 'data': edit_neighbors_payload})
+ return requests
+
+ def get_delete_specific_bgp_peergroup_param_request(self, vrf_name, cmd, want_match):
+ requests = []
+ want_peer_group = want_match.get('peer_group', None)
+ for each in cmd['peer_group']:
+ if each:
+ name = each.get('name', None)
+ remote_as = each.get('remote_as', None)
+ timers = each.get('timers', None)
+ advertisement_interval = each.get('advertisement_interval', None)
+ bfd = each.get('bfd', None)
+ capability = each.get('capability', None)
+ address_family = each.get('address_family', None)
+ if name and not remote_as and not timers and not advertisement_interval and not bfd and not capability and not address_family:
+ want_pg_match = None
+ if want_peer_group:
+ want_pg_match = next((cfg for cfg in want_peer_group if cfg['name'] == name), None)
+ if want_pg_match:
+ keys = ['remote_as', 'timers', 'advertisement_interval', 'bfd', 'capability', 'address_family']
+ if not any(want_pg_match.get(key, None) for key in keys):
+ requests.append(self.get_delete_vrf_specific_peergroup_request(vrf_name, name))
+ else:
+ requests.extend(self.delete_specific_peergroup_param_request(vrf_name, each))
+ return requests
+
+ def delete_specific_peergroup_param_request(self, vrf_name, cmd):
+ requests = []
+ delete_static_path = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ delete_static_path = delete_static_path + '/peer-groups/peer-group=%s' % (cmd['name'])
+ if cmd.get('remote_as', None) is not None:
+ if cmd['remote_as'].get('peer_as', None) is not None:
+ delete_path = delete_static_path + '/config/peer-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ elif cmd['remote_as'].get('peer_type', None) is not None:
+ delete_path = delete_static_path + '/config/peer-type'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('advertisement_interval', None) is not None:
+ delete_path = delete_static_path + '/timers/config/minimum-advertisement-interval'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('timers', None) is not None:
+ if cmd['timers'].get('holdtime', None) is not None:
+ delete_path = delete_static_path + '/timers/config/hold-time'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['timers'].get('keepalive', None) is not None:
+ delete_path = delete_static_path + '/timers/config/keepalive-interval'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['timers'].get('connect_retry', None) is not None:
+ delete_path = delete_static_path + '/timers/config/connect-retry'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('capability', None) is not None:
+ if cmd['capability'].get('dynamic', None) is not None:
+ delete_path = delete_static_path + '/config/capability-dynamic'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['capability'].get('extended_nexthop', None) is not None:
+ delete_path = delete_static_path + '/config/capability-extended-nexthop'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('pg_description', None) is not None:
+ delete_path = delete_static_path + '/config/description'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('disable_connected_check', None) is not None:
+ delete_path = delete_static_path + '/config/disable-ebgp-connected-route-check'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('dont_negotiate_capability', None) is not None:
+ delete_path = delete_static_path + '/config/dont-negotiate-capability'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('enforce_first_as', None) is not None:
+ delete_path = delete_static_path + '/config/enforce-first-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('enforce_multihop', None) is not None:
+ delete_path = delete_static_path + '/config/enforce-multihop'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('override_capability', None) is not None:
+ delete_path = delete_static_path + '/config/override-capability'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('shutdown_msg', None) is not None:
+ delete_path = delete_static_path + '/config/shutdown-message'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('solo', None) is not None:
+ delete_path = delete_static_path + '/config/solo-peer'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('strict_capability_match', None) is not None:
+ delete_path = delete_static_path + '/config/strict-capability-match'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('ttl_security', None) is not None:
+ delete_path = delete_static_path + '/config/ttl-security-hops'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('local_as', None) is not None:
+ if cmd['local_as'].get('as', None) is not None:
+ delete_path = delete_static_path + '/config/local-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['local_as'].get('no_prepend', None) is not None:
+ delete_path = delete_static_path + '/config/local-as-no-prepend'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['local_as'].get('replace_as', None) is not None:
+ delete_path = delete_static_path + '/config/local-as-replace-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('local_address', None) is not None:
+ delete_path = delete_static_path + '/transport/config/local-address'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('passive', None) is not None:
+ delete_path = delete_static_path + '/transport/config/passive-mode'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('bfd', None) is not None:
+ if cmd['bfd'].get('enabled', None) is not None:
+ delete_path = delete_static_path + '/enable-bfd/config/enabled'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['bfd'].get('check_failure', None) is not None:
+ delete_path = delete_static_path + '/enable-bfd/config/check-control-plane-failure'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['bfd'].get('profile', None) is not None:
+ delete_path = delete_static_path + '/enable-bfd/config/bfd-profile'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('auth_pwd', None) is not None:
+ if cmd['auth_pwd'].get('pwd', None) is not None:
+ delete_path = delete_static_path + '/auth-password/config/password'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['auth_pwd'].get('encrypted', None) is not None:
+ delete_path = delete_static_path + '/auth-password/config/encrypted'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('ebgp_multihop', None) is not None:
+ if cmd['ebgp_multihop'].get('enabled', None) is not None:
+ delete_path = delete_static_path + '/ebgp-multihop/config/enabled'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['ebgp_multihop'].get('multihop_ttl', None) is not None:
+ delete_path = delete_static_path + '/ebgp-multihop/config/multihop_ttl'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('address_family', None) is not None:
+ if cmd['address_family'].get('afis', None) is None:
+ delete_path = delete_static_path + '/afi-safis/afi-safi'
+ requests.append({'path': delete_path, 'method': DELETE})
+ else:
+ for each in cmd['address_family']['afis']:
+ afi = each.get('afi', None)
+ safi = each.get('safi', None)
+ activate = each.get('activate', None)
+ allowas_in = each.get('allowas_in', None)
+ ip_afi = each.get('ip_afi', None)
+ prefix_limit = each.get('prefix_limit', None)
+ prefix_list_in = each.get('prefix_list_in', None)
+ prefix_list_out = each.get('prefix_list_out', None)
+ afi_safi = afi.upper() + '_' + safi.upper()
+ afi_safi_name = 'openconfig-bgp-types:' + afi_safi
+ if (afi and safi and not activate and not allowas_in and not ip_afi and not prefix_limit and not prefix_list_in
+ and not prefix_list_out):
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s' % (afi_safi_name)
+ requests.append({'path': delete_path, 'method': DELETE})
+ else:
+ if activate:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/config/enabled' % (afi_safi_name)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if allowas_in:
+ if allowas_in.get('origin', None):
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/allow-own-as/config/origin' % (afi_safi_name)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if allowas_in.get('value', None):
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/allow-own-as/config/as-count' % (afi_safi_name)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if prefix_list_in:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/prefix-list/config/import-policy' % (afi_safi_name)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if prefix_list_out:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/prefix-list/config/export-policy' % (afi_safi_name)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if afi_safi == 'IPV4_UNICAST':
+ if ip_afi:
+ requests.extend(self.delete_ip_afi_requests(ip_afi, afi_safi_name, 'ipv4-unicast', delete_static_path))
+ if prefix_limit:
+ requests.extend(self.delete_prefix_limit_requests(prefix_limit, afi_safi_name, 'ipv4-unicast', delete_static_path))
+ elif afi_safi == 'IPV6_UNICAST':
+ if ip_afi:
+ requests.extend(self.delete_ip_afi_requests(ip_afi, afi_safi_name, 'ipv6-unicast', delete_static_path))
+ if prefix_limit:
+ requests.extend(self.delete_prefix_limit_requests(prefix_limit, afi_safi_name, 'ipv6-unicast', delete_static_path))
+ elif afi_safi == 'L2VPN_EVPN':
+ if prefix_limit:
+ requests.extend(self.delete_prefix_limit_requests(prefix_limit, afi_safi_name, 'l2vpn-evpn', delete_static_path))
+
+ return requests
+
+ def delete_ip_afi_requests(self, ip_afi, afi_safi_name, afi_safi, delete_static_path):
+ requests = []
+ default_policy_name = ip_afi.get('default_policy_name', None)
+ send_default_route = ip_afi.get('send_default_route', None)
+ if default_policy_name:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/%s/config/default-policy-name' % (afi_safi_name, afi_safi)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if send_default_route:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/%s/config/send_default_route' % (afi_safi_name, afi_safi)
+ requests.append({'path': delete_path, 'method': DELETE})
+
+ return requests
+
+ def delete_prefix_limit_requests(self, prefix_limit, afi_safi_name, afi_safi, delete_static_path):
+ requests = []
+ max_prefixes = prefix_limit.get('max_prefixes', None)
+ prevent_teardown = prefix_limit.get('prevent_teardown', None)
+ warning_threshold = prefix_limit.get('warning_threshold', None)
+ restart_timer = prefix_limit.get('restart_timer', None)
+ if max_prefixes:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/%s/prefix-limit/config/max-prefixes' % (afi_safi_name, afi_safi)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if prevent_teardown:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/%s/prefix-limit/config/prevent-teardown' % (afi_safi_name, afi_safi)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if warning_threshold:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/%s/prefix-limit/config/warning-threshold-pct' % (afi_safi_name, afi_safi)
+ requests.append({'path': delete_path, 'method': DELETE})
+ if restart_timer:
+ delete_path = delete_static_path + '/afi-safis/afi-safi=%s/%s/prefix-limit/config/restart-timer' % (afi_safi_name, afi_safi)
+ requests.append({'path': delete_path, 'method': DELETE})
+
+ return requests
+
+ def get_delete_specific_bgp_param_request(self, vrf_name, cmd, want_match):
+ requests = []
+ want_neighbors = want_match.get('neighbors', None)
+ for each in cmd['neighbors']:
+ if each:
+ neighbor = each.get('neighbor', None)
+ remote_as = each.get('remote_as', None)
+ peer_group = each.get('peer_group', None)
+ timers = each.get('timers', None)
+ advertisement_interval = each.get('advertisement_interval', None)
+ bfd = each.get('bfd', None)
+ capability = each.get('capability', None)
+ if neighbor and not remote_as and not peer_group and not timers and not advertisement_interval and not bfd and not capability:
+ want_nei_match = None
+ if want_neighbors:
+ want_nei_match = next(cfg for cfg in want_neighbors if cfg['neighbor'] == neighbor)
+ if want_nei_match:
+ keys = ['remote_as', 'peer_group', 'timers', 'advertisement_interval', 'bfd', 'capability']
+ if not any(want_nei_match.get(key, None) for key in keys):
+ requests.append(self.delete_neighbor_whole_request(vrf_name, neighbor))
+ else:
+ requests.extend(self.delete_specific_param_request(vrf_name, each))
+ return requests
+
+ def delete_neighbor_whole_request(self, vrf_name, neighbor):
+ requests = []
+ url = '%s=%s/%s/%s=%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.neighbor_path, neighbor)
+ return ({'path': url, 'method': DELETE})
+
+ def delete_specific_param_request(self, vrf_name, cmd):
+ requests = []
+ delete_static_path = '%s=%s/%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path)
+ delete_static_path = delete_static_path + '/neighbors/neighbor=%s' % (cmd['neighbor'])
+ if cmd.get('remote_as', None) is not None:
+ if cmd['remote_as'].get('peer_as', None) is not None:
+ delete_path = delete_static_path + '/config/peer-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ elif cmd['remote_as'].get('peer_type', None) is not None:
+ delete_path = delete_static_path + '/config/peer-type'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('peer_group', None) is not None:
+ delete_path = delete_static_path + '/config/peer-group'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('nbr_description', None) is not None:
+ delete_path = delete_static_path + '/config/description'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('disable_connected_check', None) is not None:
+ delete_path = delete_static_path + '/config/disable-ebgp-connected-route-check'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('dont_negotiate_capability', None) is not None:
+ delete_path = delete_static_path + '/config/dont-negotiate-capability'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('enforce_first_as', None) is not None:
+ delete_path = delete_static_path + '/config/enforce-first-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('enforce_multihop', None) is not None:
+ delete_path = delete_static_path + '/config/enforce-multihop'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('override_capability', None) is not None:
+ delete_path = delete_static_path + '/config/override-capability'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('port', None) is not None:
+ delete_path = delete_static_path + '/config/peer-port'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('shutdown_msg', None) is not None:
+ delete_path = delete_static_path + '/config/shutdown-message'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('solo', None) is not None:
+ delete_path = delete_static_path + '/config/solo-peer'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('strict_capability_match', None) is not None:
+ delete_path = delete_static_path + '/config/strict-capability-match'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('ttl_security', None) is not None:
+ delete_path = delete_static_path + '/config/ttl-security-hops'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('v6only', None) is not None:
+ delete_path = delete_static_path + '/config/openconfig-bgp-ext:v6only'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('local_as', None) is not None:
+ if cmd['local_as'].get('as', None) is not None:
+ delete_path = delete_static_path + '/config/local-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['local_as'].get('no_prepend', None) is not None:
+ delete_path = delete_static_path + '/config/local-as-no-prepend'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['local_as'].get('replace_as', None) is not None:
+ delete_path = delete_static_path + '/config/local-as-replace-as'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('local_address', None) is not None:
+ delete_path = delete_static_path + '/transport/config/local-address'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('passive', None) is not None:
+ delete_path = delete_static_path + '/transport/config/passive-mode'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('advertisement_interval', None) is not None:
+ delete_path = delete_static_path + '/timers/config/minimum-advertisement-interval'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('timers', None) is not None:
+ if cmd['timers'].get('holdtime', None) is not None:
+ delete_path = delete_static_path + '/timers/config/hold-time'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['timers'].get('keepalive', None) is not None:
+ delete_path = delete_static_path + '/timers/config/keepalive-interval'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['timers'].get('connect_retry', None) is not None:
+ delete_path = delete_static_path + '/timers/config/connect-retry'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('capability', None) is not None:
+ if cmd['capability'].get('dynamic', None) is not None:
+ delete_path = delete_static_path + '/config/capability-dynamic'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['capability'].get('extended_nexthop', None) is not None:
+ delete_path = delete_static_path + '/config/capability-extended-nexthop'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('bfd', None) is not None:
+ if cmd['bfd'].get('enabled', None) is not None:
+ delete_path = delete_static_path + '/enable-bfd/config/enabled'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['bfd'].get('check_failure', None) is not None:
+ delete_path = delete_static_path + '/enable-bfd/config/check-control-plane-failure'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['bfd'].get('profile', None) is not None:
+ delete_path = delete_static_path + '/enable-bfd/config/bfd-profile'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('auth_pwd', None) is not None:
+ if cmd['auth_pwd'].get('pwd', None) is not None:
+ delete_path = delete_static_path + '/auth-password/config/password'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['auth_pwd'].get('encrypted', None) is not None:
+ delete_path = delete_static_path + '/auth-password/config/encrypted'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd.get('ebgp_multihop', None) is not None:
+ if cmd['ebgp_multihop'].get('enabled', None) is not None:
+ delete_path = delete_static_path + '/ebgp-multihop/config/enabled'
+ requests.append({'path': delete_path, 'method': DELETE})
+ if cmd['ebgp_multihop'].get('multihop_ttl', None) is not None:
+ delete_path = delete_static_path + '/ebgp-multihop/config/multihop_ttl'
+ requests.append({'path': delete_path, 'method': DELETE})
+
+ return requests
+
+ def get_delete_vrf_specific_neighbor_request(self, vrf_name, have):
+ requests = []
+ for each in have:
+ if each.get('neighbor', None):
+ requests.append(self.delete_neighbor_whole_request(vrf_name, each['neighbor']))
+ return requests
+
+ def get_delete_vrf_specific_peergroup_request(self, vrf_name, peergroup_name):
+ requests = []
+ delete_neighbor_path = '%s=%s/%s/peer-groups/peer-group=%s' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, peergroup_name)
+ return ({'path': delete_neighbor_path, 'method': DELETE})
+
+ def get_delete_all_bgp_neighbor_requests(self, commands):
+ requests = []
+ for cmd in commands:
+ if cmd.get('neighbors', None):
+ requests.extend(self.get_delete_vrf_specific_neighbor_request(cmd['vrf_name'], cmd['neighbors']))
+ if 'peer_group' in cmd and cmd['peer_group']:
+ for each in cmd['peer_group']:
+ requests.append(self.get_delete_vrf_specific_peergroup_request(cmd['vrf_name'], each['name']))
+ return requests
+
+ def get_delete_bgp_neighbor_requests(self, commands, have, want, is_delete_all):
+ requests = []
+ if is_delete_all:
+ requests = self.get_delete_all_bgp_neighbor_requests(commands)
+ else:
+ for cmd in commands:
+ vrf_name = cmd['vrf_name']
+ as_val = cmd['bgp_as']
+ neighbors = cmd.get('neighbors', None)
+ peer_group = cmd.get('peer_group', None)
+ want_match = next((cfg for cfg in want if vrf_name == cfg['vrf_name'] and as_val == cfg['bgp_as']), None)
+ want_neighbors = want_match.get('neighbors', None)
+ want_peer_group = want_match.get('peer_group', None)
+ if neighbors is None and peer_group is None and want_neighbors is None and want_peer_group is None:
+ new_cmd = {}
+ for each in have:
+ if vrf_name == each['vrf_name'] and as_val == each['bgp_as']:
+ new_neighbors = []
+ new_pg = []
+ if each.get('neighbors', None):
+ new_neighbors = [{'neighbor': i['neighbor']} for i in each.get('neighbors', None)]
+ if each.get('peer_group', None):
+ new_pg = [{'name': i['name']} for i in each.get('peer_group', None)]
+ if new_neighbors:
+ new_cmd['neighbors'] = new_neighbors
+ requests.extend(self.get_delete_vrf_specific_neighbor_request(vrf_name, new_cmd['neighbors']))
+ if new_pg:
+ new_cmd['name'] = new_pg
+ for each in new_cmd['name']:
+ requests.append(self.get_delete_vrf_specific_peergroup_request(vrf_name, each['name']))
+ break
+ else:
+ if neighbors:
+ requests.extend(self.get_delete_specific_bgp_param_request(vrf_name, cmd, want_match))
+ if peer_group:
+ requests.extend(self.get_delete_specific_bgp_peergroup_param_request(vrf_name, cmd, want_match))
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py
new file mode 100644
index 00000000..15f46f96
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/bgp_neighbors_af/bgp_neighbors_af.py
@@ -0,0 +1,584 @@
+#
+# -*- 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 sonic_bgp_neighbors_af 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
+
+try:
+ from urllib import quote
+except ImportError:
+ from urllib.parse import quote
+
+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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ validate_bgps,
+ normalize_neighbors_interface_name,
+ get_ip_afi_cfg_payload,
+ get_prefix_limit_payload
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'delete'
+TEST_KEYS = [
+ {'config': {'vrf_name': '', 'bgp_as': ''}},
+ {'neighbors': {'neighbor': ''}},
+ {'address_family': {'afi': '', 'safi': ''}},
+ {'route_map': {'name': '', 'direction': ''}},
+]
+
+
+class Bgp_neighbors_af(ConfigBase):
+ """
+ The sonic_bgp_neighbors_af class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'bgp_neighbors_af',
+ ]
+
+ network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+ protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp'
+ neighbor_path = 'neighbors/neighbor'
+ afi_safi_path = 'afi-safis/afi-safi'
+ activate_path = "/config/enabled"
+ ref_client_path = "/config/route-reflector-client"
+ serv_client_path = "/config/route-server-client"
+ allowas_origin_path = "/allow-own-as/config/origin"
+ allowas_value_path = "/allow-own-as/config/as-count"
+ allowas_enabled_path = "/allow-own-as/config/enabled"
+ prefix_list_in_path = "/prefix-list/config/import-policy"
+ prefix_list_out_path = "/prefix-list/config/export-policy"
+ def_policy_name_path = "/%s/config/default-policy-name"
+ send_def_route_path = "/%s/config/send-default-route"
+ max_prefixes_path = "/%s/prefix-limit/config/max-prefixes"
+ prv_teardown_path = "/%s/prefix-limit/config/prevent-teardown"
+ restart_timer_path = "/%s/prefix-limit/config/restart-timer"
+ wrn_threshold_path = "/%s/prefix-limit/config/warning-threshold-pct"
+
+ def __init__(self, module):
+ super(Bgp_neighbors_af, self).__init__(module)
+
+ def get_bgp_neighbors_af_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)
+ bgp_neighbors_af_facts = facts['ansible_network_resources'].get('bgp_neighbors_af')
+ if not bgp_neighbors_af_facts:
+ bgp_neighbors_af_facts = []
+ return bgp_neighbors_af_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_bgp_neighbors_af_facts = self.get_bgp_neighbors_af_facts()
+ commands, requests = self.set_config(existing_bgp_neighbors_af_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_bgp_neighbors_af_facts = self.get_bgp_neighbors_af_facts()
+
+ result['before'] = existing_bgp_neighbors_af_facts
+ if result['changed']:
+ result['after'] = changed_bgp_neighbors_af_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_bgp_neighbors_af_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']
+ normalize_neighbors_interface_name(want, self._module)
+ have = existing_bgp_neighbors_af_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 = []
+ requests = []
+ state = self._module.params['state']
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ validate_bgps(self._module, want, have)
+ requests = self.get_modify_bgp_neighbors_af_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the bgp_neighbors_afs
+ is_delete_all = False
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_bgp_neighbors_af_requests(commands, have, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def set_val(self, cfg, var, src_key, des_key):
+ value = var.get(src_key, None)
+ if value is not None:
+ cfg[des_key] = value
+
+ def get_allowas_in(self, match, conf_neighbor_val, conf_afi, conf_safi):
+ mat_allowas_in = None
+ if match:
+ mat_neighbors = match.get('neighbors', None)
+ if mat_neighbors:
+ mat_neighbor = next((nei for nei in mat_neighbors if nei['neighbor'] == conf_neighbor_val), None)
+ if mat_neighbor:
+ mat_nei_addr_fams = mat_neighbor.get('address_family', [])
+ if mat_nei_addr_fams:
+ mat_nei_addr_fam = next((af for af in mat_nei_addr_fams if (af['afi'] == conf_afi and af['safi'] == conf_safi)), None)
+ if mat_nei_addr_fam:
+ mat_allowas_in = mat_nei_addr_fam.get('allowas_in', None)
+ return mat_allowas_in
+
+ def get_single_neighbors_af_modify_request(self, match, vrf_name, conf_neighbor_val, conf_neighbor):
+ requests = []
+ conf_nei_addr_fams = conf_neighbor.get('address_family', [])
+ url = '%s=%s/%s/%s=%s/afi-safis' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.neighbor_path, conf_neighbor_val)
+ payload = {}
+ afi_safis = []
+ if not conf_nei_addr_fams:
+ return requests
+
+ for conf_nei_addr_fam in conf_nei_addr_fams:
+ afi_safi = {}
+ conf_afi = conf_nei_addr_fam.get('afi', None)
+ conf_safi = conf_nei_addr_fam.get('safi', None)
+ afi_safi_val = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ del_url = '%s=%s/%s/%s=%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.neighbor_path, conf_neighbor_val)
+ del_url += '%s=openconfig-bgp-types:%s' % (self.afi_safi_path, afi_safi_val)
+
+ afi_safi_cfg = {}
+ if conf_afi and conf_safi:
+ afi_safi_name = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ afi_safi['afi-safi-name'] = afi_safi_name
+ afi_safi_cfg['afi-safi-name'] = afi_safi_name
+
+ self.set_val(afi_safi_cfg, conf_nei_addr_fam, 'activate', 'enabled')
+ self.set_val(afi_safi_cfg, conf_nei_addr_fam, 'route_reflector_client', 'route-reflector-client')
+ self.set_val(afi_safi_cfg, conf_nei_addr_fam, 'route_server_client', 'route-server-client')
+
+ if afi_safi_cfg:
+ afi_safi['config'] = afi_safi_cfg
+
+ policy_cfg = {}
+ conf_route_map = conf_nei_addr_fam.get('route_map', None)
+ if conf_route_map:
+ for route in conf_route_map:
+ policy_key = "import-policy" if "in" == route['direction'] else "export-policy"
+ route_name = route['name']
+ policy_cfg[policy_key] = [route_name]
+ if policy_cfg:
+ afi_safi['apply-policy'] = {'config': policy_cfg}
+
+ pfx_lst_cfg = {}
+ conf_prefix_list_in = conf_nei_addr_fam.get('prefix_list_in', None)
+ conf_prefix_list_out = conf_nei_addr_fam.get('prefix_list_out', None)
+ if conf_prefix_list_in:
+ pfx_lst_cfg['import-policy'] = conf_prefix_list_in
+ if conf_prefix_list_out:
+ pfx_lst_cfg['export-policy'] = conf_prefix_list_out
+ if pfx_lst_cfg:
+ afi_safi['prefix-list'] = {'config': pfx_lst_cfg}
+
+ ip_dict = {}
+ ip_afi_cfg = {}
+ pfx_lmt_cfg = {}
+ conf_ip_afi = conf_nei_addr_fam.get('ip_afi')
+ conf_prefix_limit = conf_nei_addr_fam.get('prefix_limit')
+ if conf_prefix_limit:
+ pfx_lmt_cfg = get_prefix_limit_payload(conf_prefix_limit)
+ if pfx_lmt_cfg and afi_safi_val == 'L2VPN_EVPN':
+ afi_safi['l2vpn-evpn'] = {'prefix-limit': {'config': pfx_lmt_cfg}}
+ else:
+ if conf_ip_afi:
+ ip_afi_cfg = get_ip_afi_cfg_payload(conf_ip_afi)
+ if ip_afi_cfg:
+ ip_dict['config'] = ip_afi_cfg
+ if pfx_lmt_cfg:
+ ip_dict['prefix-limit'] = {'config': pfx_lmt_cfg}
+ if ip_dict and afi_safi_val == 'IPV4_UNICAST':
+ afi_safi['ipv4-unicast'] = ip_dict
+ elif ip_dict and afi_safi_val == 'IPV6_UNICAST':
+ afi_safi['ipv6-unicast'] = ip_dict
+
+ allowas_in_cfg = {}
+ conf_allowas_in = conf_nei_addr_fam.get('allowas_in', None)
+ if conf_allowas_in:
+ mat_allowas_in = self.get_allowas_in(match, conf_neighbor_val, conf_afi, conf_safi)
+ origin = conf_allowas_in.get('origin', None)
+ if origin is not None:
+ if mat_allowas_in:
+ mat_value = mat_allowas_in.get('value', None)
+ if mat_value:
+ self.append_delete_request(requests, mat_value, mat_allowas_in, 'value', del_url, self.allowas_value_path)
+ allowas_in_cfg['origin'] = origin
+ else:
+ value = conf_allowas_in.get('value', None)
+ if value is not None:
+ if mat_allowas_in:
+ mat_origin = mat_allowas_in.get('origin', None)
+ if mat_origin:
+ self.append_delete_request(requests, mat_origin, mat_allowas_in, 'origin', del_url, self.allowas_origin_path)
+ allowas_in_cfg['as-count'] = value
+ if allowas_in_cfg:
+ allowas_in_cfg['enabled'] = True
+ afi_safi['allow-own-as'] = {'config': allowas_in_cfg}
+
+ if afi_safi:
+ afi_safis.append(afi_safi)
+
+ if afi_safis:
+ payload = {"openconfig-network-instance:afi-safis": {"afi-safi": afi_safis}}
+ requests.append({'path': url, 'method': PATCH, 'data': payload})
+
+ return requests
+
+ def get_delete_neighbor_af_routemaps_requests(self, vrf_name, conf_neighbor_val, afi, safi, routes):
+ requests = []
+ for route in routes:
+ afi_safi_name = ("%s_%s" % (afi, safi)).upper()
+ policy_type = "import-policy" if "in" == route['direction'] else "export-policy"
+ url = '%s=%s/%s/%s=%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.neighbor_path, conf_neighbor_val)
+ url += ('%s=%s/apply-policy/config/%s' % (self.afi_safi_path, afi_safi_name, policy_type))
+ requests.append({'path': url, 'method': DELETE})
+ return requests
+
+ def get_all_neighbors_af_modify_requests(self, match, conf_neighbors, vrf_name):
+ requests = []
+ for conf_neighbor in conf_neighbors:
+ conf_neighbor_val = conf_neighbor.get('neighbor', None)
+ if conf_neighbor_val:
+ requests.extend(self.get_single_neighbors_af_modify_request(match, vrf_name, conf_neighbor_val, conf_neighbor))
+ return requests
+
+ def get_modify_requests(self, conf, match, vrf_name):
+ requests = []
+ conf_neighbors = conf.get('neighbors', [])
+ mat_neighbors = []
+ if match and match.get('neighbors', None):
+ mat_neighbors = match.get('neighbors')
+
+ if conf_neighbors:
+ for conf_neighbor in conf_neighbors:
+ conf_neighbor_val = conf_neighbor.get('neighbor', None)
+ if conf_neighbor_val is None:
+ continue
+
+ mat_neighbor = next((e_neighbor for e_neighbor in mat_neighbors if e_neighbor['neighbor'] == conf_neighbor_val), None)
+ if mat_neighbor is None:
+ continue
+
+ conf_nei_addr_fams = conf_neighbor.get('address_family', None)
+ mat_nei_addr_fams = mat_neighbor.get('address_family', None)
+ if conf_nei_addr_fams is None or mat_nei_addr_fams is None:
+ continue
+
+ for conf_nei_addr_fam in conf_nei_addr_fams:
+ afi = conf_nei_addr_fam.get('afi', None)
+ safi = conf_nei_addr_fam.get('safi', None)
+ if afi is None or safi is None:
+ continue
+
+ mat_nei_addr_fam = next((addr_fam for addr_fam in mat_nei_addr_fams if (addr_fam['afi'] == afi and addr_fam['safi'] == safi)), None)
+ if mat_nei_addr_fam is None:
+ continue
+
+ conf_route_map = conf_nei_addr_fam.get('route_map', None)
+ mat_route_map = mat_nei_addr_fam.get('route_map', None)
+ if conf_route_map is None or mat_route_map is None:
+ continue
+
+ del_routes = []
+ for route in conf_route_map:
+ exist_route = next((e_route for e_route in mat_route_map if e_route['direction'] == route['direction']), None)
+ if exist_route:
+ del_routes.append(exist_route)
+ if del_routes:
+ requests.extend(self.get_delete_neighbor_af_routemaps_requests(vrf_name, conf_neighbor_val, afi, safi, del_routes))
+
+ requests.extend(self.get_all_neighbors_af_modify_requests(match, conf_neighbors, vrf_name))
+ return requests
+
+ def get_modify_bgp_neighbors_af_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ # Create URL and payload
+ for conf in commands:
+ vrf_name = conf['vrf_name']
+ as_val = conf['bgp_as']
+
+ match = next((cfg for cfg in have if (cfg['vrf_name'] == vrf_name and (cfg['bgp_as'] == as_val))), None)
+ modify_reqs = self.get_modify_requests(conf, match, vrf_name)
+ if modify_reqs:
+ requests.extend(modify_reqs)
+
+ return requests
+
+ def append_delete_request(self, requests, cur_var, mat_var, key, url, path):
+ ret_value = False
+ request = None
+ if cur_var is not None and mat_var.get(key, None):
+ requests.append({'path': url + path, 'method': DELETE})
+ ret_value = True
+ return ret_value
+
+ def delete_ip_afi_requests(self, conf_ip_afi, mat_ip_afi, conf_afi_safi_val, url):
+ requests = []
+ default_policy_name = conf_ip_afi.get('default_policy_name', None)
+ send_default_route = conf_ip_afi.get('send_default_route', None)
+ if default_policy_name:
+ self.append_delete_request(requests, default_policy_name, mat_ip_afi, 'default_policy_name', url, self.def_policy_name_path % (conf_afi_safi_val))
+ if send_default_route:
+ self.append_delete_request(requests, send_default_route, mat_ip_afi, 'send_default_route', url, self.send_def_route_path % (conf_afi_safi_val))
+
+ return requests
+
+ def delete_prefix_limit_requests(self, conf_prefix_limit, mat_prefix_limit, conf_afi_safi_val, url):
+ requests = []
+ max_prefixes = conf_prefix_limit.get('max_prefixes', None)
+ prevent_teardown = conf_prefix_limit.get('prevent_teardown', None)
+ restart_timer = conf_prefix_limit.get('restart_timer', None)
+ warning_threshold = conf_prefix_limit.get('warning_threshold', None)
+ if max_prefixes:
+ self.append_delete_request(requests, max_prefixes, mat_prefix_limit, 'max_prefixes', url, self.max_prefixes_path % (conf_afi_safi_val))
+ if prevent_teardown:
+ self.append_delete_request(requests, prevent_teardown, mat_prefix_limit, 'prevent_teardown', url, self.prv_teardown_path % (conf_afi_safi_val))
+ if restart_timer:
+ self.append_delete_request(requests, restart_timer, mat_prefix_limit, 'restart_timer', url, self.restart_timer_path % (conf_afi_safi_val))
+ if warning_threshold:
+ self.append_delete_request(requests, warning_threshold, mat_prefix_limit, 'warning_threshold', url, self.wrn_threshold_path % (conf_afi_safi_val))
+
+ return requests
+
+ def process_delete_specific_params(self, vrf_name, conf_neighbor_val, conf_nei_addr_fam, conf_afi, conf_safi, matched_nei_addr_fams, url):
+ requests = []
+ conf_afi_safi_val = ("%s-%s" % (conf_afi, conf_safi))
+
+ mat_nei_addr_fam = None
+ if matched_nei_addr_fams:
+ mat_nei_addr_fam = next((e_af for e_af in matched_nei_addr_fams if (e_af['afi'] == conf_afi and e_af['safi'] == conf_safi)), None)
+
+ if mat_nei_addr_fam:
+ conf_alllowas_in = conf_nei_addr_fam.get('allowas_in', None)
+ conf_activate = conf_nei_addr_fam.get('activate', None)
+ conf_route_map = conf_nei_addr_fam.get('route_map', None)
+ conf_route_reflector_client = conf_nei_addr_fam.get('route_reflector_client', None)
+ conf_route_server_client = conf_nei_addr_fam.get('route_server_client', None)
+ conf_prefix_list_in = conf_nei_addr_fam.get('prefix_list_in', None)
+ conf_prefix_list_out = conf_nei_addr_fam.get('prefix_list_out', None)
+ conf_ip_afi = conf_nei_addr_fam.get('ip_afi', None)
+ conf_prefix_limit = conf_nei_addr_fam.get('prefix_limit', None)
+
+ var_list = [conf_alllowas_in, conf_activate, conf_route_map, conf_route_reflector_client, conf_route_server_client,
+ conf_prefix_list_in, conf_prefix_list_out, conf_ip_afi, conf_prefix_limit]
+ if len(list(filter(lambda var: (var is None), var_list))) == len(var_list):
+ requests.append({'path': url, 'method': DELETE})
+ else:
+ mat_route_map = mat_nei_addr_fam.get('route_map', None)
+ if conf_route_map and mat_route_map:
+ del_routes = []
+ for route in conf_route_map:
+ if any(e_route for e_route in mat_route_map if route['direction'] == e_route['direction']):
+ del_routes.append(route)
+ if del_routes:
+ requests.extend(self.get_delete_neighbor_af_routemaps_requests(vrf_name, conf_neighbor_val, conf_afi, conf_safi, del_routes))
+
+ self.append_delete_request(requests, conf_activate, mat_nei_addr_fam, 'activate', url, self.activate_path)
+ self.append_delete_request(requests, conf_route_reflector_client, mat_nei_addr_fam, 'route_reflector_client', url, self.ref_client_path)
+ self.append_delete_request(requests, conf_route_server_client, mat_nei_addr_fam, 'route_server_client', url, self.serv_client_path)
+ self.append_delete_request(requests, conf_prefix_list_in, mat_nei_addr_fam, 'prefix_list_in', url, self.prefix_list_in_path)
+ self.append_delete_request(requests, conf_prefix_list_out, mat_nei_addr_fam, 'prefix_list_out', url, self.prefix_list_out_path)
+
+ mat_alllowas_in = mat_nei_addr_fam.get('allowas_in', None)
+ if conf_alllowas_in is not None and mat_alllowas_in:
+ origin = conf_alllowas_in.get('origin', None)
+ if origin is not None:
+ if self.append_delete_request(requests, origin, mat_alllowas_in, 'origin', url, self.allowas_origin_path):
+ self.append_delete_request(requests, True, {'enabled': True}, 'enabled', url, self.allowas_enabled_path)
+ else:
+ value = conf_alllowas_in.get('value', None)
+ if value is not None:
+ if self.append_delete_request(requests, value, mat_alllowas_in, 'value', url, self.allowas_value_path):
+ self.append_delete_request(requests, True, {'enabled': True}, 'enabled', url, self.allowas_enabled_path)
+
+ mat_ip_afi = mat_nei_addr_fam.get('ip_afi', None)
+ mat_prefix_limit = mat_nei_addr_fam.get('prefix_limit', None)
+ if conf_ip_afi and mat_ip_afi:
+ requests.extend(self.delete_ip_afi_requests(conf_ip_afi, mat_ip_afi, conf_afi_safi_val, url))
+ if conf_prefix_limit and mat_prefix_limit:
+ requests.extend(self.delete_prefix_limit_requests(conf_prefix_limit, mat_prefix_limit, conf_afi_safi_val, url))
+
+ return requests
+
+ def process_neighbor_delete_address_families(self, vrf_name, conf_nei_addr_fams, matched_nei_addr_fams, neighbor_val, is_delete_all):
+ requests = []
+
+ for conf_nei_addr_fam in conf_nei_addr_fams:
+ conf_afi = conf_nei_addr_fam.get('afi', None)
+ conf_safi = conf_nei_addr_fam.get('safi', None)
+ if not conf_afi or not conf_safi:
+ continue
+ afi_safi = ("%s_%s" % (conf_afi, conf_safi)).upper()
+ url = '%s=%s/%s/%s=%s/' % (self.network_instance_path, vrf_name, self.protocol_bgp_path, self.neighbor_path, neighbor_val)
+ url += '%s=openconfig-bgp-types:%s' % (self.afi_safi_path, afi_safi)
+ if is_delete_all:
+ requests.append({'path': url, 'method': DELETE})
+ else:
+ requests.extend(self.process_delete_specific_params(vrf_name, neighbor_val, conf_nei_addr_fam, conf_afi, conf_safi, matched_nei_addr_fams, url))
+
+ return requests
+
+ def get_delete_single_bgp_neighbors_af_request(self, conf, is_delete_all, match=None):
+ requests = []
+ vrf_name = conf['vrf_name']
+ conf_neighbors = conf.get('neighbors', [])
+
+ if match and not conf_neighbors:
+ conf_neighbors = match.get('neighbors', [])
+ if conf_neighbors:
+ conf_neighbors = [{'neighbor': nei['neighbor']} for nei in conf_neighbors]
+
+ if not conf_neighbors:
+ return requests
+ mat_neighbors = None
+ if match:
+ mat_neighbors = match.get('neighbors', [])
+
+ for conf_neighbor in conf_neighbors:
+ conf_neighbor_val = conf_neighbor.get('neighbor', None)
+ if not conf_neighbor_val:
+ continue
+
+ mat_neighbor = None
+ if mat_neighbors:
+ mat_neighbor = next((e_nei for e_nei in mat_neighbors if e_nei['neighbor'] == conf_neighbor_val), None)
+
+ conf_nei_addr_fams = conf_neighbor.get('address_family', None)
+ if mat_neighbor and not conf_nei_addr_fams:
+ conf_nei_addr_fams = mat_neighbor.get('address_family', None)
+ if conf_nei_addr_fams:
+ conf_nei_addr_fams = [{'afi': af['afi'], 'safi': af['safi']} for af in conf_nei_addr_fams]
+
+ if not conf_nei_addr_fams:
+ continue
+
+ mat_nei_addr_fams = None
+ if mat_neighbor:
+ mat_nei_addr_fams = mat_neighbor.get('address_family', None)
+
+ requests.extend(self.process_neighbor_delete_address_families(vrf_name, conf_nei_addr_fams, mat_nei_addr_fams, conf_neighbor_val, is_delete_all))
+
+ return requests
+
+ def get_delete_bgp_neighbors_af_requests(self, commands, have, is_delete_all):
+ requests = []
+ for cmd in commands:
+ vrf_name = cmd['vrf_name']
+ as_val = cmd['bgp_as']
+ match = None
+ if not is_delete_all:
+ match = next((have_cfg for have_cfg in have if have_cfg['vrf_name'] == vrf_name and have_cfg['bgp_as'] == as_val), None)
+ requests.extend(self.get_delete_single_bgp_neighbors_af_request(cmd, is_delete_all, match))
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py
new file mode 100644
index 00000000..acf985eb
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/interfaces/interfaces.py
@@ -0,0 +1,354 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_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
+
+try:
+ from urllib import quote
+except ImportError:
+ from urllib.parse import quote
+
+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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import (
+ Facts,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.interfaces_util import (
+ build_interfaces_create_request,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states,
+ normalize_interface_name
+)
+from ansible.module_utils._text import to_native
+from ansible.module_utils.connection import ConnectionError
+import traceback
+
+LIB_IMP_ERR = None
+ERR_MSG = None
+try:
+ import requests
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+PATCH = 'patch'
+DELETE = 'delete'
+
+
+class Interfaces(ConfigBase):
+ """
+ The sonic_interfaces class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'interfaces',
+ ]
+
+ params = ('description', 'mtu', 'enabled')
+ delete_flag = False
+
+ def __init__(self, module):
+ super(Interfaces, self).__init__(module)
+
+ def get_interfaces_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)
+ interfaces_facts = facts['ansible_network_resources'].get('interfaces')
+ if not interfaces_facts:
+ return []
+
+ return interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+
+ existing_interfaces_facts = self.get_interfaces_facts()
+ commands, requests = self.set_config(existing_interfaces_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_interfaces_facts = self.get_interfaces_facts()
+
+ result['before'] = existing_interfaces_facts
+ if result['changed']:
+ result['after'] = 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
+ """
+ want = self._module.params['config']
+ normalize_interface_name(want, self._module)
+ have = 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']
+ # diff method works on dict, so creating temp dict
+ diff = get_diff(want, have)
+ # removing the dict in case diff found
+
+ if state == 'overridden':
+ have = [each_intf for each_intf in have if each_intf['name'].startswith('Ethernet')]
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+
+ return commands, requests
+
+ def _state_replaced(self, want, have, diff):
+ """ The command generator when state is replaced
+
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :param interface_type: interface type
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = self.filter_comands_to_change(diff, have)
+ requests = self.get_delete_interface_requests(commands, have)
+ requests.extend(self.get_modify_interface_requests(commands, have))
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "replaced")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_overridden(self, want, have, diff):
+ """ The command generator when state is overridden
+
+ :param want: the desired configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ commands_del = self.filter_comands_to_change(want, have)
+ requests = self.get_delete_interface_requests(commands_del, have)
+ del_req_count = len(requests)
+ if commands_del and del_req_count > 0:
+ commands_del = update_states(commands_del, "deleted")
+ commands.extend(commands_del)
+
+ commands_over = diff
+ requests.extend(self.get_modify_interface_requests(commands_over, have))
+ if commands_over and len(requests) > del_req_count:
+ commands_over = update_states(commands_over, "overridden")
+ commands.extend(commands_over)
+
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_interface_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :param interface_type: interface type
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the interfaces
+ if not want:
+ commands = have
+ else:
+ commands = want
+
+ requests = self.get_delete_interface_requests(commands, have)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def filter_comands_to_delete(self, configs, have):
+ commands = []
+
+ for conf in configs:
+ if self.is_this_delete_required(conf, have):
+ temp_conf = dict()
+ temp_conf['name'] = conf['name']
+ temp_conf['description'] = ''
+ temp_conf['mtu'] = 9100
+ temp_conf['enabled'] = True
+ commands.append(temp_conf)
+ return commands
+
+ def filter_comands_to_change(self, configs, have):
+ commands = []
+ if configs:
+ for conf in configs:
+ if self.is_this_change_required(conf, have):
+ commands.append(conf)
+ return commands
+
+ def get_modify_interface_requests(self, configs, have):
+ self.delete_flag = False
+ commands = self.filter_comands_to_change(configs, have)
+
+ return self.get_interface_requests(commands, have)
+
+ def get_delete_interface_requests(self, configs, have):
+ self.delete_flag = True
+ commands = self.filter_comands_to_delete(configs, have)
+
+ return self.get_interface_requests(commands, have)
+
+ def get_interface_requests(self, configs, have):
+ requests = []
+ if not configs:
+ return requests
+
+ # Create URL and payload
+ for conf in configs:
+ name = conf["name"]
+ if self.delete_flag and name.startswith('Loopback'):
+ method = DELETE
+ url = 'data/openconfig-interfaces:interfaces/interface=%s' % quote(name, safe='')
+ request = {"path": url, "method": method}
+ else:
+ # Create Loopback in case not availble in have
+ if name.startswith('Loopback'):
+ have_conf = next((cfg for cfg in have if cfg['name'] == name), None)
+ if not have_conf:
+ loopback_create_request = build_interfaces_create_request(name)
+ requests.append(loopback_create_request)
+ method = PATCH
+ url = 'data/openconfig-interfaces:interfaces/interface=%s/config' % quote(name, safe='')
+ payload = self.build_create_payload(conf)
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def is_this_delete_required(self, conf, have):
+ if conf['name'] == "eth0":
+ return False
+ intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None)
+ if intf:
+ if (intf['name'].startswith('Loopback') or not ((intf.get('description') is None or intf.get('description') == '') and
+ (intf.get('enabled') is None or intf.get('enabled') is True) and (intf.get('mtu') is None or intf.get('mtu') == 9100))):
+ return True
+ return False
+
+ def is_this_change_required(self, conf, have):
+ if conf['name'] == "eth0":
+ return False
+ ret_flag = False
+ intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None)
+ if intf:
+ # Check all parameter if any one is differen from existing
+ for param in self.params:
+ if conf.get(param) is not None and conf.get(param) != intf.get(param):
+ ret_flag = True
+ break
+ # if given interface is not present
+ else:
+ ret_flag = True
+
+ return ret_flag
+
+ def build_create_payload(self, conf):
+ temp_conf = dict()
+ temp_conf['name'] = conf['name']
+
+ if not temp_conf['name'].startswith('Loopback'):
+ if conf.get('enabled') is not None:
+ if conf.get('enabled'):
+ temp_conf['enabled'] = True
+ else:
+ temp_conf['enabled'] = False
+ if conf.get('description') is not None:
+ temp_conf['description'] = conf['description']
+ if conf.get('mtu') is not None:
+ temp_conf['mtu'] = conf['mtu']
+
+ payload = {'openconfig-interfaces:config': temp_conf}
+ return payload
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py
new file mode 100644
index 00000000..fccba770
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,414 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_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
+
+import json
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
+ ConfigBase
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states,
+ normalize_interface_name
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ to_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import (
+ Facts
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils._text import to_native
+from ansible.module_utils.connection import ConnectionError
+import traceback
+
+LIB_IMP_ERR = None
+ERR_MSG = None
+try:
+ import requests
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+PATCH = 'patch'
+intf_key = 'openconfig-if-ethernet:ethernet'
+port_chnl_key = 'openconfig-if-aggregate:aggregation'
+
+TEST_KEYS = [
+ {'allowed_vlans': {'vlan': ''}},
+]
+
+
+class L2_interfaces(ConfigBase):
+ """
+ The sonic_l2_interfaces class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'l2_interfaces',
+ ]
+
+ def __init__(self, module):
+ super(L2_interfaces, self).__init__(module)
+
+ def get_l2_interfaces_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)
+ 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}
+ warnings = list()
+
+ existing_l2_interfaces_facts = self.get_l2_interfaces_facts()
+ commands, requests = self.set_config(existing_l2_interfaces_facts)
+
+ if commands:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_l2_interfaces_facts = self.get_l2_interfaces_facts()
+
+ result['before'] = existing_l2_interfaces_facts
+ if result['changed']:
+ result['after'] = 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
+ """
+ want = self._module.params['config']
+ normalize_interface_name(want, self._module)
+ have = existing_l2_interfaces_facts
+
+ for intf in have:
+ if not intf.get('access'):
+ intf.update({'access': None})
+ if not intf.get('trunk'):
+ intf.update({'trunk': None})
+
+ 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']
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+
+ return commands, requests
+
+ def _state_replaced(self, want, have, diff):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+
+ requests = []
+ commands = diff
+
+ if commands:
+ requests_del = self.get_delete_all_switchport_requests(commands)
+ if requests_del:
+ requests.extend(requests_del)
+
+ requests_rep = self.get_create_l2_interface_request(commands)
+ if len(requests_del) or len(requests_rep):
+ requests.extend(requests_rep)
+ commands = update_states(commands, "replaced")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_overridden(self, want, have, diff):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ requests = []
+
+ commands_del = get_diff(have, want, TEST_KEYS)
+ requests_del = self.get_delete_all_switchport_requests(commands_del)
+ if len(requests_del):
+ requests.extend(requests_del)
+ commands_del = update_states(commands_del, "deleted")
+ commands.extend(commands_del)
+
+ commands_over = diff
+ requests_over = self.get_create_l2_interface_request(commands_over)
+ if requests_over:
+ requests.extend(requests_over)
+ commands_over = update_states(commands_over, "overridden")
+ commands.extend(commands_over)
+
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration at position-0
+ Requests necessary to merge to the current configuration
+ at position-1
+ """
+ commands = diff
+ requests = self.get_create_l2_interface_request(commands)
+ if commands and len(requests):
+ commands = update_states(commands, "merged")
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+
+ # if want is none, then delete all the vlan links
+ if not want or len(have) == 0:
+ commands = have
+ requests = self.get_delete_all_switchport_requests(commands)
+ else:
+ commands = want
+ requests = self.get_delete_specifig_switchport_requests(want, have)
+ if len(requests) == 0:
+ commands = []
+
+ if commands:
+ commands = update_states(commands, "deleted")
+
+ return commands, requests
+
+ def get_trunk_delete_switchport_request(self, config, match_config):
+ method = "DELETE"
+ name = config['name']
+ requests = []
+ match_trunk = match_config.get('trunk')
+ if match_trunk:
+ conf_allowed_vlans = config['trunk'].get('allowed_vlans', [])
+ if conf_allowed_vlans:
+ for each_allowed_vlan in conf_allowed_vlans:
+ if each_allowed_vlan in match_trunk.get('allowed_vlans'):
+ vlan_id = each_allowed_vlan['vlan']
+ key = intf_key
+ if name.startswith('PortChannel'):
+ key = port_chnl_key
+ url = "data/openconfig-interfaces:interfaces/interface={0}/{1}/".format(name, key)
+ url += "openconfig-vlan:switched-vlan/config/trunk-vlans={0}".format(vlan_id)
+ request = {"path": url, "method": method}
+ requests.append(request)
+ return requests
+
+ def get_access_delete_switchport_request(self, config, match_config):
+ method = "DELETE"
+ request = None
+ name = config['name']
+ match_access = match_config.get('access')
+ if match_access and match_access.get('vlan') == config['access'].get('vlan'):
+ key = intf_key
+ if name.startswith('PortChannel'):
+ key = port_chnl_key
+ url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config/access-vlan"
+ request = {"path": url.format(name, key), "method": method}
+ return request
+
+ def get_delete_all_switchport_requests(self, configs):
+ requests = []
+ if not configs:
+ return requests
+ # Create URL and payload
+ url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config"
+ method = "DELETE"
+ for intf in configs:
+ name = intf.get("name")
+ key = intf_key
+ if name.startswith('PortChannel'):
+ key = port_chnl_key
+ request = {"path": url.format(name, key),
+ "method": method,
+ }
+ requests.append(request)
+
+ return requests
+
+ def get_delete_specifig_switchport_requests(self, configs, have):
+ requests = []
+ if not configs:
+ return requests
+
+ for conf in configs:
+ name = conf['name']
+
+ matched = next((cnf for cnf in have if cnf['name'] == name), None)
+ if matched:
+ keys = conf.keys()
+
+ # if both access and trunk not mention in delete
+ if not ('access' in keys) and not ('trunk' in keys):
+ requests.extend(self.get_delete_all_switchport_requests([conf]))
+ else:
+ # if access or trnuk is mentioned with value
+ if conf.get('access') or conf.get('trunk'):
+ # if access is mentioned with value
+ if conf.get('access'):
+ vlan = conf.get('access').get('vlan')
+ if vlan:
+ request = self.get_access_delete_switchport_request(conf, matched)
+ if request:
+ requests.append(request)
+ else:
+ if matched.get('access') and matched.get('access').get('vlan'):
+ conf['access']['vlan'] = matched.get('access').get('vlan')
+ request = self.get_access_delete_switchport_request(conf, matched)
+ if request:
+ requests.append(request)
+
+ # if trunk is mentioned with value
+ if conf.get('trunk'):
+ allowed_vlans = conf['trunk'].get('allowed_vlans')
+ if allowed_vlans:
+ requests.extend(self.get_trunk_delete_switchport_request(conf, matched))
+ # allowed vlans mentinoed without value
+ else:
+ if matched.get('trunk') and matched.get('trunk').get('allowed_vlans'):
+ conf['trunk']['allowed_vlans'] = matched.get('trunk') and matched.get('trunk').get('allowed_vlans').copy()
+ requests.extend(self.get_trunk_delete_switchport_request(conf, matched))
+ # check for access or trunk is mentioned without value
+ else:
+ # access mentioned wothout value
+ if ('access' in keys) and conf.get('access', None) is None:
+ # get the existing values and delete it
+ if matched.get('access'):
+ conf['access'] = matched.get('access').copy()
+ request = self.get_access_delete_switchport_request(conf, matched)
+ if request:
+ requests.append(request)
+ # trunk mentioned wothout value
+ if ('trunk' in keys) and conf.get('trunk', None) is None:
+ # get the existing values and delete it
+ if matched.get('trunk'):
+ conf['trunk'] = matched.get('trunk').copy()
+ requests.extend(self.get_trunk_delete_switchport_request(conf, matched))
+
+ return requests
+
+ def get_create_l2_interface_request(self, configs):
+ requests = []
+ if not configs:
+ return requests
+ # Create URL and payload
+ url = "data/openconfig-interfaces:interfaces/interface={}/{}/openconfig-vlan:switched-vlan/config"
+ method = "PATCH"
+ for conf in configs:
+ name = conf.get('name')
+ if name == "eth0":
+ continue
+ key = intf_key
+ if name.startswith('PortChannel'):
+ key = port_chnl_key
+ payload = self.build_create_payload(conf)
+ request = {"path": url.format(name, key),
+ "method": method,
+ "data": payload
+ }
+ requests.append(request)
+ return requests
+
+ def build_create_payload(self, conf):
+ payload_url = '{"openconfig-vlan:config":{ '
+ access_payload = ''
+ trunk_payload = ''
+ if conf.get('access'):
+ access_vlan_id = conf['access']['vlan']
+ access_payload = '"access-vlan": {0}'.format(access_vlan_id)
+ if conf.get('trunk'):
+ trunk_payload = '"trunk-vlans": ['
+ cnt = 0
+ for each_allowed_vlan in conf['trunk']['allowed_vlans']:
+ if cnt > 0:
+ trunk_payload += ','
+ trunk_payload += str(each_allowed_vlan['vlan'])
+ cnt = cnt + 1
+ trunk_payload += ']'
+
+ if access_payload != '':
+ payload_url += access_payload
+ if trunk_payload != '':
+ if access_payload != '':
+ payload_url += ','
+ payload_url += trunk_payload
+
+ payload_url += '}}'
+
+ ret_payload = json.loads(payload_url)
+ return ret_payload
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py
new file mode 100644
index 00000000..d1b73525
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/l3_interfaces/l3_interfaces.py
@@ -0,0 +1,515 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_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
+
+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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states,
+ normalize_interface_name,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils._text import to_native
+from ansible.module_utils.connection import ConnectionError
+
+TEST_KEYS = [
+ {"addresses": {"address": "", "secondary": ""}}
+]
+
+DELETE = "DELETE"
+PATCH = "PATCH"
+
+
+class L3_interfaces(ConfigBase):
+ """
+ The sonic_l3_interfaces class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min'
+ ]
+
+ gather_network_resources = [
+ 'l3_interfaces',
+ ]
+
+ def __init__(self, module):
+ super(L3_interfaces, self).__init__(module)
+
+ def get_l3_interfaces_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)
+ l3_interfaces_facts = facts['ansible_network_resources'].get('l3_interfaces')
+ if not l3_interfaces_facts:
+ return []
+ return l3_interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+
+ existing_l3_interfaces_facts = self.get_l3_interfaces_facts()
+ commands, requests = self.set_config(existing_l3_interfaces_facts)
+ if commands:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_l3_interfaces_facts = self.get_l3_interfaces_facts()
+
+ result['before'] = existing_l3_interfaces_facts
+ if result['changed']:
+ result['after'] = 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
+ """
+ want = self._module.params['config']
+ normalize_interface_name(want, self._module)
+ have = existing_l3_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']
+ diff = get_diff(want, have, TEST_KEYS)
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ ret_commands = commands
+ return ret_commands, requests
+
+ def _state_replaced(self, want, have, diff):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ ret_requests = list()
+ commands = list()
+ l3_interfaces_to_delete = get_diff(have, want, TEST_KEYS)
+ obj = self.get_object(l3_interfaces_to_delete, want)
+ diff = get_diff(obj, want, TEST_KEYS)
+ if diff:
+ delete_l3_interfaces_requests = self.get_delete_all_requests(want)
+ ret_requests.extend(delete_l3_interfaces_requests)
+ commands.extend(update_states(want, "deleted"))
+ l3_interfaces_to_create_requests = self.get_create_l3_interfaces_requests(want, have, want)
+ ret_requests.extend(l3_interfaces_to_create_requests)
+ commands.extend(update_states(want, "merged"))
+ return commands, ret_requests
+
+ def _state_overridden(self, want, have, diff):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ ret_requests = list()
+ commands = list()
+ interfaces_to_delete = get_diff(have, want, TEST_KEYS)
+ if interfaces_to_delete:
+ delete_interfaces_requests = self.get_delete_l3_interfaces_requests(want, have)
+ ret_requests.extend(delete_interfaces_requests)
+ commands.extend(update_states(interfaces_to_delete, "deleted"))
+
+ if diff:
+ interfaces_to_create_requests = self.get_create_l3_interfaces_requests(diff, have, want)
+ ret_requests.extend(interfaces_to_create_requests)
+ commands.extend(update_states(diff, "merged"))
+
+ return commands, ret_requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ self.validate_primary_ips(want)
+ commands = diff
+ requests = self.get_create_l3_interfaces_requests(commands, have, want)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = list()
+ if not want:
+ commands = have
+ requests = self.get_delete_all_completely_requests(commands)
+ else:
+ commands = want
+ requests = self.get_delete_l3_interfaces_requests(commands, have)
+ if len(requests) == 0:
+ commands = []
+ if commands:
+ commands = update_states(commands, "deleted")
+ return commands, requests
+
+ def get_object(self, have, want):
+ objects = list()
+ names = [i.get('name', None) for i in want]
+ for obj in have:
+ if 'name' in obj and obj['name'] in names:
+ objects.append(obj.copy())
+ return objects
+
+ def get_address(self, ip_str, have_obj):
+ to_return = list()
+ for i in have_obj:
+ if i.get(ip_str) and i[ip_str].get('addresses'):
+ for ip in i[ip_str]['addresses']:
+ to_return.append(ip['address'])
+ return to_return
+
+ def get_delete_l3_interfaces_requests(self, want, have):
+ requests = []
+ ipv4_addrs_url_all = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4/addresses'
+ ipv6_addrs_url_all = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/addresses'
+ ipv4_anycast_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4'
+ ipv4_anycast_url += '/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway={anycast_ip}'
+ ipv4_addr_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4/addresses/address={address}'
+ ipv6_addr_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/addresses/address={address}'
+ ipv6_enabled_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/config/enabled'
+
+ for each_l3 in want:
+ l3 = each_l3.copy()
+ name = l3.pop('name')
+ sub_intf = self.get_sub_interface_name(name)
+ have_obj = next((e_cfg for e_cfg in have if e_cfg['name'] == name), None)
+ if not have_obj:
+ continue
+ have_ipv4_addrs = list()
+ have_ipv4_anycast_addrs = list()
+ have_ipv6_addrs = list()
+ have_ipv6_enabled = None
+
+ if have_obj.get('ipv4'):
+ if 'addresses' in have_obj['ipv4']:
+ have_ipv4_addrs = have_obj['ipv4']['addresses']
+ if 'anycast_addresses' in have_obj['ipv4']:
+ have_ipv4_anycast_addrs = have_obj['ipv4']['anycast_addresses']
+
+ have_ipv6_addrs = self.get_address('ipv6', [have_obj])
+ if have_obj.get('ipv6') and 'enabled' in have_obj['ipv6']:
+ have_ipv6_enabled = have_obj['ipv6']['enabled']
+
+ ipv4 = l3.get('ipv4', None)
+ ipv6 = l3.get('ipv6', None)
+
+ ipv4_addrs = None
+ ipv6_addrs = None
+
+ is_del_ipv4 = None
+ is_del_ipv6 = None
+ if name and ipv4 is None and ipv6 is None:
+ is_del_ipv4 = True
+ is_del_ipv6 = True
+ elif ipv4 and ipv4.get('addresses') and not ipv4.get('anycast_addresses'):
+ is_del_ipv4 = True
+ elif ipv6 and not ipv6.get('addresses') and ipv6.get('enabled') is None:
+ is_del_ipv6 = True
+
+ if is_del_ipv4:
+ if have_ipv4_addrs and len(have_ipv4_addrs) != 0:
+ ipv4_addrs_delete_request = {"path": ipv4_addrs_url_all.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(ipv4_addrs_delete_request)
+ if have_ipv4_anycast_addrs and len(have_ipv4_anycast_addrs) != 0:
+ for ip in have_ipv4_anycast_addrs:
+ ip = ip.replace('/', '%2f')
+ anycast_delete_request = {"path": ipv4_anycast_url.format(intf_name=name, sub_intf_name=sub_intf, anycast_ip=ip), "method": DELETE}
+ requests.append(anycast_delete_request)
+ else:
+ ipv4_addrs = []
+ ipv4_anycast_addrs = []
+ if l3.get('ipv4'):
+ if l3['ipv4'].get('addresses'):
+ ipv4_addrs = l3['ipv4']['addresses']
+ if l3['ipv4'].get('anycast_addresses'):
+ ipv4_anycast_addrs = l3['ipv4']['anycast_addresses']
+
+ # Store the primary ip at end of the list. So primary ip will be deleted after the secondary ips
+ ipv4_del_reqs = []
+ for ip in ipv4_addrs:
+ match_ip = next((addr for addr in have_ipv4_addrs if addr['address'] == ip['address']), None)
+ if match_ip:
+ addr = ip['address'].split('/')[0]
+ del_url = ipv4_addr_url.format(intf_name=name, sub_intf_name=sub_intf, address=addr)
+ if match_ip['secondary']:
+ del_url += '/config/secondary'
+ ipv4_del_reqs.insert(0, {"path": del_url, "method": DELETE})
+ else:
+ ipv4_del_reqs.append({"path": del_url, "method": DELETE})
+ if ipv4_del_reqs:
+ requests.extend(ipv4_del_reqs)
+
+ for ip in ipv4_anycast_addrs:
+ if have_ipv4_addrs and ip in have_ipv4_addrs:
+ ip = ip.replace('/', '%2f')
+ anycast_delete_request = {"path": ipv4_anycast_url.format(intf_name=name, sub_intf_name=sub_intf, anycast_ip=ip), "method": DELETE}
+ requests.append(anycast_delete_request)
+
+ if is_del_ipv6:
+ if have_ipv6_addrs and len(have_ipv6_addrs) != 0:
+ ipv6_addrs_delete_request = {"path": ipv6_addrs_url_all.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(ipv6_addrs_delete_request)
+
+ if have_ipv6_enabled:
+ ipv6_enabled_delete_request = {"path": ipv6_enabled_url.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(ipv6_enabled_delete_request)
+ else:
+ ipv6_addrs = []
+ ipv6_enabled = None
+ if l3.get('ipv6'):
+ if l3['ipv6'].get('addresses'):
+ ipv6_addrs = l3['ipv6']['addresses']
+ if 'enabled' in l3['ipv6']:
+ ipv6_enabled = l3['ipv6']['enabled']
+
+ for ip in ipv6_addrs:
+ if have_ipv6_addrs and ip['address'] in have_ipv6_addrs:
+ addr = ip['address'].split('/')[0]
+ request = {"path": ipv6_addr_url.format(intf_name=name, sub_intf_name=sub_intf, address=addr), "method": DELETE}
+ requests.append(request)
+
+ if have_ipv6_enabled and ipv6_enabled is not None:
+ request = {"path": ipv6_enabled_url.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(request)
+ return requests
+
+ def get_delete_all_completely_requests(self, configs):
+ delete_requests = list()
+ for l3 in configs:
+ if l3['ipv4'] or l3['ipv6']:
+ delete_requests.append(l3)
+ return self.get_delete_all_requests(delete_requests)
+
+ def get_delete_all_requests(self, configs):
+ requests = []
+ ipv4_addrs_url_all = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4/addresses'
+ ipv4_anycast_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4'
+ ipv4_anycast_url += '/openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway={anycast_ip}'
+ ipv6_addrs_url_all = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/addresses'
+ ipv6_enabled_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/config/enabled'
+ for l3 in configs:
+ name = l3.get('name')
+ ipv4_addrs = []
+ ipv4_anycast = []
+ if l3.get('ipv4'):
+ if l3['ipv4'].get('addresses'):
+ ipv4_addrs = l3['ipv4']['addresses']
+ if l3['ipv4'].get('anycast_addresses', None):
+ ipv4_anycast = l3['ipv4']['anycast_addresses']
+
+ ipv6_addrs = []
+ ipv6_enabled = None
+ if l3.get('ipv6'):
+ if l3['ipv6'].get('addresses'):
+ ipv6_addrs = l3['ipv6']['addresses']
+ if 'enabled' in l3['ipv6']:
+ ipv6_enabled = l3['ipv6']['enabled']
+
+ sub_intf = self.get_sub_interface_name(name)
+
+ if ipv4_addrs:
+ ipv4_addrs_delete_request = {"path": ipv4_addrs_url_all.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(ipv4_addrs_delete_request)
+ if ipv4_anycast:
+ for ip in ipv4_anycast:
+ ip = ip.replace('/', '%2f')
+ anycast_delete_request = {"path": ipv4_anycast_url.format(intf_name=name, sub_intf_name=sub_intf, anycast_ip=ip), "method": DELETE}
+ requests.append(anycast_delete_request)
+ if ipv6_addrs:
+ ipv6_addrs_delete_request = {"path": ipv6_addrs_url_all.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(ipv6_addrs_delete_request)
+ if ipv6_enabled:
+ ipv6_enabled_delete_request = {"path": ipv6_enabled_url.format(intf_name=name, sub_intf_name=sub_intf), "method": DELETE}
+ requests.append(ipv6_enabled_delete_request)
+ return requests
+
+ def get_create_l3_interfaces_requests(self, configs, have, want):
+ requests = []
+ if not configs:
+ return requests
+
+ ipv4_addrs_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4/addresses'
+ ipv4_anycast_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv4/'
+ ipv4_anycast_url += 'openconfig-interfaces-ext:sag-ipv4/config/static-anycast-gateway'
+ ipv6_addrs_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/addresses'
+ ipv6_enabled_url = 'data/openconfig-interfaces:interfaces/interface={intf_name}/{sub_intf_name}/openconfig-if-ip:ipv6/config'
+
+ for l3 in configs:
+ l3_interface_name = l3.get('name')
+ if l3_interface_name == "eth0":
+ continue
+
+ sub_intf = self.get_sub_interface_name(l3_interface_name)
+
+ ipv4_addrs = []
+ ipv4_anycast = []
+ if l3.get('ipv4'):
+ if l3['ipv4'].get('addresses'):
+ ipv4_addrs = l3['ipv4']['addresses']
+ if l3['ipv4'].get('anycast_addresses'):
+ ipv4_anycast = l3['ipv4']['anycast_addresses']
+
+ ipv6_addrs = []
+ ipv6_enabled = None
+ if l3.get('ipv6'):
+ if l3['ipv6'].get('addresses'):
+ ipv6_addrs = l3['ipv6']['addresses']
+ if 'enabled' in l3['ipv6']:
+ ipv6_enabled = l3['ipv6']['enabled']
+
+ if ipv4_addrs:
+ ipv4_addrs_pri_payload = []
+ ipv4_addrs_sec_payload = []
+ for item in ipv4_addrs:
+ ipv4_addr_mask = item['address'].split('/')
+ ipv4 = ipv4_addr_mask[0]
+ ipv4_mask = ipv4_addr_mask[1]
+ ipv4_secondary = item['secondary']
+ if ipv4_secondary:
+ ipv4_addrs_sec_payload.append(self.build_create_addr_payload(ipv4, ipv4_mask, ipv4_secondary))
+ else:
+ ipv4_addrs_pri_payload.append(self.build_create_addr_payload(ipv4, ipv4_mask, ipv4_secondary))
+ if ipv4_addrs_pri_payload:
+ payload = self.build_create_payload(ipv4_addrs_pri_payload)
+ ipv4_addrs_req = {"path": ipv4_addrs_url.format(intf_name=l3_interface_name, sub_intf_name=sub_intf), "method": PATCH, "data": payload}
+ requests.append(ipv4_addrs_req)
+ if ipv4_addrs_sec_payload:
+ payload = self.build_create_payload(ipv4_addrs_sec_payload)
+ ipv4_addrs_req = {"path": ipv4_addrs_url.format(intf_name=l3_interface_name, sub_intf_name=sub_intf), "method": PATCH, "data": payload}
+ requests.append(ipv4_addrs_req)
+
+ if ipv4_anycast:
+ anycast_payload = {'openconfig-interfaces-ext:static-anycast-gateway': ipv4_anycast}
+ anycast_url = ipv4_anycast_url.format(intf_name=l3_interface_name, sub_intf_name=sub_intf)
+ requests.append({'path': anycast_url, 'method': PATCH, 'data': anycast_payload})
+
+ if ipv6_addrs:
+ ipv6_addrs_payload = []
+ for item in ipv6_addrs:
+ ipv6_addr_mask = item['address'].split('/')
+ ipv6 = ipv6_addr_mask[0]
+ ipv6_mask = ipv6_addr_mask[1]
+ ipv6_addrs_payload.append(self.build_create_addr_payload(ipv6, ipv6_mask))
+ if ipv6_addrs_payload:
+ payload = self.build_create_payload(ipv6_addrs_payload)
+ ipv6_addrs_req = {"path": ipv6_addrs_url.format(intf_name=l3_interface_name, sub_intf_name=sub_intf), "method": PATCH, "data": payload}
+ requests.append(ipv6_addrs_req)
+
+ if ipv6_enabled is not None:
+ payload = self.build_update_ipv6_enabled(ipv6_enabled)
+ ipv6_enabled_req = {"path": ipv6_enabled_url.format(intf_name=l3_interface_name, sub_intf_name=sub_intf), "method": PATCH, "data": payload}
+ requests.append(ipv6_enabled_req)
+
+ return requests
+
+ def validate_primary_ips(self, want):
+ error_intf = {}
+ for l3 in want:
+ l3_interface_name = l3.get('name')
+
+ ipv4_addrs = []
+ if l3.get('ipv4') and l3['ipv4'].get('addresses'):
+ ipv4_addrs = l3['ipv4']['addresses']
+
+ if ipv4_addrs:
+ ipv4_pri_addrs = [addr['address'] for addr in ipv4_addrs if not addr['secondary']]
+ if len(ipv4_pri_addrs) > 1:
+ error_intf[l3_interface_name] = ipv4_pri_addrs
+
+ if error_intf:
+ err = "Multiple ipv4 primary ips found! " + str(error_intf)
+ self._module.fail_json(msg=str(err), code=300)
+
+ def build_create_payload(self, addrs_payload):
+ payload = {'openconfig-if-ip:addresses': {'address': addrs_payload}}
+ return payload
+
+ def build_create_addr_payload(self, ip, mask, secondary=None):
+ cfg = {'ip': ip, 'prefix-length': float(mask)}
+ if secondary:
+ cfg['secondary'] = secondary
+ addr_payload = {'ip': ip, 'openconfig-if-ip:config': cfg}
+ return addr_payload
+
+ def get_sub_interface_name(self, name):
+ sub_intf = "subinterfaces/subinterface=0"
+ if name.startswith("Vlan"):
+ sub_intf = "openconfig-vlan:routed-vlan"
+ return sub_intf
+
+ def build_update_ipv6_enabled(self, ipv6_enabled):
+ payload = {'config': {'enabled': ipv6_enabled}}
+ return payload
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py
new file mode 100644
index 00000000..541de2c4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/lag_interfaces/lag_interfaces.py
@@ -0,0 +1,421 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_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
+
+try:
+ from urllib import quote
+except ImportError:
+ from urllib.parse import quote
+
+import json
+
+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,
+ search_obj_in_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+ normalize_interface_name,
+ remove_empties_from_list,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils._text import to_native
+from ansible.module_utils.connection import ConnectionError
+import traceback
+
+LIB_IMP_ERR = None
+ERR_MSG = None
+try:
+ import jinja2
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+
+PUT = 'put'
+PATCH = 'patch'
+DELETE = 'delete'
+TEST_KEYS = [
+ {'interfaces': {'member': ''}},
+]
+
+
+class Lag_interfaces(ConfigBase):
+ """
+ The sonic_lag_interfaces class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'lag_interfaces',
+ ]
+
+ params = ('name', 'members')
+
+ def __init__(self, module):
+ super(Lag_interfaces, self).__init__(module)
+
+ def get_lag_interfaces_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)
+ 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}
+ warnings = list()
+ commands = list()
+ existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
+ commands, requests = self.set_config(existing_lag_interfaces_facts)
+ if commands:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
+
+ result['before'] = existing_lag_interfaces_facts
+ if result['changed']:
+ result['after'] = 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['config']
+ normalize_interface_name(want, self._module)
+ 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
+ """
+ commands = []
+ diff = get_diff(want, have, TEST_KEYS)
+ if diff:
+ diff_members, diff_portchannels = self.diff_list_for_member_creation(diff)
+ else:
+ diff_members = []
+ diff_portchannels = []
+
+ state = self._module.params['state']
+ if state in ('overridden', 'merged', 'replaced') and not want:
+ self._module.fail_json(msg='value of config parameter must not be empty for state {0}'.format(state))
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff_members, diff_portchannels)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff_members, diff_portchannels)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff_members, diff_portchannels)
+
+ return commands, requests
+
+ def _state_replaced(self, want, have, diff_members, diff_portchannels):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ requests = list()
+ commands = list()
+ delete_list = list()
+ delete_list = get_diff(have, want, TEST_KEYS)
+ delete_members, delete_portchannels = self.diff_list_for_member_creation(delete_list)
+ replaced_list = list()
+
+ for i in want:
+ list_obj = search_obj_in_list(i['name'], delete_members, "name")
+ if list_obj:
+ replaced_list.append(list_obj)
+ requests = self.get_delete_lag_interfaces_requests(replaced_list)
+ if requests:
+ commands.extend(update_states(replaced_list, "replaced"))
+ replaced_commands, replaced_requests = self.template_for_lag_creation(have, diff_members, diff_portchannels, "replaced")
+ if replaced_requests:
+ commands.extend(replaced_commands)
+ requests.extend(replaced_requests)
+
+ return commands, requests
+
+ def _state_overridden(self, want, have, diff_members, diff_portchannels):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ requests = list()
+ commands = list()
+ delete_list = list()
+ delete_list = get_diff(have, want, TEST_KEYS)
+ delete_members, delete_portchannels = self.diff_list_for_member_creation(delete_list)
+ replaced_list = list()
+ for i in want:
+ list_obj = search_obj_in_list(i['name'], delete_members, "name")
+ if list_obj:
+ replaced_list.append(list_obj)
+ requests = self.get_delete_lag_interfaces_requests(replaced_list)
+ commands.extend(update_states(replaced_list, "overridden"))
+ delete_members = get_diff(delete_members, replaced_list, TEST_KEYS)
+ commands_overridden, requests_overridden = self.template_for_lag_deletion(have, delete_members, delete_portchannels, "overridden")
+ requests.extend(requests_overridden)
+ commands.extend(commands_overridden)
+ override_commands, override_requests = self.template_for_lag_creation(have, diff_members, diff_portchannels, "overridden")
+ commands.extend(override_commands)
+ requests.extend(override_requests)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff_members, diff_portchannels):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ return self.template_for_lag_creation(have, diff_members, diff_portchannels, "merged")
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = list()
+ requests = list()
+ portchannel_requests = list()
+ # if want is none, then delete all the lag interfaces and all portchannels
+ if not want:
+ requests = self.get_delete_all_lag_interfaces_requests()
+ portchannel_requests = self.get_delete_all_portchannel_requests()
+ requests.extend(portchannel_requests)
+ commands.extend(update_states(have, "Deleted"))
+ else: # delete specific lag interfaces and specific portchannels
+ commands = get_diff(want, diff, TEST_KEYS)
+ commands = remove_empties_from_list(commands)
+ want_members, want_portchannels = self.diff_list_for_member_creation(commands)
+ commands, requests = self.template_for_lag_deletion(have, want_members, want_portchannels, "deleted")
+ return commands, requests
+
+ def diff_list_for_member_creation(self, diff):
+ diff_members = [x for x in diff if "members" in x.keys()]
+ diff_portchannels = [x for x in diff if ("name" in x.keys() and "members" not in x.keys())]
+ return diff_members, diff_portchannels
+
+ def template_for_lag_creation(self, have, diff_members, diff_portchannels, state_name):
+ commands = list()
+ requests = list()
+ if diff_members:
+ commands_portchannels, requests = self.call_create_port_channel(diff_members, have)
+ if commands_portchannels:
+ po_list = [{'name': x['name']} for x in commands_portchannels if x['name']]
+ else:
+ po_list = []
+ if po_list:
+ commands.extend(update_states(po_list, state_name))
+ diff_members_remove_none = [x for x in diff_members if x["members"]]
+ if diff_members_remove_none:
+ request = self.create_lag_interfaces_requests(diff_members_remove_none)
+ if request:
+ requests.extend(request)
+ else:
+ requests = request
+ commands.extend(update_states(diff_members, state_name))
+ if diff_portchannels:
+ portchannels, po_requests = self.call_create_port_channel(diff_portchannels, have)
+ requests.extend(po_requests)
+ commands.extend(update_states(portchannels, state_name))
+ return commands, requests
+
+ def template_for_lag_deletion(self, have, delete_members, delete_portchannels, state_name):
+ commands = list()
+ requests = list()
+ portchannel_requests = list()
+ if delete_members:
+ delete_members_remove_none = [x for x in delete_members if x["members"]]
+ requests = self.get_delete_lag_interfaces_requests(delete_members_remove_none)
+ delete_all_members = [x for x in delete_members if "members" in x.keys() and not x["members"]]
+ delete_all_list = list()
+ if delete_all_members:
+ for i in delete_all_members:
+ list_obj = search_obj_in_list(i['name'], have, "name")
+ if list_obj['members']:
+ delete_all_list.append(list_obj)
+ if delete_all_list:
+ deleteall_requests = self.get_delete_lag_interfaces_requests(delete_all_list)
+ else:
+ deleteall_requests = []
+ if requests and deleteall_requests:
+ requests.extend(deleteall_requests)
+ elif deleteall_requests:
+ requests = deleteall_requests
+ if requests:
+ commands.extend(update_states(delete_members, state_name))
+ if delete_portchannels:
+ portchannel_requests = self.get_delete_portchannel_requests(delete_portchannels)
+ commands.extend(update_states(delete_portchannels, state_name))
+ if requests:
+ requests.extend(portchannel_requests)
+ else:
+ requests = portchannel_requests
+ return commands, requests
+
+ def create_lag_interfaces_requests(self, commands):
+ requests = []
+ for i in commands:
+ if i.get('members') and i['members'].get('interfaces'):
+ interfaces = i['members']['interfaces']
+ else:
+ continue
+ for each in interfaces:
+ edit_payload = self.build_create_payload_member(i['name'])
+ template = 'data/openconfig-interfaces:interfaces/interface=%s/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id'
+ edit_path = template % quote(each['member'], safe='')
+ request = {'path': edit_path, 'method': PATCH, 'data': edit_payload}
+ requests.append(request)
+ return requests
+
+ def build_create_payload_member(self, name):
+ payload_template = """{\n"openconfig-if-aggregate:aggregate-id": "{{name}}"\n}"""
+ temp = name.split("PortChannel", 1)[1]
+ input_data = {"name": temp}
+ env = jinja2.Environment(autoescape=False)
+ t = env.from_string(payload_template)
+ intended_payload = t.render(input_data)
+ ret_payload = json.loads(intended_payload)
+ return ret_payload
+
+ def build_create_payload_portchannel(self, name, mode):
+ payload_template = """{\n"openconfig-interfaces:interfaces": {"interface": [{\n"name": "{{name}}",\n"config": {\n"name": "{{name}}"\n}"""
+ input_data = {"name": name}
+ if mode == "static":
+ payload_template += """,\n "openconfig-if-aggregation:aggregation": {\n"config": {\n"lag-type": "{{mode}}"\n}\n}\n"""
+ input_data["mode"] = mode.upper()
+ payload_template += """}\n]\n}\n}"""
+ env = jinja2.Environment(autoescape=False)
+ t = env.from_string(payload_template)
+ intended_payload = t.render(input_data)
+ ret_payload = json.loads(intended_payload)
+ return ret_payload
+
+ def create_port_channel(self, cmd):
+ requests = []
+ path = 'data/openconfig-interfaces:interfaces'
+ for i in cmd:
+ payload = self.build_create_payload_portchannel(i['name'], i.get('mode', None))
+ request = {'path': path, 'method': PATCH, 'data': payload}
+ requests.append(request)
+ return requests
+
+ def call_create_port_channel(self, commands, have):
+ commands_list = list()
+ for c in commands:
+ if not any(d['name'] == c['name'] for d in have):
+ commands_list.append(c)
+ requests = self.create_port_channel(commands_list)
+ return commands_list, requests
+
+ def get_delete_all_lag_interfaces_requests(self):
+ requests = []
+ delete_all_lag_url = 'data/sonic-portchannel:sonic-portchannel/PORTCHANNEL_MEMBER/PORTCHANNEL_MEMBER_LIST'
+ method = DELETE
+ delete_all_lag_request = {"path": delete_all_lag_url, "method": method}
+ requests.append(delete_all_lag_request)
+ return requests
+
+ def get_delete_all_portchannel_requests(self):
+ requests = []
+ delete_all_lag_url = 'data/sonic-portchannel:sonic-portchannel/PORTCHANNEL/PORTCHANNEL_LIST'
+ method = DELETE
+ delete_all_lag_request = {"path": delete_all_lag_url, "method": method}
+ requests.append(delete_all_lag_request)
+ return requests
+
+ def get_delete_lag_interfaces_requests(self, commands):
+ requests = []
+ # Create URL and payload
+ url = 'data/openconfig-interfaces:interfaces/interface={}/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id'
+ method = DELETE
+ for c in commands:
+ if c.get('members') and c['members'].get('interfaces'):
+ interfaces = c['members']['interfaces']
+ else:
+ continue
+
+ for each in interfaces:
+ ifname = each["member"]
+ request = {"path": url.format(ifname), "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_portchannel_requests(self, commands):
+ requests = []
+ # Create URL and payload
+ url = 'data/openconfig-interfaces:interfaces/interface={}'
+ method = DELETE
+ for c in commands:
+ name = c["name"]
+ request = {"path": url.format(name), "method": method}
+ requests.append(request)
+
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py
new file mode 100644
index 00000000..88215e8f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/mclag/mclag.py
@@ -0,0 +1,323 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_mclag 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+ get_normalize_interface_name,
+ normalize_interface_name
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'delete'
+
+TEST_KEYS = [
+ {'config': {'domain_id': ''}},
+ {'vlans': {'vlan': ''}},
+ {'portchannels': {'lag': ''}},
+]
+
+
+class Mclag(ConfigBase):
+ """
+ The sonic_mclag class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'mclag',
+ ]
+
+ def __init__(self, module):
+ super(Mclag, self).__init__(module)
+
+ def get_mclag_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)
+ mclag_facts = facts['ansible_network_resources'].get('mclag')
+ if not mclag_facts:
+ return []
+ return mclag_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_mclag_facts = self.get_mclag_facts()
+ commands, requests = self.set_config(existing_mclag_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ self.edit_config(requests)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_mclag_facts = self.get_mclag_facts()
+
+ result['before'] = existing_mclag_facts
+ if result['changed']:
+ result['after'] = changed_mclag_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def edit_config(self, requests):
+ try:
+ response = edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ def set_config(self, existing_mclag_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']
+ if want:
+ peer_link = want.get("peer_link", None)
+ if peer_link:
+ want['peer_link'] = get_normalize_interface_name(want['peer_link'], self._module)
+ unique_ip = want.get('unique_ip', None)
+ if unique_ip:
+ vlans_list = unique_ip['vlans']
+ if vlans_list:
+ normalize_interface_name(vlans_list, self._module, 'vlan')
+ members = want.get('members', None)
+ if members:
+ portchannels_list = members['portchannels']
+ if portchannels_list:
+ normalize_interface_name(portchannels_list, self._module, 'lag')
+ have = existing_mclag_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 == 'deleted':
+ commands = self._state_deleted(want, have)
+ elif state == 'merged':
+ diff = get_diff(want, have, TEST_KEYS)
+ commands = self._state_merged(want, have, diff)
+ return commands
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ requests = []
+ commands = []
+ if diff:
+ requests = self.get_create_mclag_request(want, diff)
+ if len(requests) > 0:
+ commands = update_states(diff, "merged")
+ return commands, requests
+
+ 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 = []
+ requests = []
+ if not want:
+ if have:
+ requests = self.get_delete_all_mclag_domain_request()
+ if len(requests) > 0:
+ commands = update_states(have, "deleted")
+ else:
+ new_have = self.remove_default_entries(have)
+ d_diff = get_diff(want, new_have, TEST_KEYS, is_skeleton=True)
+ diff_want = get_diff(want, d_diff, TEST_KEYS, is_skeleton=True)
+ if diff_want:
+ requests = self.get_delete_mclag_attribute_request(want, diff_want)
+ if len(requests) > 0:
+ commands = update_states(diff_want, "deleted")
+ return commands, requests
+
+ def remove_default_entries(self, data):
+ new_data = {}
+ if not data:
+ return new_data
+ else:
+ default_val_dict = {
+ 'keepalive': 1,
+ 'session_timeout': 30,
+ }
+ for key, val in data.items():
+ if not (val is None or (key in default_val_dict and val == default_val_dict[key])):
+ new_data[key] = val
+
+ return new_data
+
+ def get_delete_mclag_attribute_request(self, want, command):
+ requests = []
+ url_common = 'data/openconfig-mclag:mclag/mclag-domains/mclag-domain=%s/config' % (want["domain_id"])
+ method = DELETE
+ if 'source_address' in command and command["source_address"] is not None:
+ url = url_common + '/source-address'
+ request = {'path': url, 'method': method}
+ requests.append(request)
+ if 'peer_address' in command and command["peer_address"] is not None:
+ url = url_common + '/peer-address'
+ request = {'path': url, 'method': method}
+ requests.append(request)
+ if 'peer_link' in command and command["peer_link"] is not None:
+ url = url_common + '/peer-link'
+ request = {'path': url, 'method': method}
+ requests.append(request)
+ if 'keepalive' in command and command["keepalive"] is not None:
+ url = url_common + '/keepalive-interval'
+ request = {'path': url, 'method': method}
+ requests.append(request)
+ if 'session_timeout' in command and command["session_timeout"] is not None:
+ url = url_common + '/session-timeout'
+ request = {'path': url, 'method': method}
+ requests.append(request)
+ if 'system_mac' in command and command["system_mac"] is not None:
+ url = url_common + '/mclag-system-mac'
+ request = {'path': url, 'method': method}
+ requests.append(request)
+ if 'unique_ip' in command and command['unique_ip'] is not None:
+ if command['unique_ip']['vlans'] is None:
+ request = {'path': 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface', 'method': method}
+ requests.append(request)
+ elif command['unique_ip']['vlans'] is not None:
+ for each in command['unique_ip']['vlans']:
+ if each:
+ unique_ip_url = 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface=%s' % (each['vlan'])
+ request = {'path': unique_ip_url, 'method': method}
+ requests.append(request)
+ if 'members' in command and command['members'] is not None:
+ if command['members']['portchannels'] is None:
+ request = {'path': 'data/openconfig-mclag:mclag/interfaces/interface', 'method': method}
+ requests.append(request)
+ elif command['members']['portchannels'] is not None:
+ for each in command['members']['portchannels']:
+ if each:
+ portchannel_url = 'data/openconfig-mclag:mclag/interfaces/interface=%s' % (each['lag'])
+ request = {'path': portchannel_url, 'method': method}
+ requests.append(request)
+ return requests
+
+ def get_delete_all_mclag_domain_request(self):
+ requests = []
+ path = 'data/openconfig-mclag:mclag/mclag-domains'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ requests.append(request)
+ return requests
+
+ def get_create_mclag_request(self, want, commands):
+ requests = []
+ path = 'data/openconfig-mclag:mclag/mclag-domains/mclag-domain'
+ method = PATCH
+ payload = self.build_create_payload(want, commands)
+ if payload:
+ request = {'path': path, 'method': method, 'data': payload}
+ requests.append(request)
+ if 'unique_ip' in commands and commands['unique_ip'] is not None:
+ if commands['unique_ip']['vlans'] and commands['unique_ip']['vlans'] is not None:
+ unique_ip_path = 'data/openconfig-mclag:mclag/vlan-interfaces/vlan-interface'
+ unique_ip_method = PATCH
+ unique_ip_payload = self.build_create_unique_ip_payload(commands['unique_ip']['vlans'])
+ request = {'path': unique_ip_path, 'method': unique_ip_method, 'data': unique_ip_payload}
+ requests.append(request)
+ if 'members' in commands and commands['members'] is not None:
+ if commands['members']['portchannels'] and commands['members']['portchannels'] is not None:
+ portchannel_path = 'data/openconfig-mclag:mclag/interfaces/interface'
+ portchannel_method = PATCH
+ portchannel_payload = self.build_create_portchannel_payload(want, commands['members']['portchannels'])
+ request = {'path': portchannel_path, 'method': portchannel_method, 'data': portchannel_payload}
+ requests.append(request)
+ return requests
+
+ def build_create_payload(self, want, commands):
+ temp = {}
+ if 'session_timeout' in commands and commands['session_timeout'] is not None:
+ temp['session-timeout'] = commands['session_timeout']
+ if 'keepalive' in commands and commands['keepalive'] is not None:
+ temp['keepalive-interval'] = commands['keepalive']
+ if 'source_address' in commands and commands['source_address'] is not None:
+ temp['source-address'] = commands['source_address']
+ if 'peer_address' in commands and commands['peer_address'] is not None:
+ temp['peer-address'] = commands['peer_address']
+ if 'peer_link' in commands and commands['peer_link'] is not None:
+ temp['peer-link'] = str(commands['peer_link'])
+ if 'system_mac' in commands and commands['system_mac'] is not None:
+ temp['openconfig-mclag:mclag-system-mac'] = str(commands['system_mac'])
+ mclag_dict = {}
+ if temp:
+ domain_id = {"domain-id": want["domain_id"]}
+ mclag_dict.update(domain_id)
+ config = {"config": temp}
+ mclag_dict.update(config)
+ payload = {"openconfig-mclag:mclag-domain": [mclag_dict]}
+ else:
+ payload = {}
+ return payload
+
+ def build_create_unique_ip_payload(self, commands):
+ payload = {"openconfig-mclag:vlan-interface": []}
+ for each in commands:
+ payload['openconfig-mclag:vlan-interface'].append({"name": each['vlan'], "config": {"name": each['vlan'], "unique-ip-enable": "ENABLE"}})
+ return payload
+
+ def build_create_portchannel_payload(self, want, commands):
+ payload = {"openconfig-mclag:interface": []}
+ for each in commands:
+ payload['openconfig-mclag:interface'].append({"name": each['lag'], "config": {"name": each['lag'], "mclag-domain-id": want['domain_id']}})
+ return payload
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py
new file mode 100644
index 00000000..a4fdc7e0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/ntp/ntp.py
@@ -0,0 +1,548 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_ntp 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 (
+ to_list,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states,
+ normalize_interface_name,
+ normalize_interface_name_list
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'PATCH'
+DELETE = 'DELETE'
+
+TEST_KEYS = [
+ {
+ "vrf": "", "enable_ntp_auth": "", "source_interfaces": "", "trusted_keys": "",
+ "servers": {"address": ""}, "ntp_keys": {"key_id": ""}
+ }
+]
+
+
+class Ntp(ConfigBase):
+ """
+ The sonic_ntp class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'ntp',
+ ]
+
+ def __init__(self, module):
+ super(Ntp, self).__init__(module)
+
+ def get_ntp_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)
+ ntp_facts = facts['ansible_network_resources'].get('ntp')
+
+ if not ntp_facts:
+ return []
+ return ntp_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+ requests = list()
+
+ existing_ntp_facts = self.get_ntp_facts()
+
+ commands, requests = self.set_config(existing_ntp_facts)
+
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_ntp_facts = self.get_ntp_facts()
+
+ result['before'] = existing_ntp_facts
+ if result['changed']:
+ result['after'] = changed_ntp_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_ntp_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']
+ if want is None:
+ want = []
+
+ have = existing_ntp_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']
+
+ self.validate_want(want, state)
+ self.preprocess_want(want, state)
+
+ if state == 'deleted':
+ commands, requests = self._state_deleted(want, have)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have)
+
+ return commands, requests
+
+ def _state_merged(self, want, have):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ diff = get_diff(want, have, TEST_KEYS)
+
+ commands = diff
+ requests = []
+ if commands:
+ requests = self.get_merge_requests(commands, have)
+
+ if len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param have: the current configuration as a dictionary
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ diff = get_diff(want, have, TEST_KEYS)
+
+ want_none = {'enable_ntp_auth': None, 'ntp_keys': None,
+ 'servers': None, 'source_interfaces': [],
+ 'trusted_keys': None, 'vrf': None}
+ want_any = get_diff(want, want_none, TEST_KEYS)
+ # if want_any is none, then delete all NTP configurations
+
+ delete_all = False
+ if not want_any:
+ commands = have
+ delete_all = True
+ else:
+ if not diff:
+ commands = want_any
+ else:
+ commands = get_diff(want_any, diff, TEST_KEYS)
+
+ requests = []
+ if commands:
+ requests = self.get_delete_requests(commands, delete_all)
+
+ if len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def validate_want(self, want, state):
+
+ if state == 'deleted':
+ if 'servers' in want and want['servers'] is not None:
+ for server in want['servers']:
+ key_id_config = server.get('key_id', None)
+ minpoll_config = server.get('minpoll', None)
+ maxpoll_config = server.get('maxpoll', None)
+ if key_id_config or minpoll_config or maxpoll_config:
+ err_msg = "NTP server parameter(s) can not be deleted."
+ self._module.fail_json(msg=err_msg, code=405)
+
+ if 'ntp_keys' in want and want['ntp_keys'] is not None:
+ for ntp_key in want['ntp_keys']:
+ encrypted_config = ntp_key.get('encrypted', None)
+ key_type_config = ntp_key.get('key_type', None)
+ key_value_config = ntp_key.get('key_value', None)
+ if encrypted_config or key_type_config or key_value_config:
+ err_msg = "NTP ntp_key parameter(s) can not be deleted."
+ self._module.fail_json(msg=err_msg, code=405)
+
+ def preprocess_want(self, want, state):
+
+ if 'source_interfaces' in want:
+ want['source_interfaces'] = normalize_interface_name_list(want['source_interfaces'], self._module)
+
+ if state == 'deleted':
+ enable_auth_want = want.get('enable_ntp_auth', None)
+ if enable_auth_want is not None:
+ want['enable_ntp_auth'] = True
+
+ elif state == 'merged':
+ if 'servers' in want and want['servers'] is not None:
+ for server in want['servers']:
+ if 'key_id' in server and not server['key_id']:
+ server.pop('key_id')
+ if 'minpoll' in server and not server['minpoll']:
+ server.pop('minpoll')
+ if 'maxpoll' in server and not server['maxpoll']:
+ server.pop('maxpoll')
+
+ def get_merge_requests(self, configs, have):
+
+ requests = []
+
+ enable_auth_config = configs.get('enable_ntp_auth', None)
+ if enable_auth_config is not None:
+ enable_auth_request = self.get_create_enable_ntp_auth_requests(enable_auth_config, have)
+ if enable_auth_request:
+ requests.extend(enable_auth_request)
+
+ src_intf_config = configs.get('source_interfaces', None)
+ if src_intf_config:
+ src_intf_request = self.get_create_source_interface_requests(src_intf_config, have)
+ if src_intf_request:
+ requests.extend(src_intf_request)
+
+ keys_config = configs.get('ntp_keys', None)
+ if keys_config:
+ keys_request = self.get_create_keys_requests(keys_config, have)
+ if keys_request:
+ requests.extend(keys_request)
+
+ servers_config = configs.get('servers', None)
+ if servers_config:
+ servers_request = self.get_create_servers_requests(servers_config, have)
+ if servers_request:
+ requests.extend(servers_request)
+
+ trusted_key_config = configs.get('trusted_keys', None)
+ if trusted_key_config:
+ trusted_key_request = self.get_create_trusted_key_requests(trusted_key_config, have)
+ if trusted_key_request:
+ requests.extend(trusted_key_request)
+
+ vrf_config = configs.get('vrf', None)
+ if vrf_config:
+ vrf_request = self.get_create_vrf_requests(vrf_config, have)
+ if vrf_request:
+ requests.extend(vrf_request)
+
+ return requests
+
+ def get_delete_requests(self, configs, delete_all):
+
+ requests = []
+
+ if delete_all:
+ all_ntp_request = self.get_delete_all_ntp_requests(configs)
+ if all_ntp_request:
+ requests.extend(all_ntp_request)
+ return requests
+
+ src_intf_config = configs.get('source_interfaces', None)
+ if src_intf_config:
+ src_intf_request = self.get_delete_source_interface_requests(src_intf_config)
+ if src_intf_request:
+ requests.extend(src_intf_request)
+
+ servers_config = configs.get('servers', None)
+ if servers_config:
+ servers_request = self.get_delete_servers_requests(servers_config)
+ if servers_request:
+ requests.extend(servers_request)
+
+ trusted_key_config = configs.get('trusted_keys', None)
+ if trusted_key_config:
+ trusted_key_request = self.get_delete_trusted_key_requests(trusted_key_config)
+ if trusted_key_request:
+ requests.extend(trusted_key_request)
+
+ keys_config = configs.get('ntp_keys', None)
+ if keys_config:
+ keys_request = self.get_delete_keys_requests(keys_config)
+ if keys_request:
+ requests.extend(keys_request)
+
+ enable_auth_config = configs.get('enable_ntp_auth', None)
+ if enable_auth_config is not None:
+ enable_auth_request = self.get_delete_enable_ntp_auth_requests(enable_auth_config)
+ if enable_auth_request:
+ requests.extend(enable_auth_request)
+
+ vrf_config = configs.get('vrf', None)
+ if vrf_config:
+ vrf_request = self.get_delete_vrf_requests(vrf_config)
+ if vrf_request:
+ requests.extend(vrf_request)
+
+ return requests
+
+ def get_create_source_interface_requests(self, configs, have):
+
+ requests = []
+
+ # Create URL and payload
+ method = PATCH
+ url = 'data/openconfig-system:system/ntp/config/source-interface'
+ payload = {"openconfig-system:source-interface": configs}
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_create_servers_requests(self, configs, have):
+
+ requests = []
+
+ # Create URL and payload
+ method = PATCH
+ url = 'data/openconfig-system:system/ntp/servers'
+ server_configs = []
+ for config in configs:
+ if 'key_id' in config:
+ config['key-id'] = config['key_id']
+ config.pop('key_id')
+ server_addr = config['address']
+ server_config = {"address": server_addr, "config": config}
+ server_configs.append(server_config)
+
+ payload = {"openconfig-system:servers": {"server": server_configs}}
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_create_vrf_requests(self, configs, have):
+
+ requests = []
+
+ # Create URL and payload
+ method = PATCH
+ url = 'data/openconfig-system:system/ntp/config/network-instance'
+ payload = {"openconfig-system:network-instance": configs}
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_create_enable_ntp_auth_requests(self, configs, have):
+
+ requests = []
+
+ # Create URL and payload
+ method = PATCH
+ url = 'data/openconfig-system:system/ntp/config/enable-ntp-auth'
+ payload = {"openconfig-system:enable-ntp-auth": configs}
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_create_trusted_key_requests(self, configs, have):
+
+ requests = []
+
+ # Create URL and payload
+ method = PATCH
+ url = 'data/openconfig-system:system/ntp/config/trusted-key'
+ payload = {"openconfig-system:trusted-key": configs}
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_create_keys_requests(self, configs, have):
+
+ requests = []
+
+ # Create URL and payload
+ method = PATCH
+ url = 'data/openconfig-system:system/ntp/ntp-keys'
+ key_configs = []
+ for config in configs:
+ key_id = config['key_id']
+ if 'key_id' in config:
+ config['key-id'] = config['key_id']
+ config.pop('key_id')
+ if 'key_type' in config:
+ config['key-type'] = config['key_type']
+ config.pop('key_type')
+ if 'key_value' in config:
+ config['key-value'] = config['key_value']
+ config.pop('key_value')
+
+ key_config = {"key-id": key_id, "config": config}
+ key_configs.append(key_config)
+
+ payload = {"openconfig-system:ntp-keys": {"ntp-key": key_configs}}
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_all_ntp_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+
+ servers_config = configs.get('servers', None)
+ src_intf_config = configs.get('source_interfaces', None)
+ vrf_config = configs.get('vrf', None)
+ enable_auth_config = configs.get('enable_ntp_auth', None)
+ trusted_key_config = configs.get('trusted_keys', None)
+
+ if servers_config or src_intf_config or vrf_config or \
+ trusted_key_config or enable_auth_config is not None:
+ url = 'data/openconfig-system:system/ntp'
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ keys_config = configs.get('ntp_keys', None)
+ if keys_config:
+ url = 'data/openconfig-system:system/ntp/ntp-keys'
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_source_interface_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+ for config in configs:
+ url = 'data/openconfig-system:system/ntp/config/source-interface={0}'.format(config)
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_servers_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+ for config in configs:
+ server_addr = config['address']
+ url = 'data/openconfig-system:system/ntp/servers/server={0}'.format(server_addr)
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_vrf_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+ url = 'data/openconfig-system:system/ntp/config/network-instance'
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_enable_ntp_auth_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+ url = 'data/openconfig-system:system/ntp/config/enable-ntp-auth'
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_trusted_key_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+ for config in configs:
+ url = 'data/openconfig-system:system/ntp/config/trusted-key={0}'.format(config)
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_keys_requests(self, configs):
+
+ requests = []
+
+ # Create URL and payload
+ method = DELETE
+ key_configs = []
+ for config in configs:
+ key_id = config['key_id']
+ url = 'data/openconfig-system:system/ntp/ntp-keys/ntp-key={0}'.format(key_id)
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py
new file mode 100644
index 00000000..371019d0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/port_breakout/port_breakout.py
@@ -0,0 +1,260 @@
+#
+# -*- 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 sonic_port_breakout 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.module_utils.connection import ConnectionError
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+ get_speed_from_breakout_mode,
+ get_breakout_mode,
+)
+
+PATCH = 'patch'
+DELETE = 'delete'
+POST = 'post'
+
+
+class Port_breakout(ConfigBase):
+ """
+ The sonic_port_breakout class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'port_breakout',
+ ]
+
+ def __init__(self, module):
+ super(Port_breakout, self).__init__(module)
+
+ def get_port_breakout_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)
+ port_breakout_facts = facts['ansible_network_resources'].get('port_breakout')
+ if not port_breakout_facts:
+ return []
+ return port_breakout_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_port_breakout_facts = self.get_port_breakout_facts()
+ commands, requests = self.set_config(existing_port_breakout_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_port_breakout_facts = self.get_port_breakout_facts()
+
+ result['before'] = existing_port_breakout_facts
+ if result['changed']:
+ result['after'] = changed_port_breakout_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_port_breakout_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_port_breakout_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 = []
+ requests = []
+ state = self._module.params['state']
+ if not want:
+ want = []
+
+ have_new = self.get_all_breakout_mode(have)
+ diff = get_diff(want, have_new)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_port_breakout_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the port_breakouti except admin
+ if not want:
+ commands = have
+ else:
+ commands = want
+
+ requests = self.get_delete_port_breakout_requests(commands, have)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_port_breakout_payload(self, name, mode, match):
+ payload = {}
+ speed = get_speed_from_breakout_mode(mode)
+ if speed:
+ num_breakouts = int(mode[0])
+ mode_cfg = {'groups': {'group': [{'index': 1, 'config': {'index': 1, 'num-breakouts': num_breakouts, 'breakout-speed': speed}}]}}
+ port_cfg = {'openconfig-platform-port:breakout-mode': mode_cfg}
+ compo_cfg = {'name': name, 'port': port_cfg}
+ payload = {'openconfig-platform:components': {'component': [compo_cfg]}}
+ return payload
+
+ def get_delete_single_port_breakout(self, name, match):
+ del_req = None
+ if match:
+ del_url = 'data/openconfig-platform:components/component=%s/port/openconfig-platform-port:breakout-mode' % (name.replace('/', '%2f'))
+ del_req = {'path': del_url, 'method': DELETE}
+ return del_req
+
+ def get_modify_port_breakout_request(self, conf, match):
+ request = None
+ name = conf.get('name', None)
+ mode = conf.get('mode', None)
+ url = 'data/openconfig-platform:components'
+ payload = self.get_port_breakout_payload(name, mode, match)
+ request = {'path': url, 'method': PATCH, 'data': payload}
+ return request
+
+ def get_modify_port_breakout_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ for conf in commands:
+ match = next((cfg for cfg in have if cfg['name'] == conf['name']), None)
+ req = self.get_modify_port_breakout_request(conf, match)
+ if req:
+ requests.append(req)
+ return requests
+
+ def get_default_port_breakout_modes(self):
+ def_port_breakout_modes = []
+ request = [{"path": "operations/sonic-port-breakout:breakout_capabilities", "method": POST}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ raw_port_breakout_list = []
+ if "sonic-port-breakout:output" in response[0][1]:
+ raw_port_breakout_list = response[0][1].get("sonic-port-breakout:output", {}).get('caps', [])
+
+ for port_breakout in raw_port_breakout_list:
+ name = port_breakout.get('port', None)
+ mode = port_breakout.get('defmode', None)
+ if name and mode:
+ if '[' in mode:
+ mode = mode[:mode.index('[')]
+ def_port_breakout_modes.append({'name': name, 'mode': mode})
+ return def_port_breakout_modes
+
+ def get_delete_port_breakout_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ have_new = self.get_all_breakout_mode(have)
+ for conf in commands:
+ name = conf['name']
+ match = next((cfg for cfg in have_new if cfg['name'] == name), None)
+ req = self.get_delete_single_port_breakout(name, match)
+ if req:
+ requests.append(req)
+ return requests
+
+ def get_all_breakout_mode(self, have):
+ new_have = []
+ for cfg in have:
+ name = cfg['name']
+ mode = get_breakout_mode(self._module, name)
+ if mode:
+ new_have.append({'name': name, 'mode': mode})
+ return new_have
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py
new file mode 100644
index 00000000..d5c36d3e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/prefix_lists/prefix_lists.py
@@ -0,0 +1,458 @@
+#
+# -*- 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 sonic_prefix_lists class
+It is in this file that 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts \
+ import Facts
+
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils \
+ import (
+ get_diff,
+ update_states,
+ )
+
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+
+# from ansible.module_utils.connection import ConnectionError
+
+TEST_KEYS = [
+ {"config": {"afi": "", "name": ""}},
+ {"prefixes": {"action": "", "ge": "", "le": "", "prefix": "", "sequence": ""}}
+]
+
+DELETE = "delete"
+PATCH = "patch"
+
+
+class Prefix_lists(ConfigBase):
+ """
+ The sonic_prefix_lists class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'prefix_lists',
+ ]
+
+ prefix_sets_uri = 'data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets'
+ prefix_set_uri = 'data/openconfig-routing-policy:routing-policy/defined-sets/\
+prefix-sets/prefix-set'
+ prefix_set_delete_uri = 'data/openconfig-routing-policy:routing-policy/defined-sets/\
+prefix-sets/prefix-set={}'
+ prefix_set_delete_all_prefixes_uri = 'data/openconfig-routing-policy:routing-policy/\
+defined-sets/prefix-sets/prefix-set={}/openconfig-routing-policy-ext:extended-prefixes'
+ prefix_set_delete_prefix_uri = 'data/openconfig-routing-policy:routing-policy/\
+defined-sets/prefix-sets/prefix-set={}/\
+openconfig-routing-policy-ext:extended-prefixes/extended-prefix={},{},{}'
+ prefix_set_data_path = 'openconfig-routing-policy:prefix-set'
+ ext_prefix_set_data_path = 'openconfig-routing-policy-ext:extended-prefixes'
+
+ def __init__(self, module):
+ super(Prefix_lists, self).__init__(module)
+
+ def get_prefix_lists_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)
+ prefix_lists_facts = facts['ansible_network_resources'].get('prefix_lists', None)
+ if not prefix_lists_facts:
+ return []
+ return prefix_lists_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+
+ existing_prefix_lists_facts = self.get_prefix_lists_facts()
+ commands, requests = self.set_config(existing_prefix_lists_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_prefix_lists_facts = self.get_prefix_lists_facts()
+
+ result['before'] = existing_prefix_lists_facts
+ if result['changed']:
+ result['after'] = changed_prefix_lists_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_prefix_lists_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_prefix_lists_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 = []
+ requests = []
+ state = self._module.params['state']
+ diff = get_diff(want, have, TEST_KEYS)
+ if state == 'deleted':
+ commands, requests = self._state_deleted(want, have)
+ elif state == 'merged':
+ commands, requests = self._state_merged(diff)
+ ret_commands = commands
+ return ret_commands, requests
+
+ def _state_merged(self, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_prefix_lists_requests(commands)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ 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 = list()
+ if not want or want == []:
+ commands = have
+ requests = self.get_delete_all_prefix_list_cfg_requests()
+ else:
+ commands = want
+ requests = self.get_delete_prefix_lists_cfg_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+ return commands, requests
+
+ def get_modify_prefix_lists_requests(self, commands):
+ '''Traverse the input list of configuration "modify" commands obtained
+ from parsing the input playbook parameters. For each command,
+ create and return the appropriate set of REST API requests to modify
+ the prefix set specified by the current command.'''
+
+ requests = []
+ if not commands:
+ return requests
+
+ # Create URL and payload
+ prefix_set_payload_list = []
+ for command in commands:
+ prefix_set_payload = self.get_modify_single_prefix_set_request(command)
+ if prefix_set_payload:
+ prefix_set_payload_list.append(prefix_set_payload)
+ prefix_set_data = {self.prefix_set_data_path: prefix_set_payload_list}
+ request = {'path': self.prefix_set_uri, 'method': PATCH, 'data': prefix_set_data}
+ requests.append(request)
+ return requests
+
+ def get_modify_single_prefix_set_request(self, command):
+ '''Create and return the appropriate set of REST API requests to modfy
+ the prefix set configuration specified by the current "command".'''
+
+ request = {}
+ if not command:
+ return request
+
+ conf_afi = command.get('afi', None)
+ conf_name = command.get('name', None)
+ if not conf_afi or not conf_name:
+ return request
+
+ prefix_set_payload_header = {'name': conf_name,
+ 'config': {'name': conf_name, 'mode': conf_afi.upper()}}
+
+ pfx_conf_list = []
+ prefixes = command.get('prefixes', None)
+
+ if prefixes:
+ for prefix in prefixes:
+ pfx_payload = self.get_modify_prefix_request(prefix, conf_afi)
+ if pfx_payload:
+ pfx_conf_list.append(pfx_payload)
+
+ ext_prefix_list_payload = {'extended-prefix': pfx_conf_list}
+ ext_prefix_list_data = {self.ext_prefix_set_data_path: ext_prefix_list_payload}
+
+ prefix_set_payload = prefix_set_payload_header
+ prefix_set_payload.update(ext_prefix_list_data)
+ return prefix_set_payload
+
+ def get_modify_prefix_request(self, prefix, conf_afi):
+ '''Create a REST API request to update/merge/create the prefix specified by the
+ "prefix" input parameter.'''
+
+ pfx_payload = {}
+ prefix_val = prefix.get('prefix', None)
+ sequence = prefix.get('sequence', None)
+ action = prefix.get('action', None)
+ if not prefix_val or not sequence or not action:
+ return None
+
+ prefix_net = self.set_ipaddress_net_attrs(prefix_val, conf_afi)
+ ge = prefix.get('ge', None)
+ le = prefix.get('le', None)
+ pfx_payload['ip-prefix'] = prefix_val
+ pfx_payload['sequence-number'] = sequence
+ masklength_range_str = self.get_masklength_range_string(ge, le, prefix_net)
+ pfx_payload['masklength-range'] = masklength_range_str
+ pfx_config = {}
+ pfx_config['sequence-number'] = sequence
+ pfx_config['ip-prefix'] = prefix_val
+ pfx_config['masklength-range'] = pfx_payload['masklength-range']
+ pfx_config['openconfig-routing-policy-ext:action'] = action.upper()
+ pfx_payload['config'] = pfx_config
+
+ return pfx_payload
+
+ def get_create_prefix_lists_cfg_requests(self, commands):
+ '''Placeholder function Modify this function if necessary to enable
+ separate actions for "CREATE" vs "MERGE" ("PATCH") requests'''
+
+ return self.get_modify_prefix_lists_requests(commands)
+
+ def get_delete_prefix_lists_cfg_requests(self, commands, have):
+ '''Traverse the input list of configuration "delete" commands obtained
+ from parsing the input playbook parameters. For each command,
+ create and return the appropriate set of REST API requests to delete
+ the prefix set configuration specified by the current "command".'''
+ requests = []
+ for command in commands:
+ new_requests = self.get_delete_single_prefix_cfg_requests(command, have)
+ if new_requests and len(new_requests) > 0:
+ requests.extend(new_requests)
+ return requests
+
+ def get_delete_single_prefix_cfg_requests(self, command, have):
+ '''Create and return the appropriate set of REST API requests to delete
+ the prefix set configuration specified by the current "command".'''
+
+ requests = list()
+ pfx_set_name = command.get('name', None)
+ if not pfx_set_name:
+ return requests
+
+ cfg_prefix_set = self.prefix_set_in_config(pfx_set_name, have)
+ if not cfg_prefix_set:
+ return requests
+
+ prefixes = command.get('prefixes', None)
+ if not prefixes or prefixes == []:
+ requests = self.get_delete_prefix_set_cfg(command)
+ else:
+ requests = self.get_delete_one_prefix_list_cfg(cfg_prefix_set, command)
+ return requests
+
+ def get_delete_prefix_set_cfg(self, command):
+ '''Create and return a REST API request to delete the prefix set specified
+ by the current "command".'''
+
+ pfx_set_name = command.get('name', None)
+
+ requests = [{'path': self.prefix_set_delete_uri.format(pfx_set_name), 'method': DELETE}]
+ return requests
+
+ def get_delete_one_prefix_list_cfg(self, cfg_prefix_set, command):
+ '''Create the list of REST API prefix deletion requests needed for deletion
+ of the the requested set of prefixes from the currently configured
+ prefix set specified by "cfg_prefix_set".'''
+
+ pfx_delete_cfg_list = list()
+ prefixes = command.get('prefixes', None)
+
+ for prefix in prefixes:
+ pfx_delete_cfg = self.prefix_get_delete_single_prefix_cfg(prefix,
+ cfg_prefix_set,
+ command)
+ if pfx_delete_cfg and len(pfx_delete_cfg) > 0:
+ pfx_delete_cfg_list.append(pfx_delete_cfg)
+ return pfx_delete_cfg_list
+
+ def prefix_get_delete_single_prefix_cfg(self, prefix, cfg_prefix_set, command):
+ '''Create the REST API request to delete the prefix specified by the "prefix"
+ input parameter from the configured prefix set specified by "cfg_prefix_set".
+ Return an empty request if the prefix is not present in the confgured prefix set.'''
+
+ pfx_delete_cfg_request = {}
+ if not self.prefix_in_prefix_list_cfg(prefix, cfg_prefix_set):
+ return pfx_delete_cfg_request
+
+ conf_afi = command.get('afi', None)
+ if not conf_afi:
+ return pfx_delete_cfg_request
+
+ pfx_set_name = command.get('name', None)
+ pfx_seq = prefix.get("sequence", None)
+ pfx_val = prefix.get("prefix", None)
+ pfx_ge = prefix.get("ge", None)
+ pfx_le = prefix.get("le", None)
+
+ if not pfx_seq or not pfx_val:
+ return pfx_delete_cfg_request
+
+ prefix_net = self.set_ipaddress_net_attrs(pfx_val, conf_afi)
+ masklength_range_str = self.get_masklength_range_string(pfx_ge, pfx_le, prefix_net)
+ prefix_string = pfx_val.replace("/", "%2F")
+ extended_pfx_cfg_str = self.prefix_set_delete_prefix_uri.format(pfx_set_name,
+ int(pfx_seq),
+ prefix_string,
+ masklength_range_str)
+ pfx_delete_cfg_request = {'path': extended_pfx_cfg_str, 'method': DELETE}
+ return pfx_delete_cfg_request
+
+ def get_delete_all_prefix_list_cfg_requests(self):
+ '''Delete all prefix list configuration'''
+ requests = list()
+ requests = [{'path': self.prefix_sets_uri, 'method': DELETE}]
+ return requests
+
+ def get_masklength_range_string(self, pfx_ge, pfx_le, prefix_net):
+ '''Determine the "masklength range" string required for the openconfig
+ REST API to configure the affected prefix.'''
+ if not pfx_ge and not pfx_le:
+ masklength_range_string = "exact"
+ elif pfx_ge and not pfx_le:
+ masklength_range_string = str(pfx_ge) + ".." + str(prefix_net['max_prefixlen'])
+ elif not pfx_ge and pfx_le:
+ masklength_range_string = str(prefix_net['prefixlen']) + ".." + str(pfx_le)
+ else:
+ masklength_range_string = str(pfx_ge) + ".." + str(pfx_le)
+
+ return masklength_range_string
+
+ def prefix_set_in_config(self, pfx_set_name, have):
+ '''Determine if the prefix set specifid by "pfx_set_name" is present in
+ the current switch configuration. If it is present, return the "found"
+ prefix set. (Otherwise, return "None"'''
+ for cfg_prefix_set in have:
+ cfg_prefix_set_name = cfg_prefix_set.get('name', None)
+ if cfg_prefix_set_name and cfg_prefix_set_name == pfx_set_name:
+ return cfg_prefix_set
+
+ return None
+
+ def prefix_in_prefix_list_cfg(self, prefix, cfg_prefix_set):
+ '''Determine, based on the keys, if the "target" prefix specified by the "prefix"
+ input parameter is present in the currently configured prefix set specified
+ ty the "cfg_prefix_set" input parameter. Return "True" if the prifix is found,
+ or "False" if it isn't.'''
+ req_pfx = prefix.get("prefix", None)
+ req_seq = prefix.get("sequence", None)
+ req_ge = prefix.get("ge", None)
+ req_le = prefix.get("le", None)
+
+ cfg_prefix_list = cfg_prefix_set.get("prefixes", None)
+ if not cfg_prefix_list: # The configured prefix set has no prefix list
+ return False
+
+ for cfg_prefix in cfg_prefix_list:
+ cfg_pfx = cfg_prefix.get("prefix", None)
+ cfg_seq = cfg_prefix.get("sequence", None)
+ cfg_ge = cfg_prefix.get("ge", None)
+ cfg_le = cfg_prefix.get("le", None)
+
+ # Check for matching key attributes
+ if not (req_pfx and cfg_pfx and req_pfx == cfg_pfx):
+ continue
+ if not (req_seq and cfg_seq and req_seq == cfg_seq):
+ continue
+
+ # Check for ge match
+ if not req_ge:
+ if cfg_ge:
+ continue
+ else:
+ if not cfg_ge or req_ge != cfg_ge:
+ continue
+
+ # Check for le match
+ if not req_le:
+ if cfg_le:
+ continue
+ else:
+ if not cfg_le or req_le != cfg_le:
+ continue
+
+ # All key attributes match for this cfg_prefix
+ return True
+
+ # No matching configured prefixes were found in the prefix set.
+ return False
+
+ def set_ipaddress_net_attrs(self, prefix_val, conf_afi):
+ '''Create and return a dictionary containing the values for any prefix-related
+ attributes needed for handling of prefix configuration requests. NOTE: This
+ method should be replaced with use of the Python "ipaddress" module after
+ Ansible drops downward compatibility support for Python 2.7.'''
+
+ prefix_net = dict()
+ if conf_afi == 'ipv4':
+ prefix_net['max_prefixlen'] = 32
+ else: # Assuming IPv6 for this case
+ prefix_net['max_prefixlen'] = 128
+
+ prefix_net['prefixlen'] = int(prefix_val.split("/")[1])
+ return prefix_net
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py
new file mode 100644
index 00000000..dfa65482
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/radius_server/radius_server.py
@@ -0,0 +1,362 @@
+#
+# -*- 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 sonic_radius_server 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.module_utils.connection import ConnectionError
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+ normalize_interface_name,
+)
+
+PATCH = 'patch'
+DELETE = 'delete'
+TEST_KEYS = [
+ {'host': {'name': ''}},
+]
+
+
+class Radius_server(ConfigBase):
+ """
+ The sonic_radius_server class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'radius_server',
+ ]
+
+ def __init__(self, module):
+ super(Radius_server, self).__init__(module)
+
+ def get_radius_server_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)
+ radius_server_facts = facts['ansible_network_resources'].get('radius_server')
+ if not radius_server_facts:
+ return []
+ return radius_server_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_radius_server_facts = self.get_radius_server_facts()
+ commands, requests = self.set_config(existing_radius_server_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_radius_server_facts = self.get_radius_server_facts()
+
+ result['before'] = existing_radius_server_facts
+ if result['changed']:
+ result['after'] = changed_radius_server_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_radius_server_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']
+
+ if want and want.get('servers', None) and want['servers'].get('host', None):
+ normalize_interface_name(want['servers']['host'], self._module, 'source_interface')
+
+ have = existing_radius_server_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 = []
+ requests = []
+ state = self._module.params['state']
+ if not want:
+ want = {}
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = []
+ command = diff
+ requests = self.get_modify_radius_server_requests(command, have)
+ if command and len(requests) > 0:
+ commands = update_states([command], "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the radius_serveri except admin
+ commands = []
+ if not want:
+ command = have
+ else:
+ command = want
+
+ requests = self.get_delete_radius_server_requests(command, have)
+
+ if command and len(requests) > 0:
+ commands = update_states([command], "deleted")
+
+ return commands, requests
+
+ def get_radius_global_payload(self, conf):
+ payload = {}
+ global_cfg = {}
+
+ if conf.get('auth_type', None):
+ global_cfg['auth-type'] = conf['auth_type']
+ if conf.get('key', None):
+ global_cfg['secret-key'] = conf['key']
+ if conf.get('timeout', None):
+ global_cfg['timeout'] = conf['timeout']
+
+ if global_cfg:
+ payload = {'openconfig-system:config': global_cfg}
+
+ return payload
+
+ def get_radius_global_ext_payload(self, conf):
+ payload = {}
+ global_ext_cfg = {}
+
+ if conf.get('nas_ip', None):
+ global_ext_cfg['nas-ip-address'] = conf['nas_ip']
+ if conf.get('retransmit', None):
+ global_ext_cfg['retransmit-attempts'] = conf['retransmit']
+ if conf.get('statistics', None):
+ global_ext_cfg['statistics'] = conf['statistics']
+
+ if global_ext_cfg:
+ payload = {'openconfig-aaa-radius-ext:config': global_ext_cfg}
+
+ return payload
+
+ def get_radius_server_payload(self, hosts):
+ payload = {}
+ servers_load = []
+ for host in hosts:
+ if host.get('name', None):
+ host_cfg = {'address': host['name']}
+ if host.get('auth_type', None):
+ host_cfg['auth-type'] = host['auth_type']
+ if host.get('priority', None):
+ host_cfg['priority'] = host['priority']
+ if host.get('vrf', None):
+ host_cfg['vrf'] = host['vrf']
+ if host.get('timeout', None):
+ host_cfg['timeout'] = host['timeout']
+
+ radius_port_key_cfg = {}
+ if host.get('port', None):
+ radius_port_key_cfg['auth-port'] = host['port']
+ if host.get('key', None):
+ radius_port_key_cfg['secret-key'] = host['key']
+ if host.get('retransmit', None):
+ radius_port_key_cfg['retransmit-attempts'] = host['retransmit']
+ if host.get('source_interface', None):
+ radius_port_key_cfg['openconfig-aaa-radius-ext:source-interface'] = host['source_interface']
+
+ if radius_port_key_cfg:
+ consolidated_load = {'address': host['name']}
+ consolidated_load['config'] = host_cfg
+ consolidated_load['radius'] = {'config': radius_port_key_cfg}
+ servers_load.append(consolidated_load)
+
+ if servers_load:
+ payload = {'openconfig-system:servers': {'server': servers_load}}
+
+ return payload
+
+ def get_modify_servers_request(self, command):
+ request = None
+
+ hosts = []
+ if command.get('servers', None) and command['servers'].get('host', None):
+ hosts = command['servers']['host']
+ if hosts:
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers'
+ payload = self.get_radius_server_payload(hosts)
+ if payload:
+ request = {'path': url, 'method': PATCH, 'data': payload}
+
+ return request
+
+ def get_modify_global_config_request(self, conf):
+ request = None
+
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config'
+ payload = self.get_radius_global_payload(conf)
+ if payload:
+ request = {'path': url, 'method': PATCH, 'data': payload}
+
+ return request
+
+ def get_modify_global_ext_config_request(self, conf):
+ request = None
+
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config'
+ payload = self.get_radius_global_ext_payload(conf)
+ if payload:
+ request = {'path': url, 'method': PATCH, 'data': payload}
+
+ return request
+
+ def get_modify_radius_server_requests(self, command, have):
+ requests = []
+ if not command:
+ return requests
+
+ request = self.get_modify_global_config_request(command)
+ if request:
+ requests.append(request)
+
+ request = self.get_modify_global_ext_config_request(command)
+ if request:
+ requests.append(request)
+
+ request = self.get_modify_servers_request(command)
+ if request:
+ requests.append(request)
+
+ return requests
+
+ def get_delete_global_ext_params(self, conf, match):
+
+ requests = []
+
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config/'
+ if conf.get('nas_ip', None) and match.get('nas_ip', None):
+ requests.append({'path': url + 'nas-ip-address', 'method': DELETE})
+ if conf.get('retransmit', None) and match.get('retransmit', None):
+ requests.append({'path': url + 'retransmit-attempts', 'method': DELETE})
+ if conf.get('statistics', None) and match.get('statistics', None):
+ requests.append({'path': url + 'statistics', 'method': DELETE})
+
+ return requests
+
+ def get_delete_global_params(self, conf, match):
+
+ requests = []
+
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config/'
+ if conf.get('auth_type', None) and match.get('auth_type', None) and match['auth_type'] != 'pap':
+ requests.append({'path': url + 'auth-type', 'method': DELETE})
+ if conf.get('key', None) and match.get('key', None):
+ requests.append({'path': url + 'secret-key', 'method': DELETE})
+ if conf.get('timeout', None) and match.get('timeout', None) and match['timeout'] != 5:
+ requests.append({'path': url + 'timeout', 'method': DELETE})
+
+ return requests
+
+ def get_delete_servers(self, command, have):
+ requests = []
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers/server='
+
+ mat_hosts = []
+ if have.get('servers', None) and have['servers'].get('host', None):
+ mat_hosts = have['servers']['host']
+
+ if command.get('servers', None):
+ if command['servers'].get('host', None):
+ hosts = command['servers']['host']
+ else:
+ hosts = mat_hosts
+
+ if mat_hosts and hosts:
+ for host in hosts:
+ if next((m_host for m_host in mat_hosts if m_host['name'] == host['name']), None):
+ requests.append({'path': url + host['name'], 'method': DELETE})
+
+ return requests
+
+ def get_delete_radius_server_requests(self, command, have):
+ requests = []
+ if not command:
+ return requests
+
+ requests.extend(self.get_delete_global_params(command, have))
+ requests.extend(self.get_delete_global_ext_params(command, have))
+ requests.extend(self.get_delete_servers(command, have))
+
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py
new file mode 100644
index 00000000..04735747
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/static_routes/static_routes.py
@@ -0,0 +1,344 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_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,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+
+network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+protocol_static_routes_path = 'protocols/protocol=STATIC,static/static-routes'
+
+PATCH = 'patch'
+DELETE = 'delete'
+TEST_KEYS = [
+ {'config': {'vrf_name': ''}},
+ {'static_list': {'prefix': ''}},
+ {'next_hops': {'index': ''}},
+]
+
+
+class Static_routes(ConfigBase):
+ """
+ The sonic_static_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):
+ """ 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)
+ static_routes_facts = facts['ansible_network_resources'].get('static_routes')
+ if not static_routes_facts:
+ return []
+ return static_routes_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = []
+ commands = []
+ existing_static_routes_facts = self.get_static_routes_facts()
+ commands, requests = self.set_config(existing_static_routes_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_static_routes_facts = self.get_static_routes_facts()
+
+ result['before'] = existing_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
+ """
+ want = self._module.params['config']
+ have = existing_static_routes_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 = []
+ requests = []
+ state = self._module.params['state']
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = self.get_modify_static_routes_requests(commands, have)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ is_delete_all = False
+ # if want is none, then delete ALL
+ if not want:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ requests = self.get_delete_static_routes_requests(commands, have, is_delete_all)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_modify_static_routes_requests(self, commands, have):
+ requests = []
+
+ if not commands:
+ return requests
+
+ for conf in commands:
+ vrf_name = conf.get('vrf_name', None)
+ static_list = conf.get('static_list', [])
+ for static in static_list:
+ prefix = static.get('prefix', None)
+ next_hops = static.get('next_hops', [])
+ if next_hops:
+ for next_hop in next_hops:
+ requests.append(self.get_modify_static_route_request(vrf_name, prefix, next_hop))
+
+ return requests
+
+ def get_modify_static_route_request(self, vrf_name, prefix, next_hop):
+ request = None
+ next_hop_cfg = {}
+ index = next_hop.get('index', {})
+ blackhole = index.get('blackhole', None)
+ interface = index.get('interface', None)
+ nexthop_vrf = index.get('nexthop_vrf', None)
+ next_hop_attr = index.get('next_hop', None)
+ metric = next_hop.get('metric', None)
+ track = next_hop.get('track', None)
+ tag = next_hop.get('tag', None)
+ idx = self.generate_index(index)
+ if idx:
+ next_hop_cfg['index'] = idx
+ if blackhole:
+ next_hop_cfg['blackhole'] = blackhole
+ if nexthop_vrf:
+ next_hop_cfg['network-instance'] = nexthop_vrf
+ if next_hop:
+ next_hop_cfg['next-hop'] = next_hop_attr
+ if metric:
+ next_hop_cfg['metric'] = metric
+ if track:
+ next_hop_cfg['track'] = track
+ if tag:
+ next_hop_cfg['tag'] = tag
+
+ url = '%s=%s/%s' % (network_instance_path, vrf_name, protocol_static_routes_path)
+ next_hops_cfg = {'next-hop': [{'index': idx, 'config': next_hop_cfg}]}
+ if interface:
+ next_hops_cfg['next-hop'][0]['interface-ref'] = {'config': {'interface': interface}}
+ payload = {'openconfig-network-instance:static-routes': {'static': [{'prefix': prefix, 'config': {'prefix': prefix}, 'next-hops': next_hops_cfg}]}}
+ request = {'path': url, 'method': PATCH, 'data': payload}
+
+ return request
+
+ def generate_index(self, index):
+ idx = None
+ blackhole = index.get('blackhole', None)
+ interface = index.get('interface', None)
+ nexthop_vrf = index.get('nexthop_vrf', None)
+ next_hop = index.get('next_hop', None)
+
+ if blackhole is True:
+ idx = 'DROP'
+ else:
+ if interface:
+ if not next_hop and not nexthop_vrf:
+ idx = interface
+ elif next_hop and not nexthop_vrf:
+ idx = interface + '_' + next_hop
+ elif nexthop_vrf and not next_hop:
+ idx = interface + '_' + nexthop_vrf
+ else:
+ idx = interface + '_' + next_hop + '_' + nexthop_vrf
+ else:
+ if next_hop and not nexthop_vrf:
+ idx = next_hop
+ elif next_hop and nexthop_vrf:
+ idx = next_hop + '_' + nexthop_vrf
+
+ return idx
+
+ def get_delete_static_routes_requests(self, commands, have, is_delete_all):
+ requests = []
+ if is_delete_all:
+ for cmd in commands:
+ vrf_name = cmd.get('vrf_name', None)
+ if vrf_name:
+ requests.append(self.get_delete_static_routes_for_vrf(vrf_name))
+ else:
+ for cmd in commands:
+ vrf_name = cmd.get('vrf_name', None)
+ static_list = cmd.get('static_list', [])
+ for cfg in have:
+ cfg_vrf_name = cfg.get('vrf_name', None)
+ if vrf_name == cfg_vrf_name:
+ if not static_list:
+ requests.append(self.get_delete_static_routes_for_vrf(vrf_name))
+ else:
+ for static in static_list:
+ prefix = static.get('prefix', None)
+ next_hops = static.get('next_hops', [])
+ cfg_static_list = cfg.get('static_list', [])
+ for cfg_static in cfg_static_list:
+ cfg_prefix = cfg_static.get('prefix', None)
+ if prefix == cfg_prefix:
+ if prefix and not next_hops:
+ requests.append(self.get_delete_static_routes_prefix_request(vrf_name, prefix))
+ else:
+ for next_hop in next_hops:
+ index = next_hop.get('index', {})
+ idx = self.generate_index(index)
+ metric = next_hop.get('metric', None)
+ track = next_hop.get('track', None)
+ tag = next_hop.get('tag', None)
+
+ cfg_next_hops = cfg_static.get('next_hops', [])
+ if cfg_next_hops:
+ for cfg_next_hop in cfg_next_hops:
+ cfg_index = cfg_next_hop.get('index', {})
+ cfg_idx = self.generate_index(cfg_index)
+ if idx == cfg_idx:
+ cfg_metric = cfg_next_hop.get('metric', None)
+ cfg_track = cfg_next_hop.get('track', None)
+ cfg_tag = cfg_next_hop.get('tag', None)
+ if not metric and not track and not tag:
+ requests.append(self.get_delete_static_routes_next_hop_request(vrf_name, prefix, idx))
+ else:
+ if metric == cfg_metric:
+ requests.append(self.get_delete_next_hop_config_attr_request(vrf_name, prefix, idx,
+ 'metric'))
+ if track == cfg_track:
+ requests.append(self.get_delete_next_hop_config_attr_request(vrf_name, prefix, idx,
+ 'track'))
+ if tag == cfg_tag:
+ requests.append(self.get_delete_next_hop_config_attr_request(vrf_name, prefix, idx, 'tag'))
+
+ return requests
+
+ def get_delete_static_routes_for_vrf(self, vrf_name):
+ url = '%s=%s/%s' % (network_instance_path, vrf_name, protocol_static_routes_path)
+ request = {'path': url, 'method': DELETE}
+
+ return request
+
+ def get_delete_static_routes_prefix_request(self, vrf_name, prefix):
+ prefix = prefix.replace('/', '%2F')
+ url = '%s=%s/%s/static=%s' % (network_instance_path, vrf_name, protocol_static_routes_path, prefix)
+ request = {'path': url, 'method': DELETE}
+
+ return request
+
+ def get_delete_static_routes_next_hop_request(self, vrf_name, prefix, index):
+ prefix = prefix.replace('/', '%2F')
+ url = '%s=%s/%s/static=%s' % (network_instance_path, vrf_name, protocol_static_routes_path, prefix)
+ url += '/next-hops/next-hop=%s' % (index)
+ request = {'path': url, 'method': DELETE}
+
+ return request
+
+ def get_delete_next_hop_config_attr_request(self, vrf_name, prefix, index, attr):
+ prefix = prefix.replace('/', '%2F')
+ url = '%s=%s/%s/static=%s' % (network_instance_path, vrf_name, protocol_static_routes_path, prefix)
+ url += '/next-hops/next-hop=%s/config/%s' % (index, attr)
+ request = {'path': url, 'method': DELETE}
+
+ return request
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py
new file mode 100644
index 00000000..21d575a1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/system/system.py
@@ -0,0 +1,294 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_system 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.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+
+PATCH = 'patch'
+DELETE = 'delete'
+
+
+class System(ConfigBase):
+ """
+ The sonic_system class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'system',
+ ]
+
+ def __init__(self, module):
+ super(System, self).__init__(module)
+
+ def get_system_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)
+ system_facts = facts['ansible_network_resources'].get('system')
+ if not system_facts:
+ return []
+ return system_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_system_facts = self.get_system_facts()
+ commands, requests = self.set_config(existing_system_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ self.edit_config(requests)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_system_facts = self.get_system_facts()
+
+ result['before'] = existing_system_facts
+ if result['changed']:
+ result['after'] = changed_system_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def edit_config(self, requests):
+ try:
+ response = edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ def set_config(self, existing_system_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_system_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 == 'deleted':
+ commands = self._state_deleted(want, have)
+ elif state == 'merged':
+ diff = get_diff(want, have)
+ commands = self._state_merged(want, have, diff)
+ return commands
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ requests = []
+ commands = []
+ if diff:
+ requests = self.get_create_system_request(want, diff)
+ if len(requests) > 0:
+ commands = update_states(diff, "merged")
+ return commands, requests
+
+ 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 = []
+ requests = []
+ new_have = self.remove_default_entries(have)
+ if not want:
+ if have:
+ requests = self.get_delete_all_system_request(new_have)
+ if len(requests) > 0:
+ commands = update_states(have, "deleted")
+ else:
+ want = utils.remove_empties(want)
+ d_diff = get_diff(want, new_have, is_skeleton=True)
+ diff_want = get_diff(want, d_diff, is_skeleton=True)
+ if diff_want:
+ requests = self.get_delete_all_system_request(diff_want)
+ if len(requests) > 0:
+ commands = update_states(diff_want, "deleted")
+ return commands, requests
+
+ def get_create_system_request(self, want, commands):
+ requests = []
+ host_path = 'data/openconfig-system:system/config'
+ method = PATCH
+ hostname_payload = self.build_create_hostname_payload(commands)
+ if hostname_payload:
+ request = {'path': host_path, 'method': method, 'data': hostname_payload}
+ requests.append(request)
+ name_path = 'data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode'
+ name_payload = self.build_create_name_payload(commands)
+ if name_payload:
+ request = {'path': name_path, 'method': method, 'data': name_payload}
+ requests.append(request)
+ anycast_path = 'data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/'
+ anycast_payload = self.build_create_anycast_payload(commands)
+ if anycast_payload:
+ request = {'path': anycast_path, 'method': method, 'data': anycast_payload}
+ requests.append(request)
+ return requests
+
+ def build_create_hostname_payload(self, commands):
+ payload = {"openconfig-system:config": {}}
+ if "hostname" in commands and commands["hostname"]:
+ payload['openconfig-system:config'].update({"hostname": commands["hostname"]})
+ return payload
+
+ def build_create_name_payload(self, commands):
+ payload = {}
+ if "interface_naming" in commands and commands["interface_naming"]:
+ payload.update({'sonic-device-metadata:intf_naming_mode': commands["interface_naming"]})
+ return payload
+
+ def build_create_anycast_payload(self, commands):
+ payload = {}
+ if "anycast_address" in commands and commands["anycast_address"]:
+ payload = {"sonic-sag:SAG_GLOBAL_LIST": []}
+ temp = {}
+ if "ipv4" in commands["anycast_address"] and commands["anycast_address"]["ipv4"]:
+ temp.update({'IPv4': "enable"})
+ if "ipv4" in commands["anycast_address"] and not commands["anycast_address"]["ipv4"]:
+ temp.update({'IPv4': "disable"})
+ if "ipv6" in commands["anycast_address"] and commands["anycast_address"]["ipv6"]:
+ temp.update({'IPv6': "enable"})
+ if "ipv6" in commands["anycast_address"] and not commands["anycast_address"]["ipv6"]:
+ temp.update({'IPv6': "disable"})
+ if "mac_address" in commands["anycast_address"] and commands["anycast_address"]["mac_address"]:
+ temp.update({'gwmac': commands["anycast_address"]["mac_address"]})
+ if temp:
+ temp.update({"table_distinguisher": "IP"})
+ payload["sonic-sag:SAG_GLOBAL_LIST"].append(temp)
+ return payload
+
+ def remove_default_entries(self, data):
+ new_data = {}
+ if not data:
+ return new_data
+ else:
+ hostname = data.get('hostname', None)
+ if hostname != "sonic":
+ new_data["hostname"] = hostname
+ intf_name = data.get('interface_naming', None)
+ if intf_name != "native":
+ new_data["interface_naming"] = intf_name
+ new_anycast = {}
+ anycast = data.get('anycast_address', None)
+ if anycast:
+ ipv4 = anycast.get("ipv4", None)
+ if ipv4 is not True:
+ new_anycast["ipv4"] = ipv4
+ ipv6 = anycast.get("ipv6", None)
+ if ipv6 is not True:
+ new_anycast["ipv6"] = ipv6
+ mac = anycast.get("mac_address", None)
+ if mac is not None:
+ new_anycast["mac_address"] = mac
+ new_data["anycast_address"] = new_anycast
+ return new_data
+
+ def get_delete_all_system_request(self, have):
+ requests = []
+ if "hostname" in have:
+ request = self.get_hostname_delete_request()
+ requests.append(request)
+ if "interface_naming" in have:
+ request = self.get_intfname_delete_request()
+ requests.append(request)
+ if "anycast_address" in have:
+ request = self.get_anycast_delete_request(have["anycast_address"])
+ requests.extend(request)
+ return requests
+
+ def get_hostname_delete_request(self):
+ path = 'data/openconfig-system:system/config/'
+ method = PATCH
+ payload = {"openconfig-system:config": {}}
+ payload['openconfig-system:config'].update({"hostname": "sonic"})
+ request = {'path': path, 'method': method, 'data': payload}
+ return request
+
+ def get_intfname_delete_request(self):
+ path = 'data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost/intf_naming_mode'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ return request
+
+ def get_anycast_delete_request(self, anycast):
+ requests = []
+ if "ipv4" in anycast:
+ path = 'data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST=IP/IPv4'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ requests.append(request)
+ if "ipv6" in anycast:
+ path = 'data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST=IP/IPv6'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ requests.append(request)
+ if "mac_address" in anycast:
+ path = 'data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST=IP/gwmac'
+ method = DELETE
+ request = {'path': path, 'method': method}
+ requests.append(request)
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py
new file mode 100644
index 00000000..498fcbe2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/tacacs_server/tacacs_server.py
@@ -0,0 +1,318 @@
+#
+# -*- 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 sonic_tacacs_server 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.module_utils.connection import ConnectionError
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ update_states,
+ get_diff,
+ get_normalize_interface_name,
+)
+
+PATCH = 'patch'
+DELETE = 'delete'
+TEST_KEYS = [
+ {'host': {'name': ''}},
+]
+
+
+class Tacacs_server(ConfigBase):
+ """
+ The sonic_tacacs_server class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'tacacs_server',
+ ]
+
+ def __init__(self, module):
+ super(Tacacs_server, self).__init__(module)
+
+ def get_tacacs_server_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)
+ tacacs_server_facts = facts['ansible_network_resources'].get('tacacs_server')
+ if not tacacs_server_facts:
+ return []
+ return tacacs_server_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_tacacs_server_facts = self.get_tacacs_server_facts()
+ commands, requests = self.set_config(existing_tacacs_server_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_tacacs_server_facts = self.get_tacacs_server_facts()
+
+ result['before'] = existing_tacacs_server_facts
+ if result['changed']:
+ result['after'] = changed_tacacs_server_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_tacacs_server_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']
+
+ if want and want.get('source_interface', None):
+ want['source_interface'] = get_normalize_interface_name(want['source_interface'], self._module)
+
+ have = existing_tacacs_server_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 = []
+ requests = []
+ state = self._module.params['state']
+ if not want:
+ want = {}
+
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = []
+ command = diff
+ requests = self.get_modify_tacacs_server_requests(command, have)
+ if command and len(requests) > 0:
+ commands = update_states([command], "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the tacacs_serveri except admin
+ commands = []
+ if not want:
+ command = have
+ else:
+ command = want
+
+ requests = self.get_delete_tacacs_server_requests(command, have)
+
+ if command and len(requests) > 0:
+ commands = update_states([command], "deleted")
+
+ return commands, requests
+
+ def get_tacacs_global_payload(self, conf):
+ payload = {}
+ global_cfg = {}
+
+ if conf.get('auth_type', None):
+ global_cfg['auth-type'] = conf['auth_type']
+ if conf.get('key', None):
+ global_cfg['secret-key'] = conf['key']
+ if conf.get('source_interface', None):
+ global_cfg['source-interface'] = conf['source_interface']
+ if conf.get('timeout', None):
+ global_cfg['timeout'] = conf['timeout']
+
+ if global_cfg:
+ payload = {'openconfig-system:config': global_cfg}
+
+ return payload
+
+ def get_tacacs_server_payload(self, hosts):
+ payload = {}
+ servers_load = []
+ for host in hosts:
+ if host.get('name', None):
+ host_cfg = {'address': host['name']}
+ if host.get('auth_type', None):
+ host_cfg['auth-type'] = host['auth_type']
+ if host.get('priority', None):
+ host_cfg['priority'] = host['priority']
+ if host.get('vrf', None):
+ host_cfg['vrf'] = host['vrf']
+ if host.get('timeout', None):
+ host_cfg['timeout'] = host['timeout']
+
+ tacacs_port_key_cfg = {}
+ if host.get('port', None):
+ tacacs_port_key_cfg['port'] = host['port']
+ if host.get('key', None):
+ tacacs_port_key_cfg['secret-key'] = host['key']
+
+ if tacacs_port_key_cfg:
+ consolidated_load = {'address': host['name']}
+ consolidated_load['config'] = host_cfg
+ consolidated_load['tacacs'] = {'config': tacacs_port_key_cfg}
+ servers_load.append(consolidated_load)
+
+ if servers_load:
+ payload = {'openconfig-system:servers': {'server': servers_load}}
+
+ return payload
+
+ def get_modify_servers_request(self, command):
+ request = None
+
+ hosts = []
+ if command.get('servers', None) and command['servers'].get('host', None):
+ hosts = command['servers']['host']
+ if hosts:
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers'
+ payload = self.get_tacacs_server_payload(hosts)
+ if payload:
+ request = {'path': url, 'method': PATCH, 'data': payload}
+
+ return request
+
+ def get_modify_global_config_request(self, conf):
+ request = None
+
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config'
+ payload = self.get_tacacs_global_payload(conf)
+ if payload:
+ request = {'path': url, 'method': PATCH, 'data': payload}
+
+ return request
+
+ def get_modify_tacacs_server_requests(self, command, have):
+ requests = []
+ if not command:
+ return requests
+
+ request = self.get_modify_global_config_request(command)
+ if request:
+ requests.append(request)
+
+ request = self.get_modify_servers_request(command)
+ if request:
+ requests.append(request)
+
+ return requests
+
+ def get_delete_global_params(self, conf, match):
+
+ requests = []
+
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config/'
+ if conf.get('auth_type', None) and match.get('auth_type', None) and match['auth_type'] != 'pap':
+ requests.append({'path': url + 'auth-type', 'method': DELETE})
+ if conf.get('key', None) and match.get('key', None):
+ requests.append({'path': url + 'secret-key', 'method': DELETE})
+ if conf.get('source_interface', None) and match.get('source_interface', None):
+ requests.append({'path': url + 'source-interface', 'method': DELETE})
+ if conf.get('timeout', None) and match.get('timeout', None) and match['timeout'] != 5:
+ requests.append({'path': url + 'timeout', 'method': DELETE})
+
+ return requests
+
+ def get_delete_servers(self, command, have):
+ requests = []
+ url = 'data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers/server='
+
+ mat_hosts = []
+ if have.get('servers', None) and have['servers'].get('host', None):
+ mat_hosts = have['servers']['host']
+
+ hosts = []
+ if command.get('servers', None):
+ if command['servers'].get('host', None):
+ hosts = command['servers']['host']
+ else:
+ hosts = mat_hosts
+
+ if mat_hosts and hosts:
+ for host in hosts:
+ if next((m_host for m_host in mat_hosts if m_host['name'] == host['name']), None):
+ requests.append({'path': url + host['name'], 'method': DELETE})
+
+ return requests
+
+ def get_delete_tacacs_server_requests(self, command, have):
+ requests = []
+ if not command:
+ return requests
+
+ requests.extend(self.get_delete_global_params(command, have))
+ requests.extend(self.get_delete_servers(command, have))
+
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py
new file mode 100644
index 00000000..73398cf7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/users/users.py
@@ -0,0 +1,299 @@
+#
+# -*- 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 sonic_users 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 json
+
+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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ dict_to_set,
+ update_states,
+ get_diff,
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'delete'
+
+
+class Users(ConfigBase):
+ """
+ The sonic_users class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'users',
+ ]
+
+ def __init__(self, module):
+ super(Users, self).__init__(module)
+
+ def get_users_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)
+ users_facts = facts['ansible_network_resources'].get('users')
+ if not users_facts:
+ return []
+ return users_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ existing_users_facts = self.get_users_facts()
+ commands, requests = self.set_config(existing_users_facts)
+ auth_error = False
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ try:
+ json_obj = json.loads(str(exc).replace("'", '"'))
+ if json_obj and type(json_obj) is dict and 401 == json_obj['code']:
+ auth_error = True
+ warnings.append("Unable to get after configs as password got changed for current user")
+ else:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ except Exception as err:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_users_facts = []
+ if not auth_error:
+ changed_users_facts = self.get_users_facts()
+
+ result['before'] = existing_users_facts
+ if result['changed']:
+ result['after'] = changed_users_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_users_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_users_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 = []
+ requests = []
+ state = self._module.params['state']
+ if not want:
+ want = []
+
+ new_want = [{'name': conf['name'], 'role': conf['role']} for conf in want]
+ new_have = [{'name': conf['name'], 'role': conf['role']} for conf in have]
+ new_diff = get_diff(new_want, new_have)
+
+ diff = []
+ for cfg in new_diff:
+ match = next((w_cfg for w_cfg in want if w_cfg['name'] == cfg['name']), None)
+ if match:
+ diff.append(match)
+
+ for cfg in want:
+ if cfg['password'] and cfg['update_password'] == 'always':
+ d_match = next((d_cfg for d_cfg in diff if d_cfg['name'] == cfg['name']), None)
+ if d_match is None:
+ diff.append(cfg)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ self.validate_new_users(want, have)
+
+ commands = diff
+ requests = self.get_modify_users_requests(commands, have)
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the usersi except admin
+ if not want:
+ commands = have
+ else:
+ commands = want
+
+ requests = self.get_delete_users_requests(commands, have)
+
+ if commands and len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_pwd(self, pw):
+ clear_pwd = hashed_pwd = ""
+ pwd = pw.replace("\\", "")
+ if pwd[:3] == '$6$':
+ hashed_pwd = pwd
+ else:
+ clear_pwd = pwd
+ return clear_pwd, hashed_pwd
+
+ def get_single_user_payload(self, name, role, password, update_pass, match):
+ user_cfg = {'username': name}
+ if not role and match:
+ role = match['role']
+
+ if not password and match:
+ password = match['password']
+
+ if role:
+ user_cfg['role'] = role
+
+ if password:
+ clear_pwd, hashed_pwd = self.get_pwd(password)
+ user_cfg['password'] = clear_pwd
+ user_cfg['password-hashed'] = hashed_pwd
+
+ pay_load = {'openconfig-system:user': [{'username': name, 'config': user_cfg}]}
+ return pay_load
+
+ def get_modify_single_user_request(self, conf, match):
+ request = None
+ name = conf.get('name', None)
+ role = conf.get('role', None)
+ password = conf.get('password', None)
+ update_pass = conf.get('update_password', None)
+ if role or (password and update_pass == 'always'):
+ url = 'data/openconfig-system:system/aaa/authentication/users/user=%s' % (name)
+ payload = self.get_single_user_payload(name, role, password, update_pass, match)
+ request = {'path': url, 'method': PATCH, 'data': payload}
+ return request
+
+ def get_modify_users_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ for conf in commands:
+ match = next((cfg for cfg in have if cfg['name'] == conf['name']), None)
+ req = self.get_modify_single_user_request(conf, match)
+ if req:
+ requests.append(req)
+ return requests
+
+ def get_new_users(self, want, have):
+ new_users = []
+ for user in want:
+ if not next((h_user for h_user in have if h_user['name'] == user['name']), None):
+ new_users.append(user)
+ return new_users
+
+ def validate_new_users(self, want, have):
+ new_users = self.get_new_users(want, have)
+ invalid_users = []
+ for user in new_users:
+ params = []
+ if not user['role']:
+ params.append('role')
+ if not user['password']:
+ params.append('password')
+ if params:
+ invalid_users.append({user['name']: params})
+ if invalid_users:
+ err_msg = "Missing parameter(s) for new users! " + str(invalid_users)
+ self._module.fail_json(msg=err_msg, code=513)
+
+ def get_delete_users_requests(self, commands, have):
+ requests = []
+ if not commands:
+ return requests
+
+ # Skip the asmin user in 'deleted' state. we cannot delete all users
+ admin_usr = None
+
+ for conf in commands:
+ # Skip the asmin user in 'deleted' state. we cannot delete all users
+ if conf['name'] == 'admin':
+ admin_usr = conf
+ continue
+ match = next((cfg for cfg in have if cfg['name'] == conf['name']), None)
+ if match:
+ url = 'data/openconfig-system:system/aaa/authentication/users/user=%s' % (conf['name'])
+ requests.append({'path': url, 'method': DELETE})
+
+ if admin_usr:
+ commands.remove(admin_usr)
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py
new file mode 100644
index 00000000..40405107
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vlans/vlans.py
@@ -0,0 +1,265 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_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 json
+
+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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states,
+ remove_empties_from_list,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.interfaces_util import (
+ build_interfaces_create_request,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils._text import to_native
+from ansible.module_utils.connection import ConnectionError
+import traceback
+
+LIB_IMP_ERR = None
+ERR_MSG = None
+try:
+ import jinja2
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+TEST_KEYS = [
+ {'config': {'vlan_id': ''}},
+]
+
+
+class Vlans(ConfigBase):
+ """
+ The sonic_vlans class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'vlans',
+ ]
+
+ def __init__(self, module):
+ super(Vlans, self).__init__(module)
+
+ def get_vlans_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)
+ vlans_facts = facts['ansible_network_resources'].get('vlans')
+ if not vlans_facts:
+ return []
+ return vlans_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+
+ existing_vlans_facts = self.get_vlans_facts()
+ commands, requests = self.set_config(existing_vlans_facts)
+ if commands:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_vlans_facts = self.get_vlans_facts()
+
+ result['before'] = existing_vlans_facts
+ if result['changed']:
+ result['after'] = 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 = remove_empties_from_list(self._module.params['config'])
+ have = existing_vlans_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']
+ # diff method works on dict, so creating temp dict
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+
+ ret_commands = remove_empties_from_list(commands)
+ return ret_commands, requests
+
+ def _state_replaced(self, want, have, diff):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ return self._state_merged(want, have, diff)
+
+ def _state_overridden(self, want, have, diff):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ ret_requests = list()
+ commands = list()
+ vlans_to_delete = get_diff(have, want, TEST_KEYS)
+ if vlans_to_delete:
+ delete_vlans_requests = self.get_delete_vlans_requests(vlans_to_delete)
+ ret_requests.extend(delete_vlans_requests)
+ commands.extend(update_states(vlans_to_delete, "deleted"))
+
+ if diff:
+ vlans_to_create_requests = self.get_create_vlans_requests(diff)
+ ret_requests.extend(vlans_to_create_requests)
+ commands.extend(update_states(diff, "merged"))
+
+ return commands, ret_requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration at position-0
+ Requests necessary to merge to the current configuration
+ at position-1
+ """
+ commands = update_states(diff, "merged")
+ requests = self.get_create_vlans_requests(commands)
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = list()
+ # if want is none, then delete all the vlans
+ if not want:
+ commands = have
+ else: # delete specific vlans
+ commands = get_diff(want, diff, TEST_KEYS)
+
+ requests = self.get_delete_vlans_requests(commands)
+ commands = update_states(commands, "deleted")
+ return commands, requests
+
+ def get_delete_vlans_requests(self, configs):
+ requests = []
+ if not configs:
+ return requests
+ # Create URL and payload
+ url = "data/openconfig-interfaces:interfaces/interface=Vlan{}"
+ method = "DELETE"
+ for vlan in configs:
+ vlan_id = vlan.get("vlan_id")
+ description = vlan.get("description")
+ if description:
+ path = self.get_delete_vlan_config_attr(vlan_id, "description")
+ else:
+ path = url.format(vlan_id)
+
+ request = {"path": path,
+ "method": method,
+ }
+ requests.append(request)
+
+ return requests
+
+ def get_delete_vlan_config_attr(self, vlan_id, attr_name):
+ url = "data/openconfig-interfaces:interfaces/interface=Vlan{}/config/{}"
+ path = url.format(vlan_id, attr_name)
+
+ return path
+
+ def get_create_vlans_requests(self, configs):
+ requests = []
+ if not configs:
+ return requests
+ for vlan in configs:
+ vlan_id = vlan.get("vlan_id")
+ interface_name = "Vlan" + str(vlan_id)
+ description = vlan.get("description", None)
+ request = build_interfaces_create_request(interface_name=interface_name)
+ requests.append(request)
+ if description:
+ requests.append(self.get_modify_vlan_config_attr(interface_name, 'description', description))
+
+ return requests
+
+ def get_modify_vlan_config_attr(self, intf_name, attr_name, attr_value):
+ url = "data/openconfig-interfaces:interfaces/interface={}/config"
+ payload = {"openconfig-interfaces:config": {"name": intf_name, attr_name: attr_value}}
+ method = "PATCH"
+ request = {"path": url.format(intf_name), "method": method, "data": payload}
+
+ return request
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py
new file mode 100644
index 00000000..83deb0ec
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vrfs/vrfs.py
@@ -0,0 +1,303 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_vrfs 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states,
+ normalize_interface_name
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'DELETE'
+TEST_KEYS = [
+ {'interfaces': {'name': ''}},
+]
+
+
+class Vrfs(ConfigBase):
+ """
+ The sonic_vrfs class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'vrfs',
+ ]
+
+ delete_all_flag = False
+
+ def __init__(self, module):
+ super(Vrfs, self).__init__(module)
+
+ def get_vrf_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)
+ vrf_interfaces_facts = facts['ansible_network_resources'].get('vrfs')
+ if not vrf_interfaces_facts:
+ return []
+ return vrf_interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+ commands = list()
+
+ existing_vrf_interfaces_facts = self.get_vrf_facts()
+ commands, requests = self.set_config(existing_vrf_interfaces_facts)
+ if commands and len(requests) > 0:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_vrf_interfaces_facts = self.get_vrf_facts()
+
+ result['before'] = existing_vrf_interfaces_facts
+ if result['changed']:
+ result['after'] = changed_vrf_interfaces_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_vrf_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['config']
+ have = existing_vrf_interfaces_facts
+ if want is None:
+ want = []
+
+ for each in want:
+ if each.get("members", None):
+ interfaces = each["members"].get("interfaces", None)
+ if interfaces:
+ interfaces = normalize_interface_name(interfaces, self._module)
+ 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']
+ diff = get_diff(want, have, TEST_KEYS)
+
+ if state == 'deleted':
+ commands, requests = self._state_deleted(want, have)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :param want: the additive configuration as a dictionary
+ :param obj_in_have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration
+ """
+ commands = diff
+ requests = []
+ if commands:
+ requests = self.get_create_requests(commands, have)
+
+ if len(requests) > 0:
+ commands = update_states(commands, "merged")
+ else:
+ commands = []
+ return commands, requests
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+
+ :param want: the objects from which the configuration should be removed
+ :param obj_in_have: the current configuration as a dictionary
+ :param interface_type: interface type
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ # if want is none, then delete all the vrfs
+ if not want:
+ commands = have
+ self.delete_all_flag = True
+ else:
+ commands = want
+ self.delete_all_flag = False
+
+ requests = []
+ if commands:
+ requests = self.get_delete_vrf_interface_requests(commands, have, want)
+
+ if len(requests) > 0:
+ commands = update_states(commands, "deleted")
+ else:
+ commands = []
+
+ return commands, requests
+
+ def get_delete_vrf_interface_requests(self, configs, have, want):
+ requests = []
+ if not configs:
+ return requests
+
+ # Create URL and payload
+ method = DELETE
+ for conf in configs:
+ name = conf['name']
+ empty_flag = False
+ members = conf.get('members', None)
+ if members:
+ interfaces = members.get('interfaces', None)
+ if members is None:
+ empty_flag = True
+ elif members is not None and interfaces is None:
+ empty_flag = True
+ matched = next((have_cfg for have_cfg in have if have_cfg['name'] == name), None)
+ if not matched:
+ continue
+
+ # if members are not mentioned delet the vrf name
+ if (self._module.params['state'] == 'deleted' and self.delete_all_flag) or empty_flag:
+ url = 'data/openconfig-network-instance:network-instances/network-instance={0}'.format(name)
+ request = {"path": url, "method": method}
+ requests.append(request)
+ else:
+ matched_members = matched.get('members', None)
+
+ if matched_members:
+ matched_intf = matched_members.get('interfaces', None)
+ if matched_intf:
+ for del_mem in matched_intf:
+ url = 'data/openconfig-network-instance:network-instances/'
+ url = url + 'network-instance={0}/interfaces/interface={1}'.format(name, del_mem['name'])
+ request = {"path": url, "method": method}
+ requests.append(request)
+
+ return requests
+
+ def get_create_requests(self, configs, have):
+ requests = []
+ if not configs:
+ return requests
+
+ requests_vrf = self.get_create_vrf_requests(configs, have)
+ if requests_vrf:
+ requests.extend(requests_vrf)
+
+ requests_vrf_intf = self.get_create_vrf_interface_requests(configs, have)
+ if requests_vrf_intf:
+ requests.extend(requests_vrf_intf)
+ return requests
+
+ def get_create_vrf_requests(self, configs, have):
+ requests = []
+ if not configs:
+ return requests
+ # Create URL and payload
+ method = PATCH
+ for conf in configs:
+ if conf.get("name", None):
+ name = conf["name"]
+ matched = next((have_cfg for have_cfg in have if have_cfg['name'] == name), None)
+ if not matched:
+ url = 'data/openconfig-network-instance:network-instances'
+ payload = self.build_create_vrf_payload(conf)
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+ return requests
+
+ def get_create_vrf_interface_requests(self, configs, have):
+ requests = []
+ if not configs:
+ return requests
+
+ # Create URL and payload
+ method = PATCH
+ for conf in configs:
+ if conf.get("members", None):
+ if conf["members"].get("interfaces", None):
+ url = 'data/openconfig-network-instance:network-instances/network-instance={0}/interfaces/interface'.format(conf["name"])
+ payload = self.build_create_vrf_interface_payload(conf)
+ if payload:
+ request = {"path": url, "method": method, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def build_create_vrf_payload(self, conf):
+ name = conf['name']
+
+ netw_inst = dict({'name': name})
+ netw_inst['config'] = dict({'name': name})
+ netw_inst['config'].update({'enabled': True})
+ netw_inst['config'].update({'type': 'L3VRF'})
+ netw_inst_arr = [netw_inst]
+
+ return dict({'openconfig-network-instance:network-instances': {'network-instance': netw_inst_arr}})
+
+ def build_create_vrf_interface_payload(self, conf):
+ members = conf["members"].get("interfaces", None)
+ network_inst_payload = dict()
+ if members:
+ network_inst_payload.update({"openconfig-network-instance:interface": []})
+ for member in members:
+ if member["name"]:
+ member_config_payload = dict({"id": member["name"]})
+ member_payload = dict({"id": member["name"], "config": member_config_payload})
+ network_inst_payload["openconfig-network-instance:interface"].append(member_payload)
+
+ return network_inst_payload
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py
new file mode 100644
index 00000000..d44adced
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/config/vxlans/vxlans.py
@@ -0,0 +1,606 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic_vxlans 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+ update_states
+)
+from ansible.module_utils.connection import ConnectionError
+
+PATCH = 'patch'
+DELETE = 'delete'
+test_keys = [
+ {'vlan_map': {'vlan': '', 'vni': ''}},
+ {'vrf_map': {'vni': '', 'vrf': ''}},
+]
+
+
+class Vxlans(ConfigBase):
+ """
+ The sonic_vxlans class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'vxlans',
+ ]
+
+ def __init__(self, module):
+ super(Vxlans, self).__init__(module)
+
+ def get_vxlans_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)
+ vxlans_facts = facts['ansible_network_resources'].get('vxlans')
+ if not vxlans_facts:
+ return []
+ return vxlans_facts
+
+ def execute_module(self):
+ """ Execute the module
+
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ warnings = list()
+
+ existing_vxlans_facts = self.get_vxlans_facts()
+ commands, requests = self.set_config(existing_vxlans_facts)
+
+ if commands and requests:
+ if not self._module.check_mode:
+ try:
+ edit_config(self._module, to_request(self._module, requests))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_vxlans_facts = self.get_vxlans_facts()
+
+ result['before'] = existing_vxlans_facts
+ if result['changed']:
+ result['after'] = changed_vxlans_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_vxlans_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_vxlans_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']
+
+ diff = get_diff(want, have, test_keys)
+
+ if state == 'overridden':
+ commands, requests = self._state_overridden(want, have, diff)
+ elif state == 'deleted':
+ commands, requests = self._state_deleted(want, have, diff)
+ elif state == 'merged':
+ commands, requests = self._state_merged(want, have, diff)
+ elif state == 'replaced':
+ commands, requests = self._state_replaced(want, have, diff)
+
+ return commands, requests
+
+ def _state_replaced(self, want, have, diff):
+ """ The command generator when state is replaced
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+
+ requests = []
+ commands = []
+
+ commands_del = get_diff(have, want, test_keys)
+ requests_del = []
+ if commands_del:
+ requests_del = self.get_delete_vxlan_request(commands_del, have)
+ if requests_del:
+ requests.extend(requests_del)
+ commands_del = update_states(commands_del, "deleted")
+ commands.extend(commands_del)
+
+ commands_rep = diff
+ requests_rep = []
+ if commands_rep:
+ requests_rep = self.get_create_vxlans_request(commands_rep, have)
+ if requests_rep:
+ requests.extend(requests_rep)
+ commands_rep = update_states(commands_rep, "replaced")
+ commands.extend(commands_rep)
+
+ return commands, requests
+
+ def _state_overridden(self, want, have, diff):
+ """ The command generator when state is overridden
+
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ requests = []
+ commands = []
+
+ commands_del = get_diff(have, want)
+ requests_del = []
+ if commands_del:
+ requests_del = self.get_delete_vxlan_request(commands_del, have)
+ if requests_del:
+ requests.extend(requests_del)
+ commands_del = update_states(commands_del, "deleted")
+ commands.extend(commands_del)
+
+ commands_over = diff
+ requests_over = []
+ if commands_over:
+ requests_over = self.get_create_vxlans_request(commands_over, have)
+ if requests_over:
+ requests.extend(requests_over)
+ commands_over = update_states(commands_over, "overridden")
+ commands.extend(commands_over)
+
+ return commands, requests
+
+ def _state_merged(self, want, have, diff):
+ """ The command generator when state is merged
+
+ :rtype: A list
+ :returns: the commands necessary to merge the provided into
+ the current configuration at position-0
+ Requests necessary to merge to the current configuration
+ at position-1
+ """
+ commands = diff
+ requests = self.get_create_vxlans_request(commands, have)
+
+ if len(requests) == 0:
+ commands = []
+
+ if commands:
+ commands = update_states(commands, "merged")
+
+ return commands, requests
+
+ def _state_deleted(self, want, have, diff):
+ """ The command generator when state is deleted
+
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+
+ requests = []
+ is_delete_all = False
+ # if want is none, then delete all the vxlans
+ if not want or len(have) == 0:
+ commands = have
+ is_delete_all = True
+ else:
+ commands = want
+
+ if is_delete_all:
+ requests = self.get_delete_all_vxlan_request(have)
+ else:
+ requests = self.get_delete_vxlan_request(commands, have)
+
+ if len(requests) == 0:
+ commands = []
+
+ if commands:
+ commands = update_states(commands, "deleted")
+
+ return commands, requests
+
+ def get_create_vxlans_request(self, configs, have):
+ requests = []
+
+ if not configs:
+ return requests
+
+ tunnel_requests = self.get_create_tunnel_request(configs, have)
+ vlan_map_requests = self.get_create_vlan_map_request(configs, have)
+ vrf_map_requests = self.get_create_vrf_map_request(configs, have)
+
+ if tunnel_requests:
+ requests.extend(tunnel_requests)
+ if vlan_map_requests:
+ requests.extend(vlan_map_requests)
+ if vrf_map_requests:
+ requests.extend(vrf_map_requests)
+
+ return requests
+
+ def get_delete_all_vxlan_request(self, have):
+ requests = []
+
+ vrf_map_requests = []
+ vlan_map_requests = []
+ src_ip_requests = []
+ primary_ip_requests = []
+ tunnel_requests = []
+
+ # Need to delete in reverse order of creation.
+ # vrf_map needs to be cleared before vlan_map
+ # vlan_map needs to be cleared before tunnel(source-ip)
+ for conf in have:
+ name = conf['name']
+ vlan_map_list = conf.get('vlan_map', [])
+ vrf_map_list = conf.get('vrf_map', [])
+ src_ip = conf.get('source_ip', None)
+ primary_ip = conf.get('primary_ip', None)
+
+ if vrf_map_list:
+ vrf_map_requests.extend(self.get_delete_vrf_map_request(conf, conf, name, vrf_map_list))
+ if vlan_map_list:
+ vlan_map_requests.extend(self.get_delete_vlan_map_request(conf, conf, name, vlan_map_list))
+ if src_ip:
+ src_ip_requests.extend(self.get_delete_src_ip_request(conf, conf, name, src_ip))
+ if primary_ip:
+ primary_ip_requests.extend(self.get_delete_primary_ip_request(conf, conf, name, primary_ip))
+ tunnel_requests.extend(self.get_delete_tunnel_request(conf, conf, name))
+
+ if vrf_map_requests:
+ requests.extend(vrf_map_requests)
+ if vlan_map_requests:
+ requests.extend(vlan_map_requests)
+ if src_ip_requests:
+ requests.extend(src_ip_requests)
+ if primary_ip_requests:
+ requests.extend(primary_ip_requests)
+ if tunnel_requests:
+ requests.extend(tunnel_requests)
+
+ return requests
+
+ def get_delete_vxlan_request(self, configs, have):
+ requests = []
+
+ if not configs:
+ return requests
+
+ vrf_map_requests = []
+ vlan_map_requests = []
+ src_ip_requests = []
+ primary_ip_requests = []
+ tunnel_requests = []
+
+ # Need to delete in the reverse order of creation.
+ # vrf_map needs to be cleared before vlan_map
+ # vlan_map needs to be cleared before tunnel(source-ip)
+ for conf in configs:
+
+ name = conf['name']
+ src_ip = conf.get('source_ip', None)
+ primary_ip = conf.get('primary_ip', None)
+ vlan_map_list = conf.get('vlan_map', None)
+ vrf_map_list = conf.get('vrf_map', None)
+
+ have_vlan_map_count = 0
+ have_vrf_map_count = 0
+ matched = next((each_vxlan for each_vxlan in have if each_vxlan['name'] == name), None)
+ if matched:
+ have_vlan_map = matched.get('vlan_map', [])
+ have_vrf_map = matched.get('vrf_map', [])
+ if have_vlan_map:
+ have_vlan_map_count = len(have_vlan_map)
+ if have_vrf_map:
+ have_vrf_map_count = len(have_vrf_map)
+
+ is_delete_full = False
+ if (name and vlan_map_list is None and vrf_map_list is None and
+ src_ip is None and primary_ip is None):
+ is_delete_full = True
+ vrf_map_list = matched.get("vrf_map", [])
+ vlan_map_list = matched.get("vlan_map", [])
+
+ if vlan_map_list is not None and len(vlan_map_list) == 0 and matched:
+ vlan_map_list = matched.get("vlan_map", [])
+ if vrf_map_list is not None and len(vrf_map_list) == 0 and matched:
+ vrf_map_list = matched.get("vrf_map", [])
+
+ if vrf_map_list:
+ temp_vrf_map_requests = self.get_delete_vrf_map_request(conf, matched, name, vrf_map_list)
+ if temp_vrf_map_requests:
+ vrf_map_requests.extend(temp_vrf_map_requests)
+ have_vrf_map_count -= len(temp_vrf_map_requests)
+ if vlan_map_list:
+ temp_vlan_map_requests = self.get_delete_vlan_map_request(conf, matched, name, vlan_map_list)
+ if temp_vlan_map_requests:
+ vlan_map_requests.extend(temp_vlan_map_requests)
+ have_vlan_map_count -= len(temp_vlan_map_requests)
+ if src_ip:
+ src_ip_requests.extend(self.get_delete_src_ip_request(conf, matched, name, src_ip))
+
+ if primary_ip:
+ primary_ip_requests.extend(self.get_delete_primary_ip_request(conf, matched, name, primary_ip))
+ if is_delete_full:
+ tunnel_requests.extend(self.get_delete_tunnel_request(conf, matched, name))
+
+ if vrf_map_requests:
+ requests.extend(vrf_map_requests)
+ if vlan_map_requests:
+ requests.extend(vlan_map_requests)
+ if src_ip_requests:
+ requests.extend(src_ip_requests)
+ if primary_ip_requests:
+ requests.extend(primary_ip_requests)
+ if tunnel_requests:
+ requests.extend(tunnel_requests)
+
+ return requests
+
+ def get_create_evpn_request(self, conf):
+ # Create URL and payload
+ url = "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST"
+ payload = self.build_create_evpn_payload(conf)
+ request = {"path": url, "method": PATCH, "data": payload}
+
+ return request
+
+ def get_create_tunnel_request(self, configs, have):
+ # Create URL and payload
+ requests = []
+ url = "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL"
+ for conf in configs:
+ payload = self.build_create_tunnel_payload(conf)
+ request = {"path": url, "method": PATCH, "data": payload}
+ requests.append(request)
+ if conf.get('source_ip', None):
+ requests.append(self.get_create_evpn_request(conf))
+
+ return requests
+
+ def build_create_evpn_payload(self, conf):
+
+ evpn_nvo_list = [{'name': conf['evpn_nvo'], 'source_vtep': conf['name']}]
+ evpn_dict = {'sonic-vxlan:EVPN_NVO_LIST': evpn_nvo_list}
+
+ return evpn_dict
+
+ def build_create_tunnel_payload(self, conf):
+ payload_url = dict()
+
+ vtep_ip_dict = dict()
+ vtep_ip_dict['name'] = conf['name']
+ if conf.get('source_ip', None):
+ vtep_ip_dict['src_ip'] = conf['source_ip']
+ if conf.get('primary_ip', None):
+ vtep_ip_dict['primary_ip'] = conf['primary_ip']
+
+ payload_url['sonic-vxlan:VXLAN_TUNNEL'] = {'VXLAN_TUNNEL_LIST': [vtep_ip_dict]}
+
+ return payload_url
+
+ def get_create_vlan_map_request(self, configs, have):
+ # Create URL and payload
+ requests = []
+ for conf in configs:
+ new_vlan_map_list = conf.get('vlan_map', [])
+ if new_vlan_map_list:
+ for each_vlan_map in new_vlan_map_list:
+ name = conf['name']
+ vlan = each_vlan_map.get('vlan')
+ vni = each_vlan_map.get('vni')
+ matched = next((each_vxlan for each_vxlan in have if each_vxlan['name'] == name), None)
+
+ is_change_needed = True
+ if matched:
+ matched_vlan_map_list = matched.get('vlan_map', [])
+ if matched_vlan_map_list:
+ matched_vlan_map = next((e_vlan_map for e_vlan_map in matched_vlan_map_list if e_vlan_map['vni'] == vni), None)
+ if matched_vlan_map:
+ if matched_vlan_map['vlan'] == vlan:
+ is_change_needed = False
+
+ if is_change_needed:
+ map_name = "map_{0}_Vlan{1}".format(vni, vlan)
+ payload = self.build_create_vlan_map_payload(conf, each_vlan_map)
+ url = "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP"
+ request = {"path": url, "method": PATCH, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def build_create_vlan_map_payload(self, conf, vlan_map):
+ payload_url = dict()
+
+ vlan_map_dict = dict()
+ vlan_map_dict['name'] = conf['name']
+ vlan_map_dict['mapname'] = "map_{vni}_Vlan{vlan}".format(vni=vlan_map['vni'], vlan=vlan_map['vlan'])
+ vlan_map_dict['vlan'] = "Vlan{vlan}".format(vlan=vlan_map['vlan'])
+ vlan_map_dict['vni'] = vlan_map['vni']
+
+ payload_url['sonic-vxlan:VXLAN_TUNNEL_MAP'] = {'VXLAN_TUNNEL_MAP_LIST': [vlan_map_dict]}
+
+ return payload_url
+
+ def get_create_vrf_map_request(self, configs, have):
+ # Create URL and payload
+ requests = []
+ for conf in configs:
+ new_vrf_map_list = conf.get('vrf_map', [])
+ if new_vrf_map_list:
+ for each_vrf_map in new_vrf_map_list:
+ name = conf['name']
+ vrf = each_vrf_map.get('vrf')
+ vni = each_vrf_map.get('vni')
+ matched = next((each_vxlan for each_vxlan in have if each_vxlan['name'] == name), None)
+
+ is_change_needed = True
+ if matched:
+ matched_vrf_map_list = matched.get('vrf_map', [])
+ if matched_vrf_map_list:
+ matched_vrf_map = next((e_vrf_map for e_vrf_map in matched_vrf_map_list if e_vrf_map['vni'] == vni), None)
+ if matched_vrf_map:
+ if matched_vrf_map['vrf'] == vrf:
+ is_change_needed = False
+
+ if is_change_needed:
+ payload = self.build_create_vrf_map_payload(conf, each_vrf_map)
+ url = "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST={vrf}/vni".format(vrf=vrf)
+ request = {"path": url, "method": PATCH, "data": payload}
+ requests.append(request)
+
+ return requests
+
+ def build_create_vrf_map_payload(self, conf, vrf_map):
+
+ payload_url = dict({"sonic-vrf:vni": vrf_map['vni']})
+ return payload_url
+
+ def get_delete_evpn_request(self, conf):
+ # Create URL and payload
+ url = "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST={evpn_nvo}".format(evpn_nvo=conf['evpn_nvo'])
+ request = {"path": url, "method": DELETE}
+
+ return request
+
+ def get_delete_tunnel_request(self, conf, matched, name):
+ # Create URL and payload
+ requests = []
+
+ url = "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST={name}".format(name=name)
+ requests.append({"path": url, "method": DELETE})
+
+ return requests
+
+ def get_delete_src_ip_request(self, conf, matched, name, del_source_ip):
+ # Create URL and payload
+ requests = []
+
+ url = "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST={name}/src_ip"
+
+ is_change_needed = False
+ if matched:
+ matched_source_ip = matched.get('source_ip', None)
+ if matched_source_ip and matched_source_ip == del_source_ip:
+ is_change_needed = True
+
+ # Delete the EVPN NVO if the source_ip address is being deleted.
+ if is_change_needed:
+ requests.append(self.get_delete_evpn_request(conf))
+ request = {"path": url.format(name=name), "method": DELETE}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_primary_ip_request(self, conf, matched, name, del_primary_ip):
+ # Create URL and payload
+ requests = []
+
+ url = "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL/VXLAN_TUNNEL_LIST={name}/primary_ip"
+
+ is_change_needed = False
+ if matched:
+ matched_primary_ip = matched.get('primary_ip', None)
+ if matched_primary_ip and matched_primary_ip == del_primary_ip:
+ is_change_needed = True
+
+ if is_change_needed:
+ request = {"path": url.format(name=name), "method": DELETE}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_vlan_map_request(self, conf, matched, name, del_vlan_map_list):
+ # Create URL and payload
+ requests = []
+
+ for each_vlan_map in del_vlan_map_list:
+ vlan = each_vlan_map.get('vlan')
+ vni = each_vlan_map.get('vni')
+
+ is_change_needed = False
+ if matched:
+ matched_vlan_map_list = matched.get('vlan_map', None)
+ if matched_vlan_map_list:
+ matched_vlan_map = next((e_vlan_map for e_vlan_map in matched_vlan_map_list if e_vlan_map['vni'] == vni), None)
+ if matched_vlan_map:
+ if matched_vlan_map['vlan'] == vlan:
+ is_change_needed = True
+
+ if is_change_needed:
+ map_name = "map_{0}_Vlan{1}".format(vni, vlan)
+ url = "data/sonic-vxlan:sonic-vxlan/VXLAN_TUNNEL_MAP/VXLAN_TUNNEL_MAP_LIST={name},{map_name}".format(name=name, map_name=map_name)
+ request = {"path": url, "method": DELETE}
+ requests.append(request)
+
+ return requests
+
+ def get_delete_vrf_map_request(self, conf, matched, name, del_vrf_map_list):
+ # Create URL and payload
+ requests = []
+
+ for each_vrf_map in del_vrf_map_list:
+ vrf = each_vrf_map.get('vrf')
+ vni = each_vrf_map.get('vni')
+
+ is_change_needed = False
+ if matched:
+ matched_vrf_map_list = matched.get('vrf_map', None)
+ if matched_vrf_map_list:
+ matched_vrf_map = next((e_vrf_map for e_vrf_map in matched_vrf_map_list if e_vrf_map['vni'] == vni), None)
+ if matched_vrf_map:
+ if matched_vrf_map['vrf'] == vrf:
+ is_change_needed = True
+
+ if is_change_needed:
+ url = "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST={vrf}/vni".format(vrf=vrf)
+ request = {"path": url, "method": DELETE}
+ requests.append(request)
+
+ return requests
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py
new file mode 100644
index 00000000..5a7bd05c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/aaa/aaa.py
@@ -0,0 +1,111 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic aaa 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.aaa.aaa import AaaArgs
+
+GET = "get"
+
+
+class AaaFacts(object):
+ """ The sonic aaa fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = AaaArgs.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_aaa(self):
+ """Get aaa details available in chassis"""
+ request = [{"path": "data/openconfig-system:system/aaa", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ data = {}
+ if ('openconfig-system:aaa' in response[0][1]):
+ if ('authentication' in response[0][1]['openconfig-system:aaa']):
+ if ('config' in response[0][1]['openconfig-system:aaa']['authentication']):
+ data = response[0][1]['openconfig-system:aaa']['authentication']['config']
+ return data
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for aaa
+ :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_aaa()
+ objs = []
+ objs = self.render_config(self.generated_spec, data)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['aaa'] = 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 = self.parse_sonic_aaa(spec, conf)
+ return config
+
+ def parse_sonic_aaa(self, spec, conf):
+ config = deepcopy(spec)
+ if conf:
+ temp = {}
+ if ('authentication-method' in conf) and (conf['authentication-method']):
+ if 'local' in conf['authentication-method']:
+ temp['local'] = True
+ choices = ['tacacs+', 'ldap', 'radius']
+ for i, word in enumerate(conf['authentication-method']):
+ if word in choices:
+ temp['group'] = conf['authentication-method'][i]
+ if ('failthrough' in conf):
+ temp['fail_through'] = conf['failthrough']
+ if temp:
+ config['authentication']['data'] = temp
+ return utils.remove_empties(config)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py
new file mode 100644
index 00000000..c86b53c2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp/bgp.py
@@ -0,0 +1,156 @@
+#
+# -*- 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 sonic bgp 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ remove_empties_from_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp.bgp import BgpArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ get_bgp_data,
+)
+
+
+class BgpFacts(object):
+ """ The sonic bgp fact class
+ """
+
+ global_params_map = {
+ 'bgp_as': 'as',
+ 'router_id': 'router-id',
+ 'holdtime': 'hold-time',
+ 'keepalive_interval': 'keepalive-interval',
+ 'log_neighbor_changes': ['logging-options', 'log-neighbor-state-changes'],
+ 'as_path_confed': ['route-selection-options', 'compare-confed-as-path'],
+ 'as_path_ignore': ['route-selection-options', 'ignore-as-path-length'],
+ 'as_path_multipath_relax': ['use-multiple-paths', 'ebgp', 'config', 'allow-multiple-as'],
+ 'as_path_multipath_relax_as_set': ['use-multiple-paths', 'ebgp', 'config', 'as-set'],
+ 'compare_routerid': ['route-selection-options', 'external-compare-router-id'],
+ 'med_confed': ['route-selection-options', 'med-confed'],
+ 'med_missing_as_worst': ['route-selection-options', 'med-missing-as-worst'],
+ 'always_compare_med': ['route-selection-options', 'always-compare-med'],
+ 'admin_max_med': ['max-med', 'admin-max-med-val'],
+ 'max_med_on_startup_timer': ['max-med', 'time'],
+ 'max_med_on_startup_med_val': ['max-med', 'max-med-val'],
+ }
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = BgpArgs.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 BGP
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = list()
+ if connection: # just for linting purposes, remove
+ pass
+
+ if not data:
+ data = get_bgp_data(self._module, self.global_params_map)
+ self.normalise_bgp_data(data)
+
+ # operate on a collection of resource x
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ # split the config into instances of the resource
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('bgp', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)})
+ facts['bgp'] = params['config']
+ ansible_facts['ansible_network_resources'].update(facts)
+ return ansible_facts
+
+ def normalise_bgp_data(self, data):
+ for conf in data:
+ bestpath = {}
+ med = {}
+ timers = {}
+ as_path = {}
+ max_med_on_start_up = {}
+
+ conf['log_neighbor_changes'] = conf.get('log_neighbor_changes', False)
+
+ as_path['confed'] = conf.get('as_path_confed', False)
+ as_path['ignore'] = conf.get('as_path_ignore', False)
+ as_path['multipath_relax'] = conf.get('as_path_multipath_relax', False)
+ as_path['multipath_relax_as_set'] = conf.get('as_path_multipath_relax_as_set', False)
+ bestpath['as_path'] = as_path
+
+ med['confed'] = conf.get('med_confed', False)
+ med['missing_as_worst'] = conf.get('med_missing_as_worst', False)
+ med['always_compare_med'] = conf.get('always_compare_med', False)
+ bestpath['med'] = med
+
+ timers['holdtime'] = conf.get('holdtime', None)
+ timers['keepalive_interval'] = conf.get('keepalive_interval', None)
+ conf['timers'] = timers
+ bestpath['compare_routerid'] = conf.get('compare_routerid', False)
+
+ conf['bestpath'] = bestpath
+
+ max_med_on_start_up["timer"] = conf.get('max_med_on_startup_timer', None)
+ max_med_on_start_up["med_val"] = conf.get('max_med_on_startup_med_val', None)
+
+ conf['max_med'] = {
+ 'on_startup': max_med_on_start_up,
+ }
+
+ keys = [
+ 'as_path_confed', 'as_path_ignore', 'as_path_multipath_relax', 'as_path_multipath_relax_as_set',
+ 'med_confed', 'med_missing_as_worst', 'always_compare_med', 'max_med_val', 'holdtime',
+ 'keepalive_interval', 'compare_routerid', 'admin_max_med', 'max_med_on_startup_timer',
+ 'max_med_on_startup_med_val',
+ ]
+ for key in keys:
+ if key in conf:
+ conf.pop(key)
+
+ 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
+ """
+
+ return conf
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py
new file mode 100644
index 00000000..fd37533e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_af/bgp_af.py
@@ -0,0 +1,258 @@
+#
+# -*- 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 sonic bgp_af 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ remove_empties_from_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_af.bgp_af import Bgp_afArgs
+
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ get_bgp_af_data,
+ get_all_bgp_af_redistribute,
+)
+
+
+class Bgp_afFacts(object):
+ """ The sonic bgp_af fact class
+ """
+
+ afi_safi_types_map = {
+ 'openconfig-bgp-types:IPV4_UNICAST': 'ipv4_unicast',
+ 'openconfig-bgp-types:IPV6_UNICAST': 'ipv6_unicast',
+ 'openconfig-bgp-types:L2VPN_EVPN': 'l2vpn_evpn',
+ }
+
+ af_params_map = {
+ 'afi': 'afi-safi-name',
+ 'route_map': 'policy-name',
+ 'prefix': 'prefix',
+ 'neighbor': 'neighbor-address',
+ 'route_reflector_client': 'route-reflector-client',
+ 'route_server_client': 'route-server-client',
+ 'next_hop_self': ['next-hop-self', 'enabled'],
+ 'remove_private_as': ['remove-private-as', 'enabled'],
+ 'prefix_list_in': ['prefix-list', 'import-policy'],
+ 'prefix_list_out': ['prefix-list', 'export-policy'],
+ 'maximum_prefix': ['prefix-limit', 'max-prefixes'],
+ 'activate': 'enabled',
+ 'advertise_pip': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'advertise-pip'],
+ 'advertise_pip_ip': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'advertise-pip-ip'],
+ 'advertise_pip_peer_ip': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'advertise-pip-peer-ip'],
+ 'advertise_svi_ip': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'advertise-svi-ip'],
+ 'advertise_all_vni': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'advertise-all-vni'],
+ 'advertise_default_gw': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:config', 'advertise-default-gw'],
+ 'ebgp': ['use-multiple-paths', 'ebgp', 'maximum-paths'],
+ 'ibgp': ['use-multiple-paths', 'ibgp', 'maximum-paths'],
+ 'network': ['network-config', 'network'],
+ 'dampening': ['route-flap-damping', 'config', 'enabled'],
+ 'route_advertise_list': ['l2vpn-evpn', 'openconfig-bgp-evpn-ext:route-advertise', 'route-advertise-list'],
+ }
+
+ af_redis_params_map = {
+ 'protocol': 'src-protocol',
+ 'afi': 'address-family',
+ 'metric': 'metric',
+ 'route_map': 'import-policy'
+ }
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Bgp_afArgs.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 BGP
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = list()
+ if connection: # just for linting purposes, remove
+ pass
+ if not data:
+ data = get_bgp_af_data(self._module, self.af_params_map)
+ vrf_list = [e_bgp_af['vrf_name'] for e_bgp_af in data]
+ self.update_max_paths(data)
+ self.update_network(data)
+ self.update_route_advertise_list(data)
+ bgp_redis_data = get_all_bgp_af_redistribute(self._module, vrf_list, self.af_redis_params_map)
+ self.update_redis_data(data, bgp_redis_data)
+ self.update_afis(data)
+
+ # operate on a collection of resource x
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ # split the config into instances of the resource
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('bgp_af', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)})
+ facts['bgp_af'] = 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
+ """
+
+ return conf
+
+ def check_afi(self, afi, redis_data):
+ afi_rhs = afi
+ afi_lhs = redis_data.get('afi', None)
+ return (afi_lhs and (afi_rhs == afi_lhs))
+
+ def update_redis_data(self, objs, af_redis_data):
+ if not (af_redis_data or objs):
+ return
+
+ for conf in objs:
+ vrf_name = conf['vrf_name']
+ raw_af_redis_data = next((e_af_redis for e_af_redis in af_redis_data if vrf_name in e_af_redis), None)
+ if not raw_af_redis_data:
+ continue
+ norm_af_redis_data = self.normalize_af_redis_params(raw_af_redis_data[vrf_name])
+ if norm_af_redis_data:
+ if 'address_family' in conf:
+ afs = conf['address_family']
+ if not afs:
+ continue
+ for e_af in afs:
+ if 'afi' in e_af:
+ afi = e_af['afi']
+ redis_arr = []
+ for e_redis_data in norm_af_redis_data:
+ if self.check_afi(afi, e_redis_data):
+ e_redis_data.pop('afi')
+ redis_arr.append(e_redis_data)
+ e_af.update({'redistribute': redis_arr})
+ else:
+ addr_fams = []
+ for e_norm_af_redis in norm_af_redis_data:
+ afi = e_norm_af_redis['afi']
+ e_norm_af_redis.pop('afi')
+ mat_addr_fam = next((each_addr_fam for each_addr_fam in addr_fams if each_addr_fam['afi'] == afi), None)
+ if mat_addr_fam:
+ mat_addr_fam['redistribute'].append(e_norm_af_redis)
+ else:
+ addr_fams.append({'redistribute': [e_norm_af_redis], 'afi': afi})
+
+ if addr_fams:
+ conf.update({'address_family': addr_fams})
+
+ def update_max_paths(self, data):
+ for conf in data:
+ afs = conf.get('address_family', [])
+ if afs:
+ for af in afs:
+ max_path = {}
+ ebgp = af.get('ebgp', None)
+ if ebgp:
+ af.pop('ebgp')
+ max_path['ebgp'] = ebgp
+ ibgp = af.get('ibgp', None)
+ if ibgp:
+ af.pop('ibgp')
+ max_path['ibgp'] = ibgp
+ if max_path:
+ af['max_path'] = max_path
+
+ def update_network(self, data):
+ for conf in data:
+ afs = conf.get('address_family', [])
+ if afs:
+ for af in afs:
+ temp = []
+ network = af.get('network', None)
+ if network:
+ for e in network:
+ prefix = e.get('prefix', None)
+ if prefix:
+ temp.append(prefix)
+ af['network'] = temp
+ dampening = af.get('dampening', None)
+ if dampening:
+ af.pop('dampening')
+ af['dampening'] = dampening
+
+ def update_afis(self, data):
+ for conf in data:
+ if 'address_family' in conf:
+ conf['address_family'] = {'afis': conf['address_family']}
+
+ def update_route_advertise_list(self, data):
+ for conf in data:
+ afs = conf.get('address_family', [])
+ if afs:
+ for af in afs:
+ rt_adv_lst = []
+ route_advertise_list = af.get('route_advertise_list', None)
+ if route_advertise_list:
+ for rt in route_advertise_list:
+ rt_adv_dict = {}
+ advertise_afi = rt['advertise-afi-safi'].split(':')[1].split('_')[0].lower()
+ route_map_config = rt['config']
+ route_map = route_map_config.get('route-map', None)
+ if advertise_afi:
+ rt_adv_dict['advertise_afi'] = advertise_afi
+ if route_map:
+ rt_adv_dict['route_map'] = route_map[0]
+ if rt_adv_dict and rt_adv_dict not in rt_adv_lst:
+ rt_adv_lst.append(rt_adv_dict)
+ af['route_advertise_list'] = rt_adv_lst
+
+ def normalize_af_redis_params(self, af):
+ norm_af = list()
+ for e_af in af:
+ temp = e_af.copy()
+ for key, val in e_af.items():
+ if 'afi' == key or 'protocol' == key and val:
+ if ':' in val:
+ temp[key] = val.split(':')[1].lower()
+ if '_' in val:
+ temp[key] = val.split('_')[1].lower()
+ elif 'route_map' == key and val:
+ temp['route_map'] = val[0]
+
+ norm_af.append(temp)
+ return norm_af
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py
new file mode 100644
index 00000000..822db22a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_as_paths/bgp_as_paths.py
@@ -0,0 +1,129 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic bgp_as_paths 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_as_paths.bgp_as_paths import Bgp_as_pathsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+
+class Bgp_as_pathsFacts(object):
+ """ The sonic bgp_as_paths fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Bgp_as_pathsArgs.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_as_path_list(self):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets"
+ method = "GET"
+ request = [{"path": url, "method": method}]
+
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ as_path_lists = []
+ if "openconfig-bgp-policy:as-path-sets" in response[0][1]:
+ temp = response[0][1].get("openconfig-bgp-policy:as-path-sets", {})
+ if "as-path-set" in temp:
+ as_path_lists = temp["as-path-set"]
+
+ as_path_list_configs = []
+ for as_path in as_path_lists:
+ result = dict()
+ as_name = as_path["as-path-set-name"]
+ member_config = as_path['config']
+ members = member_config.get("as-path-set-member", [])
+ permit_str = member_config.get("openconfig-bgp-policy-ext:action", None)
+ result['name'] = as_name
+ result['members'] = members
+ if permit_str and permit_str == "PERMIT":
+ result['permit'] = True
+ else:
+ result['permit'] = False
+ as_path_list_configs.append(result)
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('as_path_list: ' + str(as_path_list_configs) + '\n')
+ return as_path_list_configs
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for as_path_list
+ :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
+
+ if not data:
+ resources = self.get_as_path_list()
+
+ 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('bgp_as_paths', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['bgp_as_paths'] = 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)
+ try:
+ config['name'] = str(conf['name'])
+ config['members'] = conf['members']
+ config['permit'] = conf['permit']
+ except TypeError:
+ config['name'] = None
+ config['members'] = None
+ config['permit'] = None
+ return utils.remove_empties(config)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py
new file mode 100644
index 00000000..ffa29422
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py
@@ -0,0 +1,145 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic bgp_communities 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_communities.bgp_communities import Bgp_communitiesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+
+class Bgp_communitiesFacts(object):
+ """ The sonic bgp_communities fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Bgp_communitiesArgs.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_bgp_communities(self):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets"
+ method = "GET"
+ request = [{"path": url, "method": method}]
+
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ bgp_communities = []
+ if "openconfig-bgp-policy:community-sets" in response[0][1]:
+ temp = response[0][1].get("openconfig-bgp-policy:community-sets", {})
+ if "community-set" in temp:
+ bgp_communities = temp["community-set"]
+
+ bgp_communities_configs = []
+ for bgp_community in bgp_communities:
+ result = dict()
+ name = bgp_community["community-set-name"]
+ member_config = bgp_community['config']
+ match = member_config['match-set-options']
+ permit_str = member_config.get('openconfig-bgp-policy-ext:action', None)
+ members = member_config.get("community-member", [])
+ result['name'] = name
+ result['match'] = match
+ if permit_str and permit_str == 'PERMIT':
+ result['permit'] = True
+ else:
+ result['permit'] = False
+ if members:
+ result['type'] = 'expanded' if 'REGEX' in members[0] else 'standard'
+ else:
+ result['type'] = ''
+ if result['type'] == 'expanded':
+ members = [':'.join(i.split(':')[1:]) for i in members]
+ result['local_as'] = True if "NO_EXPORT_SUBCONFED" in members else False
+ result['no_advertise'] = True if "NO_ADVERTISE" in members else False
+ result['no_export'] = True if "NO_EXPORT" in members else False
+ result['no_peer'] = True if "NOPEER" in members else False
+ result['members'] = {'regex': members}
+ bgp_communities_configs.append(result)
+ # with open('/root/ansible_log.log', 'a+') as fp:
+ # fp.write('bgp_communities: ' + str(bgp_communities_configs) + '\n')
+ return bgp_communities_configs
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for bgp_communities
+ :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
+
+ if not data:
+ resources = self.get_bgp_communities()
+
+ 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('bgp_communities', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['bgp_communities'] = 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)
+ try:
+ config['name'] = str(conf['name'])
+ config['members'] = conf['members']
+ config['match'] = conf['match']
+ config['type'] = conf['type']
+ config['permit'] = conf['permit']
+ except TypeError:
+ config['name'] = None
+ config['members'] = None
+ config['match'] = None
+ config['type'] = None
+ config['permit'] = None
+ return utils.remove_empties(config)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py
new file mode 100644
index 00000000..b1d7c4ad
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py
@@ -0,0 +1,158 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic bgp_ext_communities 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_ext_communities.bgp_ext_communities import (
+ Bgp_ext_communitiesArgs,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+
+class Bgp_ext_communitiesFacts(object):
+ """ The sonic bgp_ext_communities fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Bgp_ext_communitiesArgs.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_bgp_extcommunities(self):
+ url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets"
+ method = "GET"
+ request = [{"path": url, "method": method}]
+
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ bgp_extcommunities = []
+ if "openconfig-bgp-policy:ext-community-sets" in response[0][1]:
+ temp = response[0][1].get("openconfig-bgp-policy:ext-community-sets", {})
+ if "ext-community-set" in temp:
+ bgp_extcommunities = temp["ext-community-set"]
+
+ bgp_extcommunities_configs = []
+ for bgp_extcommunity in bgp_extcommunities:
+ result = dict()
+ name = bgp_extcommunity["ext-community-set-name"]
+ member_config = bgp_extcommunity['config']
+ match = member_config['match-set-options']
+ permit_str = member_config.get('openconfig-bgp-policy-ext:action', None)
+ members = member_config.get("ext-community-member", [])
+ result['name'] = name
+ result['match'] = match.lower()
+
+ if permit_str and permit_str == 'PERMIT':
+ result['permit'] = True
+ else:
+ result['permit'] = False
+
+ result['members'] = dict()
+ rt = list()
+ soo = list()
+ regex = list()
+ for member in members:
+ if member.startswith('route-target'):
+ rt.append(':'.join(member.split(':')[1:]))
+ elif member.startswith('route-origin'):
+ soo.append(':'.join(member.split(':')[1:]))
+ elif member.startswith('REGEX'):
+ regex.append(':'.join(member.split(':')[1:]))
+
+ result['type'] = 'standard'
+ if regex and len(regex) > 0:
+ result['type'] = 'expanded'
+ result['members']['regex'] = regex
+ if rt and len(rt) > 0:
+ result['members']['route_target'] = rt
+ if soo and len(soo) > 0:
+ result['members']['route_origin'] = soo
+
+ bgp_extcommunities_configs.append(result)
+
+ return bgp_extcommunities_configs
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for bgp_ext_communities
+ :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
+
+ if not data:
+ resources = self.get_bgp_extcommunities()
+
+ 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('bgp_ext_communities', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['bgp_ext_communities'] = 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)
+ try:
+ config['name'] = str(conf['name'])
+ config['members'] = conf['members']
+ config['match'] = conf['match']
+ config['type'] = conf['type']
+ config['permit'] = conf['permit']
+ except TypeError:
+ config['name'] = None
+ config['members'] = None
+ config['match'] = None
+ config['type'] = None
+ config['permit'] = None
+ return utils.remove_empties(config)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py
new file mode 100644
index 00000000..903b93de
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors/bgp_neighbors.py
@@ -0,0 +1,229 @@
+#
+# -*- 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 sonic bgp_neighbors 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ remove_empties_from_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_neighbors.bgp_neighbors import Bgp_neighborsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ get_all_bgp_neighbors,
+ get_from_params_map,
+ get_peergroups,
+)
+
+
+class Bgp_neighborsFacts(object):
+ """ The sonic bgp_neighbors fact class
+ """
+
+ neighbor_params_map = {
+ 'neighbor': 'neighbor-address',
+ 'peer_as': 'peer-as',
+ 'peer_type': 'peer-type',
+ 'peer_group': 'peer-group',
+ 'keepalive': 'keepalive-interval',
+ 'holdtime': 'hold-time',
+ 'connect_retry': 'connect-retry',
+ 'advertisement_interval': 'minimum-advertisement-interval',
+ 'bfd_enabled': ['enable-bfd', 'enabled'],
+ 'check_failure': ['enable-bfd', 'check-control-plane-failure'],
+ 'profile': ['enable-bfd', 'bfd-profile'],
+ 'dynamic': 'capability-dynamic',
+ 'extended_nexthop': 'capability-extended-nexthop',
+ 'pwd': ['auth-password', 'password'],
+ 'encrypted': ['auth-password', 'encrypted'],
+ 'nbr_description': 'description',
+ 'disable_connected_check': 'disable-ebgp-connected-route-check',
+ 'dont_negotiate_capability': 'dont-negotiate-capability',
+ 'enforce_first_as': 'enforce-first-as',
+ 'enforce_multihop': 'enforce-multihop',
+ 'local_address': ['transport', 'config', 'local-address'],
+ 'as': 'local-as',
+ 'no_prepend': 'local-as-no-prepend',
+ 'replace_as': 'local-as-replace-as',
+ 'override_capability': 'override-capability',
+ 'port': 'peer-port',
+ 'shutdown_msg': 'shutdown-message',
+ 'solo': 'solo-peer',
+ 'strict_capability_match': 'strict-capability-match',
+ 'ttl_security': 'ttl-security-hops',
+ 'enabled': ['ebgp-multihop', 'enabled'],
+ 'multihop_ttl': ['ebgp-multihop', 'multihop-ttl'],
+ 'v6only': 'openconfig-bgp-ext:v6only',
+ 'passive': ['transport', 'config', 'passive-mode']
+ }
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Bgp_neighborsArgs.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 BGP
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = list()
+
+ if not data:
+ data = get_all_bgp_neighbors(self._module)
+ filtered_data = self.filter_neighbors_data(data)
+ if filtered_data:
+ data = filtered_data
+
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('bgp_neighbors', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)})
+ facts['bgp_neighbors'] = 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
+ """
+
+ return conf
+
+ def filter_neighbors_data(self, data):
+ filtered_data = []
+ for conf in data:
+ vrf_name = conf['vrf_name']
+ tmp = {}
+ bgp_as = conf['bgp_as']
+ val = None
+ if 'neighbors' in conf and 'neighbor' in conf['neighbors']:
+ val = conf['neighbors']['neighbor']
+
+ tmp['vrf_name'] = vrf_name
+ tmp['bgp_as'] = bgp_as
+ peergroup = get_peergroups(self._module, vrf_name)
+ if peergroup:
+ tmp['peer_group'] = peergroup
+ fil_neighbors = []
+ if val:
+ for neighbor in val:
+ fil_neighbor = get_from_params_map(self.neighbor_params_map, neighbor)
+ capability = {}
+ capability_dynamic = fil_neighbor.get('dynamic', None)
+ if capability_dynamic is not None:
+ capability['dynamic'] = capability_dynamic
+ fil_neighbor.pop('dynamic')
+ capability_extended_nexthop = fil_neighbor.get('extended_nexthop', None)
+ if capability_extended_nexthop is not None:
+ capability['extended_nexthop'] = capability_extended_nexthop
+ fil_neighbor.pop('extended_nexthop')
+ if capability:
+ fil_neighbor['capability'] = capability
+ remote = {}
+ peer_as = fil_neighbor.get('peer_as', None)
+ if peer_as is not None:
+ remote['peer_as'] = peer_as
+ fil_neighbor.pop('peer_as')
+ peer_type = fil_neighbor.get('peer_type', None)
+ if peer_type is not None:
+ remote['peer_type'] = peer_type.lower()
+ fil_neighbor.pop('peer_type')
+ if remote:
+ fil_neighbor['remote_as'] = remote
+ auth_pwd = {}
+ pwd = fil_neighbor.get('pwd', None)
+ if pwd is not None:
+ auth_pwd['pwd'] = pwd
+ fil_neighbor.pop('pwd')
+ encrypted = fil_neighbor.get('encrypted', None)
+ if encrypted is not None:
+ auth_pwd['encrypted'] = encrypted
+ fil_neighbor.pop('encrypted')
+ ebgp_multihop = {}
+ enabled = fil_neighbor.get('enabled', None)
+ if enabled is not None:
+ ebgp_multihop['enabled'] = enabled
+ fil_neighbor.pop('enabled')
+ multihop_ttl = fil_neighbor.get('multihop_ttl', None)
+ if multihop_ttl is not None:
+ ebgp_multihop['multihop_ttl'] = multihop_ttl
+ fil_neighbor.pop('multihop_ttl')
+ local_as = {}
+ asn = fil_neighbor.get('as', None)
+ if asn is not None:
+ local_as['as'] = asn
+ fil_neighbor.pop('as')
+ no_prepend = fil_neighbor.get('no_prepend', None)
+ if no_prepend is not None:
+ local_as['no_prepend'] = no_prepend
+ fil_neighbor.pop('no_prepend')
+ replace_as = fil_neighbor.get('replace_as', None)
+ if replace_as is not None:
+ local_as['replace_as'] = replace_as
+ fil_neighbor.pop('replace_as')
+ bfd = {}
+ bfd_enabled = fil_neighbor.get('bfd_enabled', None)
+ if bfd_enabled is not None:
+ bfd['enabled'] = bfd_enabled
+ fil_neighbor.pop('bfd_enabled')
+ check_failure = fil_neighbor.get('check_failure', None)
+ if check_failure is not None:
+ bfd['check_failure'] = check_failure
+ fil_neighbor.pop('check_failure')
+ profile = fil_neighbor.get('profile', None)
+ if profile is not None:
+ bfd['profile'] = profile
+ fil_neighbor.pop('profile')
+ if auth_pwd:
+ fil_neighbor['auth_pwd'] = auth_pwd
+ if ebgp_multihop:
+ fil_neighbor['ebgp_multihop'] = ebgp_multihop
+ if local_as:
+ fil_neighbor['local_as'] = local_as
+ if bfd:
+ fil_neighbor['bfd'] = bfd
+ if fil_neighbor:
+ fil_neighbors.append(fil_neighbor)
+ if fil_neighbors:
+ tmp['neighbors'] = fil_neighbors
+ filtered_data.append(tmp)
+ return filtered_data
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py
new file mode 100644
index 00000000..26119b61
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/bgp_neighbors_af/bgp_neighbors_af.py
@@ -0,0 +1,222 @@
+#
+# -*- 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 sonic bgp_neighbors_af 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ remove_empties_from_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_neighbors_af.bgp_neighbors_af import Bgp_neighbors_afArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ get_all_bgp_neighbors,
+ get_from_params_map,
+ update_bgp_nbr_pg_ip_afi_dict,
+ update_bgp_nbr_pg_prefix_limit_dict
+)
+
+
+class Bgp_neighbors_afFacts(object):
+ """ The sonic bgp_neighbors_af fact class
+ """
+
+ neighbor_af_params_map = {
+ 'afi': 'afi-safi-name',
+ 'route_reflector_client': 'route-reflector-client',
+ 'route_server_client': 'route-server-client',
+ 'allowas_in_origin': ['allow-own-as', 'origin'],
+ 'allowas_in_value': ['allow-own-as', 'as-count'],
+ 'in_route_name': ['apply-policy', 'import-policy'],
+ 'out_route_name': ['apply-policy', 'export-policy'],
+ 'activate': 'enabled',
+ 'prefix_list_in': ['prefix-list', 'import-policy'],
+ 'prefix_list_out': ['prefix-list', 'export-policy'],
+ 'ipv4_unicast': 'ipv4-unicast',
+ 'ipv6_unicast': 'ipv6-unicast',
+ 'l2vpn_evpn': ['l2vpn-evpn', 'prefix-limit']
+ }
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Bgp_neighbors_afArgs.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 fill_route_map(self, data):
+ for route_map_key in ['out_route_name', 'in_route_name']:
+ if route_map_key in data:
+ route_map = data['route_map']
+ for e_route in data[route_map_key]:
+ direction = route_map_key.split('_', maxsplit=1)[0]
+ route_map.append({'name': e_route, 'direction': direction})
+ data.pop(route_map_key)
+
+ def normalize_neighbors_af_data(self, neighbors):
+ norm_neighbors = []
+
+ for nei_data in neighbors:
+ norm_neighbor = {}
+
+ neighbor = nei_data.get('neighbor-address', None)
+ if not neighbor:
+ continue
+ norm_neighbor['neighbor'] = neighbor
+ norm_neighbor['address_family'] = []
+ nei_afs = nei_data.get('afi-safis', None)
+ if not nei_afs:
+ if norm_neighbor:
+ norm_neighbors.append(norm_neighbor)
+ continue
+ nei_afs = nei_afs.get('afi-safi', None)
+ if not nei_afs:
+ if norm_neighbor:
+ norm_neighbors.append(norm_neighbor)
+ continue
+ norm_neighbor_afs = []
+ for nei_af in nei_afs:
+ norm_nei_af = get_from_params_map(self.neighbor_af_params_map, nei_af)
+ if norm_nei_af:
+ if 'activate' not in norm_nei_af:
+ norm_nei_af['activate'] = False
+ if 'route_server_client' not in norm_nei_af:
+ norm_nei_af['route_server_client'] = False
+ norm_nei_af['route_map'] = []
+ self.fill_route_map(norm_nei_af)
+
+ allowas_in = {}
+ allowas_in_origin = norm_nei_af.get('allowas_in_origin', None)
+ if allowas_in_origin is not None:
+ allowas_in['origin'] = allowas_in_origin
+ norm_nei_af.pop('allowas_in_origin')
+
+ allowas_in_value = norm_nei_af.get('allowas_in_value', None)
+ if allowas_in_value is not None:
+ allowas_in['value'] = allowas_in_value
+ norm_nei_af.pop('allowas_in_value')
+ if allowas_in:
+ norm_nei_af['allowas_in'] = allowas_in
+
+ ipv4_unicast = norm_nei_af.get('ipv4_unicast', None)
+ ipv6_unicast = norm_nei_af.get('ipv6_unicast', None)
+ l2vpn_evpn = norm_nei_af.get('l2vpn_evpn', None)
+ if ipv4_unicast:
+ if 'config' in ipv4_unicast:
+ ip_afi = update_bgp_nbr_pg_ip_afi_dict(ipv4_unicast['config'])
+ if ip_afi:
+ norm_nei_af['ip_afi'] = ip_afi
+ if 'prefix-limit' in ipv4_unicast and 'config' in ipv4_unicast['prefix-limit']:
+ prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(ipv4_unicast['prefix-limit']['config'])
+ if prefix_limit:
+ norm_nei_af['prefix_limit'] = prefix_limit
+ norm_nei_af.pop('ipv4_unicast')
+ elif ipv6_unicast:
+ if 'config' in ipv6_unicast:
+ ip_afi = update_bgp_nbr_pg_ip_afi_dict(ipv6_unicast['config'])
+ if ip_afi:
+ norm_nei_af['ip_afi'] = ip_afi
+ if 'prefix-limit' in ipv6_unicast and 'config' in ipv6_unicast['prefix-limit']:
+ prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(ipv6_unicast['prefix-limit']['config'])
+ if prefix_limit:
+ norm_nei_af['prefix_limit'] = prefix_limit
+ norm_nei_af.pop('ipv6_unicast')
+ elif l2vpn_evpn:
+ if 'config' in l2vpn_evpn:
+ prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(l2vpn_evpn['config'])
+ if prefix_limit:
+ norm_nei_af['prefix_limit'] = prefix_limit
+ norm_nei_af.pop('l2vpn_evpn')
+
+ norm_neighbor_afs.append(norm_nei_af)
+ if norm_neighbor_afs:
+ norm_neighbor['address_family'] = norm_neighbor_afs
+ if norm_neighbor:
+ norm_neighbors.append(norm_neighbor)
+ return norm_neighbors
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for BGP
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = list()
+ if connection: # just for linting purposes, remove
+ pass
+
+ if not data:
+ data = get_all_bgp_neighbors(self._module)
+
+ new_data = []
+ for conf in data:
+ if not conf:
+ continue
+ new_item = {}
+ new_item['bgp_as'] = conf['bgp_as']
+ new_item['vrf_name'] = conf['vrf_name']
+ neighbors = conf.get('neighbors', None)
+ if not neighbors:
+ new_data.append(new_item)
+ continue
+ neighbors = neighbors.get('neighbor', None)
+ if not neighbors:
+ new_data.append(new_item)
+ continue
+
+ new_neighbors = self.normalize_neighbors_af_data(neighbors)
+ if new_neighbors:
+ new_item['neighbors'] = new_neighbors
+ if new_item:
+ new_data.append(new_item)
+
+ # operate on a collection of resource x
+ for conf in new_data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ # split the config into instances of the resource
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('bgp_neighbors_af', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)})
+ facts['bgp_neighbors_af'] = 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
+ """
+ return conf
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py
new file mode 100644
index 00000000..75622632
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/facts.py
@@ -0,0 +1,101 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The facts class for sonic
+this file validates each subset of facts and selectively
+calls the appropriate facts gathering function
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.facts.facts import FactsArgs
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts import (
+ FactsBase,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vlans.vlans import VlansFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.interfaces.interfaces import InterfacesFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l2_interfaces.l2_interfaces import L2_interfacesFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.l3_interfaces.l3_interfaces import L3_interfacesFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp.bgp import BgpFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_af.bgp_af import Bgp_afFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_neighbors.bgp_neighbors import Bgp_neighborsFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_neighbors_af.bgp_neighbors_af import Bgp_neighbors_afFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_as_paths.bgp_as_paths import Bgp_as_pathsFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_communities.bgp_communities import Bgp_communitiesFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.bgp_ext_communities.bgp_ext_communities import (
+ Bgp_ext_communitiesFacts,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.mclag.mclag import MclagFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.prefix_lists.prefix_lists import Prefix_listsFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vrfs.vrfs import VrfsFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.vxlans.vxlans import VxlansFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.users.users import UsersFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.port_breakout.port_breakout import Port_breakoutFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.aaa.aaa import AaaFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.tacacs_server.tacacs_server import Tacacs_serverFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.system.system import SystemFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.radius_server.radius_server import Radius_serverFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.static_routes.static_routes import Static_routesFacts
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.ntp.ntp import NtpFacts
+
+FACT_LEGACY_SUBSETS = {}
+FACT_RESOURCE_SUBSETS = dict(
+ vlans=VlansFacts,
+ interfaces=InterfacesFacts,
+ l2_interfaces=L2_interfacesFacts,
+ l3_interfaces=L3_interfacesFacts,
+ lag_interfaces=Lag_interfacesFacts,
+ bgp=BgpFacts,
+ bgp_af=Bgp_afFacts,
+ bgp_neighbors=Bgp_neighborsFacts,
+ bgp_neighbors_af=Bgp_neighbors_afFacts,
+ bgp_as_paths=Bgp_as_pathsFacts,
+ bgp_communities=Bgp_communitiesFacts,
+ bgp_ext_communities=Bgp_ext_communitiesFacts,
+ mclag=MclagFacts,
+ prefix_lists=Prefix_listsFacts,
+ vrfs=VrfsFacts,
+ vxlans=VxlansFacts,
+ users=UsersFacts,
+ system=SystemFacts,
+ port_breakout=Port_breakoutFacts,
+ aaa=AaaFacts,
+ tacacs_server=Tacacs_serverFacts,
+ radius_server=Radius_serverFacts,
+ static_routes=Static_routesFacts,
+ ntp=NtpFacts,
+)
+
+
+class Facts(FactsBase):
+ """ The fact class for sonic
+ """
+
+ 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 sonic
+
+ :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
+ """
+ netres_choices = FactsArgs.argument_spec['gather_network_resources'].get('choices', [])
+ 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/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py
new file mode 100644
index 00000000..a36b5d3c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py
@@ -0,0 +1,147 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.interfaces.interfaces import InterfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class InterfacesFacts(object):
+ """ The sonic interfaces fact class
+ """
+ loop_backs = ","
+
+ 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 get_all_interfaces(self):
+ """Get all the interfaces available in chassis"""
+ all_interfaces = {}
+ request = [{"path": "data/openconfig-interfaces:interfaces", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-interfaces:interfaces" in response[0][1]:
+ all_interfaces = response[0][1].get("openconfig-interfaces:interfaces", {})
+ return all_interfaces['interface']
+
+ 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
+ """
+ objs = []
+ if connection: # just for linting purposes, remove
+ pass
+
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_interfaces()
+ # operate on a collection of resource x
+ self.reset_loop_backs()
+
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ obj = self.transform_config(obj)
+ # split the config into instances of the resource
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('interfaces', None)
+ facts = {}
+ if objs:
+ facts['interfaces'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ if params:
+ facts['interfaces'].extend(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
+ """
+ return conf
+
+ def transform_config(self, conf):
+
+ exist_cfg = conf['config']
+ trans_cfg = None
+
+ is_loop_back = False
+ name = conf['name']
+ if name.startswith('Loopback'):
+ is_loop_back = True
+ pos = name.find('|')
+ if pos > 0:
+ name = name[0:pos]
+
+ if not (is_loop_back and self.is_loop_back_already_esist(name)) and (name != "eth0"):
+ trans_cfg = dict()
+ trans_cfg['name'] = name
+ if is_loop_back:
+ self.update_loop_backs(name)
+ else:
+ trans_cfg['enabled'] = exist_cfg['enabled'] if exist_cfg.get('enabled') is not None else True
+ trans_cfg['description'] = exist_cfg['description'] if exist_cfg.get('description') else ""
+ trans_cfg['mtu'] = exist_cfg['mtu'] if exist_cfg.get('mtu') else 9100
+
+ return trans_cfg
+
+ def reset_loop_backs(self):
+ self.loop_backs = ","
+
+ def update_loop_backs(self, loop_back):
+ self.loop_backs += "{0},".format(loop_back)
+
+ def is_loop_back_already_esist(self, loop_back):
+ return (",{0},".format(loop_back) in self.loop_backs)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py
new file mode 100644
index 00000000..07d7f97d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,160 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class L2_interfacesFacts(object):
+ """ The sonic 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 vlan_range_to_list(self, in_range):
+ range_bounds = in_range.split('-')
+ range_bottom = int(range_bounds[0])
+ range_top = int(range_bounds[1]) + 1
+ vlan_list = list(range(range_bottom, range_top))
+ vlan_dict_list = []
+ for vlan in vlan_list:
+ vlan_dict_list.append({'vlan': vlan})
+ return vlan_dict_list
+
+ def get_l2_interfaces_from_interfaces(self, interfaces):
+ l2_interfaces = []
+
+ for intf in interfaces:
+ name = intf['name']
+ key = 'openconfig-if-ethernet:ethernet'
+ if name.startswith('PortChannel'):
+ key = 'openconfig-if-aggregate:aggregation'
+ eth_det = intf.get(key)
+ if eth_det:
+ open_cfg_vlan = eth_det.get('openconfig-vlan:switched-vlan')
+ if open_cfg_vlan:
+ new_det = dict()
+ new_det['name'] = name
+ if name == "eth0":
+ continue
+ if (open_cfg_vlan['config'].get('access-vlan')):
+ new_det['access'] = dict({'vlan': open_cfg_vlan['config'].get('access-vlan')})
+ if (open_cfg_vlan['config'].get('trunk-vlans')):
+ new_det['trunk'] = {}
+ new_det['trunk']['allowed_vlans'] = []
+
+ # Save trunk vlans as a list of single vlan dicts: Convert
+ # any ranges to lists of individual vlan dicts and merge
+ # each resulting "range list" onto the main list for the
+ # interface.
+ for vlan in open_cfg_vlan['config'].get('trunk-vlans'):
+ if isinstance(vlan, str) and '-' in vlan:
+ new_det['trunk']['allowed_vlans'].extend(self.vlan_range_to_list(vlan))
+ else:
+ new_det['trunk']['allowed_vlans'].append({'vlan': vlan})
+ l2_interfaces.append(new_det)
+
+ return l2_interfaces
+
+ def get_all_l2_interfaces(self):
+ """Get all the l2_interfaces available in chassis"""
+ l2_interfaces = {}
+ request = [{"path": "data/openconfig-interfaces:interfaces", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-interfaces:interfaces" in response[0][1]:
+ interfaces = response[0][1].get("openconfig-interfaces:interfaces", {})
+ if interfaces.get("interface"):
+ interfaces = interfaces['interface']
+ l2_interfaces = self.get_l2_interfaces_from_interfaces(interfaces)
+ else:
+ l2_interfaces = {}
+
+ return l2_interfaces
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for l2_interfaces
+ :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
+
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_l2_interfaces()
+
+ objs = list()
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ # split the config into instances of the resource
+ 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
+ """
+ return conf
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py
new file mode 100644
index 00000000..69a6dcd4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/l3_interfaces/l3_interfaces.py
@@ -0,0 +1,185 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+
+class L3_interfacesFacts(object):
+ """ The sonic l3_interfaces fact class
+ """
+
+ loop_backs = ","
+
+ 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 get_l3_interfaces(self):
+ url = "data/openconfig-interfaces:interfaces/interface"
+ method = "GET"
+ request = [{"path": url, "method": method}]
+
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ l3_lists = []
+ if "openconfig-interfaces:interface" in response[0][1]:
+ l3_lists = response[0][1].get("openconfig-interfaces:interface", [])
+
+ l3_configs = []
+ for l3 in l3_lists:
+ l3_dict = dict()
+ l3_name = l3["name"]
+ if l3_name == "eth0":
+ continue
+
+ l3_dict['name'] = l3_name
+
+ ip = None
+ anycast_addr = list()
+ if l3.get('openconfig-vlan:routed-vlan'):
+ ip = l3['openconfig-vlan:routed-vlan']
+ if ip.get('openconfig-if-ip:ipv4', None) and ip['openconfig-if-ip:ipv4'].get('openconfig-interfaces-ext:sag-ipv4', None):
+ if ip['openconfig-if-ip:ipv4']['openconfig-interfaces-ext:sag-ipv4'].get('config', None):
+ if ip['openconfig-if-ip:ipv4']['openconfig-interfaces-ext:sag-ipv4']['config'].get('static-anycast-gateway', None):
+ anycast_addr = ip['openconfig-if-ip:ipv4']['openconfig-interfaces-ext:sag-ipv4']['config']['static-anycast-gateway']
+ else:
+ ip = l3.get('subinterfaces', {}).get('subinterface', [{}])[0]
+
+ l3_dict['ipv4'] = dict()
+ l3_ipv4 = list()
+ if anycast_addr:
+ l3_dict['ipv4']['anycast_addresses'] = anycast_addr
+ elif 'openconfig-if-ip:ipv4' in ip and 'addresses' in ip['openconfig-if-ip:ipv4'] and 'address' in ip['openconfig-if-ip:ipv4']['addresses']:
+ for ipv4 in ip['openconfig-if-ip:ipv4']['addresses']['address']:
+ if ipv4.get('config') and ipv4.get('config').get('ip'):
+ temp = dict()
+ temp['address'] = str(ipv4['config']['ip']) + '/' + str(ipv4['config']['prefix-length'])
+ temp['secondary'] = ipv4['config']['secondary']
+ l3_ipv4.append(temp)
+ if l3_ipv4:
+ l3_dict['ipv4']['addresses'] = l3_ipv4
+
+ l3_dict['ipv6'] = dict()
+ l3_ipv6 = list()
+ if 'openconfig-if-ip:ipv6' in ip:
+ if 'addresses' in ip['openconfig-if-ip:ipv6'] and 'address' in ip['openconfig-if-ip:ipv6']['addresses']:
+ for ipv6 in ip['openconfig-if-ip:ipv6']['addresses']['address']:
+ if ipv6.get('config') and ipv6.get('config').get('ip'):
+ temp = dict()
+ temp['address'] = str(ipv6['config']['ip']) + '/' + str(ipv6['config']['prefix-length'])
+ l3_ipv6.append(temp)
+ if l3_ipv6:
+ l3_dict['ipv6']['addresses'] = l3_ipv6
+ if 'config' in ip['openconfig-if-ip:ipv6'] and 'enabled' in ip['openconfig-if-ip:ipv6']['config']:
+ l3_dict['ipv6']['enabled'] = ip['openconfig-if-ip:ipv6']['config']['enabled']
+
+ l3_configs.append(l3_dict)
+ return l3_configs
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for l3_interfaces
+ :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
+ if not data:
+ resources = self.get_l3_interfaces()
+ objs = []
+ for resource in resources:
+ if resource:
+ obj = self.render_config(self.generated_spec, resource)
+ obj = self.transform_config(obj)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('l3_interfaces', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['l3_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
+ """
+ return conf
+
+ def transform_config(self, conf):
+ exist_cfg = conf
+ trans_cfg = None
+
+ is_loop_back = False
+ name = exist_cfg['name']
+ if name.startswith('Loopback'):
+ is_loop_back = True
+ pos = name.find('|')
+ if pos > 0:
+ name = name[0:pos]
+
+ if not (is_loop_back and self.is_loop_back_already_esist(name)) and (name != "eth0"):
+ trans_cfg = dict()
+ trans_cfg['name'] = name
+ if is_loop_back:
+ self.update_loop_backs(name)
+ trans_cfg['ipv4'] = exist_cfg.get('ipv4', {})
+ trans_cfg['ipv6'] = exist_cfg.get('ipv6', {})
+
+ return trans_cfg
+
+ def reset_loop_backs(self):
+ self.loop_backs = ","
+
+ def update_loop_backs(self, loop_back):
+ self.loop_backs += "{Loopback},".format(Loopback=loop_back)
+
+ def is_loop_back_already_esist(self, loop_back):
+ return (",{0},".format(loop_back) in self.loop_backs)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py
new file mode 100644
index 00000000..72819681
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/lag_interfaces/lag_interfaces.py
@@ -0,0 +1,135 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class Lag_interfacesFacts(object):
+ """ The sonic 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 get_all_portchannels(self):
+ """Get all the interfaces available in chassis"""
+ request = [{"path": "data/sonic-portchannel:sonic-portchannel", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ if response[0][1]:
+ data = response[0][1]['sonic-portchannel:sonic-portchannel']
+ else:
+ data = []
+ if data is not None:
+ if "PORTCHANNEL_MEMBER" in data:
+ portchannel_members_list = data["PORTCHANNEL_MEMBER"]["PORTCHANNEL_MEMBER_LIST"]
+ else:
+ portchannel_members_list = []
+ if "PORTCHANNEL" in data:
+ portchannel_list = data["PORTCHANNEL"]["PORTCHANNEL_LIST"]
+ else:
+ portchannel_list = []
+ if portchannel_list:
+ for i in portchannel_list:
+ if not any(d["name"] == i["name"] for d in portchannel_members_list):
+ portchannel_members_list.append({'ifname': None, 'name': i['name']})
+ if data:
+ return portchannel_members_list
+ else:
+ return []
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for lag_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 = self.get_all_portchannels()
+ # operate on a collection of resource x
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ obj = self.transform_config(obj)
+ if obj:
+ self.merge_portchannels(objs, obj)
+ facts = {}
+ if objs:
+ facts['lag_interfaces'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ for cfg in params['config']:
+ facts['lag_interfaces'].append(cfg)
+ ansible_facts['ansible_network_resources'].update(facts)
+ return ansible_facts
+
+ def render_config(self, spec, conf):
+ return conf
+
+ def transform_config(self, conf):
+ trans_cfg = dict()
+ trans_cfg['name'] = conf['name']
+ trans_cfg['members'] = dict()
+ if conf['ifname']:
+ interfaces = list()
+ interface = {'member': conf['ifname']}
+ interfaces.append(interface)
+ trans_cfg['members'] = {'interfaces': interfaces}
+ return trans_cfg
+
+ def merge_portchannels(self, configs, conf):
+ if len(configs) == 0:
+ configs.append(conf)
+ else:
+ new_interface = None
+ if conf.get('members') and conf['members'].get('interfaces'):
+ new_interface = conf['members']['interfaces'][0]
+ else:
+ configs.append(conf)
+ if new_interface:
+ matched = next((cfg for cfg in configs if cfg['name'] == conf['name']), None)
+ if matched and matched.get('members'):
+ ext_interfaces = matched.get('members').get('interfaces', [])
+ ext_interfaces.append(new_interface)
+ else:
+ configs.append(conf)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py
new file mode 100644
index 00000000..69864cdf
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/mclag/mclag.py
@@ -0,0 +1,139 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic mclag 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.mclag.mclag import MclagArgs
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class MclagFacts(object):
+ """ The sonic mclag fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = MclagArgs.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_all_mclag(self):
+ """Get all the mclag available in chassis"""
+ request = [{"path": "data/openconfig-mclag:mclag", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ if ('openconfig-mclag:mclag' in response[0][1]):
+ data = response[0][1]['openconfig-mclag:mclag']
+ else:
+ data = {}
+ return data
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for mclag
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = None
+ if not data:
+ data = self.get_all_mclag()
+ if data:
+ objs = self.render_config(self.generated_spec, data)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['mclag'] = 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 = self.parse_sonic_mclag(spec, conf)
+ return config
+
+ def parse_sonic_mclag(self, spec, conf):
+ config = {}
+ portchannels_list = []
+ if conf:
+ domain_data = None
+ if conf.get('mclag-domains', None) and conf['mclag-domains'].get('mclag-domain', None):
+ domain_data = conf['mclag-domains']['mclag-domain'][0]
+ if domain_data:
+ domain_id = domain_data['domain-id']
+ config['domain_id'] = domain_id
+ domain_config = domain_data.get('config', None)
+ if domain_config:
+ if domain_config.get('session-timeout', None):
+ config['session_timeout'] = domain_config['session-timeout']
+ if domain_config.get('keepalive-interval', None):
+ config['keepalive'] = domain_config['keepalive-interval']
+ if domain_config.get('source-address', None):
+ config['source_address'] = domain_config['source-address']
+ if domain_config.get('peer-address', None):
+ config['peer_address'] = domain_config['peer-address']
+ if domain_config.get('peer-link', None):
+ config['peer_link'] = domain_config['peer-link']
+ if domain_config.get('mclag-system-mac', None):
+ config['system_mac'] = domain_config['mclag-system-mac']
+
+ if conf.get('vlan-interfaces', None) and conf['vlan-interfaces'].get('vlan-interface', None):
+ vlans_list = []
+ vlan_data = conf['vlan-interfaces']['vlan-interface']
+ for vlan in vlan_data:
+ vlans_list.append({'vlan': vlan['name']})
+ if vlans_list:
+ config['unique_ip'] = {'vlans': vlans_list}
+
+ if conf.get('interfaces', None) and conf['interfaces'].get('interface', None):
+ portchannels_list = []
+ po_data = conf['interfaces']['interface']
+ for po in po_data:
+ if po.get('config', None) and po['config'].get('mclag-domain-id', None) and domain_id == domain_data['domain-id']:
+ portchannels_list.append({'lag': po['name']})
+ if portchannels_list:
+ config['members'] = {'portchannels': portchannels_list}
+
+ return config
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py
new file mode 100644
index 00000000..a47142b4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/ntp/ntp.py
@@ -0,0 +1,153 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic ntp 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.ntp.ntp import NtpArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class NtpFacts(object):
+ """ The sonic ntp fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = NtpArgs.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 ntp
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_ntp_configuration()
+
+ obj = self.render_config(self.generated_spec, data)
+
+ ansible_facts['ansible_network_resources'].pop('ntp', None)
+ facts = {}
+ if obj:
+ params = utils.validate_config(self.argument_spec, {'config': obj})
+ facts['ntp'] = 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
+ """
+ return conf
+
+ def get_ntp_configuration(self):
+ """Get all NTP configuration"""
+
+ all_ntp_request = [{"path": "data/openconfig-system:system/ntp", "method": GET}]
+ all_ntp_response = []
+ try:
+ all_ntp_response = edit_config(self._module, to_request(self._module, all_ntp_request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ all_ntp_config = dict()
+ if 'openconfig-system:ntp' in all_ntp_response[0][1]:
+ all_ntp_config = all_ntp_response[0][1].get('openconfig-system:ntp', {})
+
+ ntp_global_config = dict()
+ if 'config' in all_ntp_config:
+ ntp_global_config = all_ntp_config.get('config', {})
+
+ ntp_servers = []
+ if 'servers' in all_ntp_config:
+ ntp_servers = all_ntp_config['servers'].get('server', [])
+
+ ntp_keys = []
+ if 'ntp-keys' in all_ntp_config:
+ ntp_keys = all_ntp_config['ntp-keys'].get('ntp-key', [])
+
+ ntp_config = dict()
+
+ if 'network-instance' in ntp_global_config:
+ ntp_config['vrf'] = ntp_global_config['network-instance']
+
+ if 'enable-ntp-auth' in ntp_global_config:
+ ntp_config['enable_ntp_auth'] = ntp_global_config['enable-ntp-auth']
+
+ if 'source-interface' in ntp_global_config:
+ ntp_config['source_interfaces'] = ntp_global_config['source-interface']
+
+ if 'trusted-key' in ntp_global_config:
+ ntp_config['trusted_keys'] = ntp_global_config['trusted-key']
+
+ servers = []
+ for ntp_server in ntp_servers:
+ if 'config' in ntp_server:
+ server = {}
+ server['address'] = ntp_server['config'].get('address', None)
+ if 'key-id' in ntp_server['config']:
+ server['key_id'] = ntp_server['config']['key-id']
+ server['minpoll'] = ntp_server['config'].get('minpoll', None)
+ server['maxpoll'] = ntp_server['config'].get('maxpoll', None)
+ servers.append(server)
+ ntp_config['servers'] = servers
+
+ keys = []
+ for ntp_key in ntp_keys:
+ if 'config' in ntp_key:
+ key = {}
+ key['encrypted'] = ntp_key['config'].get('encrypted', None)
+ key['key_id'] = ntp_key['config'].get('key-id', None)
+ key_type_str = ntp_key['config'].get('key-type', None)
+ key_type = key_type_str.split(":", 1)[-1]
+ key['key_type'] = key_type
+ key['key_value'] = ntp_key['config'].get('key-value', None)
+ keys.append(key)
+ ntp_config['ntp_keys'] = keys
+
+ return ntp_config
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py
new file mode 100644
index 00000000..938bd642
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/port_breakout/port_breakout.py
@@ -0,0 +1,125 @@
+#
+# -*- 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 sonic port breakout 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 json
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.port_breakout.port_breakout import Port_breakoutArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_breakout_mode,
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+POST = "post"
+
+
+class Port_breakoutFacts(object):
+ """ The sonic port breakout fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Port_breakoutArgs.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 port_breakout
+ :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
+
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_port_breakout()
+
+ objs = list()
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('port_breakout', None)
+ facts = {}
+ if objs:
+ facts['port_breakout'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ if params:
+ facts['port_breakout'].extend(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
+ """
+ return conf
+
+ def get_all_port_breakout(self):
+ """Get all the port_breakout configured in the device"""
+ request = [{"path": "operations/sonic-port-breakout:breakout_capabilities", "method": POST}]
+ port_breakout_list = []
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ raw_port_breakout_list = []
+ if "sonic-port-breakout:output" in response[0][1]:
+ raw_port_breakout_list = response[0][1].get("sonic-port-breakout:output", {}).get('caps', [])
+
+ for port_breakout in raw_port_breakout_list:
+ name = port_breakout.get('port', None)
+ mode = port_breakout.get('defmode', None)
+ if name and mode:
+ if '[' in mode:
+ mode = mode[:mode.index('[')]
+ norm_port_breakout = {'name': name, 'mode': mode}
+ mode = get_breakout_mode(self._module, name)
+ if mode:
+ norm_port_breakout['mode'] = mode
+ port_breakout_list.append(norm_port_breakout)
+
+ return port_breakout_list
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/prefix_lists.py
new file mode 100644
index 00000000..e246b572
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/prefix_lists/prefix_lists.py
@@ -0,0 +1,158 @@
+#
+# -*- 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 sonic prefix_lists 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
+
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils \
+ import (
+ remove_empties_from_list
+ )
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.prefix_lists.prefix_lists import Prefix_listsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+
+
+def prefix_set_cfg_parse(unparsed_prefix_set):
+ '''Parse the raw input configuration JSON representation for the prefix set specified
+ by the input "unparsed_prefix_set" input parameter. Parse the information to
+ convert it to a dictionary matching the "argspec" for the "prefix_lists" resource
+ module.'''
+
+ parsed_prefix_set = dict()
+ if not unparsed_prefix_set.get("config"):
+ return parsed_prefix_set
+ parsed_prefix_set['name'] = unparsed_prefix_set['name']
+ pfx_cfg = unparsed_prefix_set['config']
+ if pfx_cfg.get('mode') and isinstance((pfx_cfg['mode']), str):
+ parsed_prefix_set['afi'] = pfx_cfg['mode'].lower()
+ if unparsed_prefix_set.get('openconfig-routing-policy-ext:extended-prefixes'):
+ prefix_lists_container = \
+ unparsed_prefix_set['openconfig-routing-policy-ext:extended-prefixes']
+ if not prefix_lists_container.get("extended-prefix"):
+ return parsed_prefix_set
+ prefix_lists_unparsed = prefix_lists_container['extended-prefix']
+
+ prefix_lists_parsed = []
+ for prefix_entry_unparsed in prefix_lists_unparsed:
+ if not prefix_entry_unparsed.get('config'):
+ continue
+ if not prefix_entry_unparsed['config'].get('action'):
+ continue
+ prefix_entry_cfg = prefix_entry_unparsed['config']
+ prefix_parsed = dict()
+ prefix_parsed['action'] = prefix_entry_cfg['action'].lower()
+ if not prefix_entry_unparsed.get('ip-prefix'):
+ continue
+ if not prefix_entry_unparsed.get('sequence-number'):
+ continue
+
+ prefix_parsed['prefix'] = prefix_entry_unparsed['ip-prefix']
+ prefix_parsed['sequence'] = prefix_entry_unparsed['sequence-number']
+ if (prefix_entry_unparsed.get('masklength-range') and
+ (not prefix_entry_unparsed['masklength-range'] == 'exact')):
+ mask = int(prefix_parsed['prefix'].split('/')[1])
+ ge_le = prefix_entry_unparsed['masklength-range'].split('..')
+ ge_bound = int(ge_le[0])
+ if ge_bound != mask:
+ prefix_parsed['ge'] = ge_bound
+ pfx_len = 32 if parsed_prefix_set['afi'] == 'ipv4' else 128
+ le_bound = int(ge_le[1])
+ if le_bound != pfx_len:
+ prefix_parsed['le'] = le_bound
+ prefix_lists_parsed.append(prefix_parsed)
+ parsed_prefix_set['prefixes'] = prefix_lists_parsed
+ return parsed_prefix_set
+
+
+class Prefix_listsFacts:
+ """ The sonic prefix_lists fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Prefix_listsArgs.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_all_prefix_sets(self):
+ '''Execute a REST "GET" API to fetch all of the current prefix list configuration
+ from the target device.'''
+
+ pfx_fetch_spec = "openconfig-routing-policy:routing-policy/defined-sets/prefix-sets"
+ pfx_resp_key = "openconfig-routing-policy:prefix-sets"
+ pfx_set_key = "prefix-set"
+ # pfx_short_spec = "openconfig-routing-policy:prefix-set"
+ url = "data/%s" % pfx_fetch_spec
+ method = "GET"
+ request = [{"path": url, "method": method}]
+
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc))
+
+ prefix_lists_unparsed = []
+ resp_prefix_set = response[0][1].get(pfx_resp_key, None)
+ if resp_prefix_set:
+ prefix_lists_unparsed = resp_prefix_set.get(pfx_set_key, None)
+ return prefix_lists_unparsed
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for prefix_lists
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if connection: # (comment by Ansible): just for linting purposes, remove
+ pass
+
+ if not data:
+ # Fetch data from the current device configuration
+ # (Skip if operating on previously fetched configuration.)
+ data = self.get_all_prefix_sets()
+
+ # split the unparsed prefix configuration list into a list
+ # of parsed prefix set "instances" (dictonary "objects").
+ prefix_sets = list()
+ for prefix_set_cfg in data:
+ prefix_set = prefix_set_cfg_parse(prefix_set_cfg)
+ if prefix_set:
+ prefix_sets.append(prefix_set)
+
+ ansible_facts['ansible_network_resources'].pop('prefix_lists', None)
+ facts = {}
+ if prefix_sets:
+ params = utils.validate_config(self.argument_spec,
+ {'config': remove_empties_from_list(prefix_sets)})
+ facts['prefix_lists'] = params['config']
+ ansible_facts['ansible_network_resources'].update(facts)
+ return ansible_facts
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py
new file mode 100644
index 00000000..72593b22
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/radius_server/radius_server.py
@@ -0,0 +1,168 @@
+#
+# -*- 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 sonic tacas server 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 json
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.radius_server.radius_server import Radius_serverArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class Radius_serverFacts(object):
+ """ The sonic tacas server fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Radius_serverArgs.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 radius_server
+ :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
+
+ obj = None
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_radius_server()
+
+ obj = self.render_config(self.generated_spec, data)
+
+ ansible_facts['ansible_network_resources'].pop('radius_server', None)
+ facts = {}
+ if obj:
+ facts['radius_server'] = {}
+ params = utils.validate_config(self.argument_spec, {'config': obj})
+ if params:
+ facts['radius_server'] = 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
+ """
+ return conf
+
+ def get_all_radius_server(self):
+ """Get all the radius_server configured in the device"""
+ request = [{"path": "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/config", "method": GET}]
+ radius_server_data = {}
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-system:config" in response[0][1]:
+ raw_radius_global_data = response[0][1].get("openconfig-system:config", {})
+
+ if 'auth-type' in raw_radius_global_data:
+ radius_server_data['auth_type'] = raw_radius_global_data['auth-type']
+ if 'secret-key' in raw_radius_global_data:
+ radius_server_data['key'] = raw_radius_global_data['secret-key']
+ if 'timeout' in raw_radius_global_data:
+ radius_server_data['timeout'] = raw_radius_global_data['timeout']
+
+ request = [{"path": "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/openconfig-aaa-radius-ext:radius/config", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-aaa-radius-ext:config" in response[0][1]:
+ raw_radius_ext_global_data = response[0][1].get("openconfig-aaa-radius-ext:config", {})
+
+ if 'nas-ip-address' in raw_radius_ext_global_data:
+ radius_server_data['nas_ip'] = raw_radius_ext_global_data['nas-ip-address']
+ if 'retransmit-attempts' in raw_radius_ext_global_data:
+ radius_server_data['retransmit'] = raw_radius_ext_global_data['retransmit-attempts']
+ if 'statistics' in raw_radius_ext_global_data:
+ radius_server_data['statistics'] = raw_radius_ext_global_data['statistics']
+
+ request = [{"path": "data/openconfig-system:system/aaa/server-groups/server-group=RADIUS/servers", "method": GET}]
+ hosts = []
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ raw_radius_server_list = []
+ if "openconfig-system:servers" in response[0][1]:
+ raw_radius_server_list = response[0][1].get("openconfig-system:servers", {}).get('server', [])
+
+ for radius_host in raw_radius_server_list:
+ host_data = {}
+ if 'address' in radius_host:
+ host_data['name'] = radius_host['address']
+ cfg = radius_host.get('config', None)
+ if cfg:
+ if 'auth-type' in cfg:
+ host_data['auth_type'] = cfg['auth-type']
+ if 'priority' in cfg:
+ host_data['priority'] = cfg['priority']
+ if 'vrf' in cfg:
+ host_data['vrf'] = cfg['vrf']
+ if 'timeout' in cfg:
+ host_data['timeout'] = cfg['timeout']
+ if radius_host.get('radius', None) and radius_host['radius'].get('config', None):
+ tacas_cfg = radius_host['radius']['config']
+ if tacas_cfg.get('auth-port', None):
+ host_data['port'] = tacas_cfg['auth-port']
+ if tacas_cfg.get('secret-key', None):
+ host_data['key'] = tacas_cfg['secret-key']
+ if tacas_cfg.get('openconfig-aaa-radius-ext:source-interface', None):
+ host_data['source_interface'] = tacas_cfg['openconfig-aaa-radius-ext:source-interface']
+ if tacas_cfg.get('retransmit-attempts', None):
+ host_data['retransmit'] = tacas_cfg['retransmit-attempts']
+ if host_data:
+ hosts.append(host_data)
+
+ if hosts:
+ radius_server_data['servers'] = {'host': hosts}
+
+ return radius_server_data
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py
new file mode 100644
index 00000000..f8356644
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/static_routes/static_routes.py
@@ -0,0 +1,173 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ remove_empties_from_list
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.static_routes.static_routes import Static_routesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.bgp_utils import (
+ get_all_vrfs,
+)
+
+network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+protocol_static_routes_path = 'protocols/protocol=STATIC,static/static-routes'
+
+
+class Static_routesFacts(object):
+ """ The sonic 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 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 = []
+ if connection: # just for linting purposes, remove
+ pass
+
+ if not data:
+ static_routes_config = self.get_static_routes(self._module)
+ data = self.update_static_routes(static_routes_config)
+ # operate on a collection of resource x
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ # split the config into instances of the resource
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('static_routes', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': remove_empties_from_list(objs)})
+ facts['static_routes'] = 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
+ """
+
+ return conf
+
+ def get_static_routes(self, module):
+ all_static_routes = []
+ vrfs = get_all_vrfs(module)
+ for vrf_name in vrfs:
+ get_path = '%s=%s/%s' % (network_instance_path, vrf_name, protocol_static_routes_path)
+ request = {'path': get_path, 'method': 'get'}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+ for resp in response:
+ if 'openconfig-network-instance:static-routes' in resp[1]:
+ static_routes_dict = resp[1].get('openconfig-network-instance:static-routes', {})
+ static_routes_dict['vrf'] = vrf_name
+ all_static_routes.append(static_routes_dict)
+ return all_static_routes
+
+ def update_static_routes(self, data):
+ static_vrf_list = []
+ for static_route in data:
+ static_vrf_dict = {}
+ static_route_list = static_route.get('static', [])
+ vrf_name = static_route.get('vrf', None)
+ static_list = []
+ for static in static_route_list:
+ static_dict = {}
+ prefix = static.get('prefix', None)
+ next_hops = static.get('next-hops', None)
+ next_hop_list = next_hops.get('next-hop', [])
+ next_hop_dict_list = []
+ for next_hop in next_hop_list:
+ next_hop_dict = {}
+ index_dict = {}
+ inf_ref = next_hop.get('interface-ref', {})
+ inf_ref_cfg = inf_ref.get('config', {})
+ interface = inf_ref_cfg.get('interface', None)
+ config = next_hop.get('config', {})
+ next_hop_attr = config.get('next-hop', None)
+ metric = config.get('metric', None)
+ nexthop_vrf = config.get('network-instance', None)
+ blackhole = config.get('blackhole', None)
+ track = config.get('track', None)
+ tag = config.get('tag', None)
+ if blackhole:
+ index_dict['blackhole'] = blackhole
+ if interface:
+ index_dict['interface'] = interface
+ if nexthop_vrf:
+ index_dict['nexthop_vrf'] = nexthop_vrf
+ if next_hop_attr:
+ index_dict['next_hop'] = next_hop_attr
+ if index_dict:
+ next_hop_dict['index'] = index_dict
+ if metric:
+ next_hop_dict['metric'] = metric
+ if track:
+ next_hop_dict['track'] = track
+ if tag:
+ next_hop_dict['tag'] = tag
+ if next_hop_dict:
+ next_hop_dict_list.append(next_hop_dict)
+ if prefix:
+ static_dict['prefix'] = prefix
+ if next_hop_dict_list:
+ static_dict['next_hops'] = next_hop_dict_list
+ if static_dict:
+ static_list.append(static_dict)
+ if static_list:
+ static_vrf_dict['static_list'] = static_list
+ if vrf_name:
+ static_vrf_dict['vrf_name'] = vrf_name
+ if static_vrf_dict:
+ static_vrf_list.append(static_vrf_dict)
+
+ return static_vrf_list
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py
new file mode 100644
index 00000000..1d7a82d8
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/system/system.py
@@ -0,0 +1,143 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic system 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.system.system import SystemArgs
+
+GET = "get"
+
+
+class SystemFacts(object):
+ """ The sonic system fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = SystemArgs.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_system(self):
+ """Get system hostname available in chassis"""
+ request = [{"path": "data/openconfig-system:system/config", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ if ('openconfig-system:config' in response[0][1]):
+ data = response[0][1]['openconfig-system:config']
+ else:
+ data = {}
+ return data
+
+ def get_naming(self):
+ """Get interface_naming type available in chassis"""
+ request = [{"path": "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ if ('sonic-device-metadata:DEVICE_METADATA_LIST' in response[0][1]):
+ intf_data = response[0][1]['sonic-device-metadata:DEVICE_METADATA_LIST']
+ if 'intf_naming_mode' in intf_data[0]:
+ data = intf_data[0]
+ else:
+ data = {}
+ return data
+
+ def get_anycast_addr(self):
+ """Get system anycast address available in chassis"""
+ request = [{"path": "data/sonic-sag:sonic-sag/SAG_GLOBAL/SAG_GLOBAL_LIST/", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+ if ('sonic-sag:SAG_GLOBAL_LIST' in response[0][1]):
+ data = response[0][1]['sonic-sag:SAG_GLOBAL_LIST'][0]
+ else:
+ data = {}
+ return data
+
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for system
+ :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_system()
+ intf_naming = self.get_naming()
+ if intf_naming:
+ data.update(intf_naming)
+ anycast_addr = self.get_anycast_addr()
+ if anycast_addr:
+ data.update(anycast_addr)
+ objs = []
+ objs = self.render_config(self.generated_spec, data)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['system'] = 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 = self.parse_sonic_system(spec, conf)
+ return config
+
+ def parse_sonic_system(self, spec, conf):
+ config = deepcopy(spec)
+ if conf:
+ if ('hostname' in conf) and (conf['hostname']):
+ config['hostname'] = conf['hostname']
+ if ('intf_naming_mode' in conf) and (conf['intf_naming_mode']):
+ config['interface_naming'] = conf['intf_naming_mode']
+ if ('IPv4' in conf) and (conf['IPv4'] == "enable"):
+ config['anycast_address']['ipv4'] = True
+ if ('IPv4' in conf) and (conf['IPv4'] == "disable"):
+ config['anycast_address']['ipv4'] = False
+ if ('IPv6' in conf) and (conf['IPv6'] == "enable"):
+ config['anycast_address']['ipv6'] = True
+ if ('IPv6' in conf) and (conf['IPv6'] == "disable"):
+ config['anycast_address']['ipv6'] = False
+ if ('gwmac' in conf) and (conf['gwmac']):
+ config['anycast_address']['mac_address'] = conf['gwmac']
+ return utils.remove_empties(config)
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py
new file mode 100644
index 00000000..a1e79910
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/tacacs_server/tacacs_server.py
@@ -0,0 +1,150 @@
+#
+# -*- 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 sonic tacas server 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 json
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.tacacs_server.tacacs_server import Tacacs_serverArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class Tacacs_serverFacts(object):
+ """ The sonic tacas server fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = Tacacs_serverArgs.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 tacacs_server
+ :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
+
+ obj = None
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_tacacs_server()
+
+ obj = self.render_config(self.generated_spec, data)
+
+ ansible_facts['ansible_network_resources'].pop('tacacs_server', None)
+ facts = {}
+ if obj:
+ facts['tacacs_server'] = {}
+ params = utils.validate_config(self.argument_spec, {'config': obj})
+ if params:
+ facts['tacacs_server'] = 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
+ """
+ return conf
+
+ def get_all_tacacs_server(self):
+ """Get all the tacacs_server configured in the device"""
+ request = [{"path": "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/config", "method": GET}]
+ tacacs_server_data = {}
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-system:config" in response[0][1]:
+ raw_tacacs_global_data = response[0][1].get("openconfig-system:config", {})
+
+ if 'auth-type' in raw_tacacs_global_data:
+ tacacs_server_data['auth_type'] = raw_tacacs_global_data['auth-type']
+ if 'secret-key' in raw_tacacs_global_data:
+ tacacs_server_data['key'] = raw_tacacs_global_data['secret-key']
+ if 'source-interface' in raw_tacacs_global_data:
+ tacacs_server_data['source_interface'] = raw_tacacs_global_data['source-interface']
+ if 'timeout' in raw_tacacs_global_data:
+ tacacs_server_data['timeout'] = raw_tacacs_global_data['timeout']
+
+ request = [{"path": "data/openconfig-system:system/aaa/server-groups/server-group=TACACS/servers", "method": GET}]
+ hosts = []
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ raw_tacacs_server_list = []
+ if "openconfig-system:servers" in response[0][1]:
+ raw_tacacs_server_list = response[0][1].get("openconfig-system:servers", {}).get('server', [])
+
+ for tacacs_host in raw_tacacs_server_list:
+ host_data = {}
+ if 'address' in tacacs_host:
+ host_data['name'] = tacacs_host['address']
+ cfg = tacacs_host.get('config', None)
+ if cfg:
+ if 'auth-type' in cfg:
+ host_data['auth_type'] = cfg['auth-type']
+ if 'priority' in cfg:
+ host_data['priority'] = cfg['priority']
+ if 'vrf' in cfg:
+ host_data['vrf'] = cfg['vrf']
+ if 'timeout' in cfg:
+ host_data['timeout'] = cfg['timeout']
+ if tacacs_host.get('tacacs', None) and tacacs_host['tacacs'].get('config', None):
+ tacas_cfg = tacacs_host['tacacs']['config']
+ if tacas_cfg.get('port', None):
+ host_data['port'] = tacas_cfg['port']
+ if tacas_cfg.get('secret-key', None):
+ host_data['key'] = tacas_cfg['secret-key']
+ if host_data:
+ hosts.append(host_data)
+
+ if hosts:
+ tacacs_server_data['servers'] = {'host': hosts}
+
+ return tacacs_server_data
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py
new file mode 100644
index 00000000..038e97f8
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/users/users.py
@@ -0,0 +1,122 @@
+#
+# -*- 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 sonic users 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.users.users import UsersArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class UsersFacts(object):
+ """ The sonic users fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = UsersArgs.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 users
+ :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
+
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_users()
+
+ objs = list()
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('users', None)
+ facts = {}
+ if objs:
+ facts['users'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ if params:
+ facts['users'].extend(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
+ """
+ return conf
+
+ def get_all_users(self):
+ """Get all the users configured in the device"""
+ request = [{"path": "data/sonic-system-aaa:sonic-system-aaa/USER", "method": GET}]
+ users = []
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ raw_users = []
+ if "sonic-system-aaa:USER" in response[0][1]:
+ raw_users = response[0][1].get("sonic-system-aaa:USER", {}).get('USER_LIST', [])
+
+ for raw_user in raw_users:
+ name = raw_user.get('username', None)
+ role = raw_user.get('role', [])
+ if role and len(role) > 0:
+ role = role[0]
+ password = raw_user.get('password', None)
+ user = {}
+ if name and role:
+ user['name'] = name
+ user['role'] = role
+ if password:
+ user['password'] = password
+ if user:
+ users.append(user)
+ return users
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py
new file mode 100644
index 00000000..7c4af2ea
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vlans/vlans.py
@@ -0,0 +1,126 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic 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
+from copy import deepcopy
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+ utils,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vlans.vlans import VlansArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class VlansFacts(object):
+ """ The sonic 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 populate_facts(self, connection, ansible_facts, data=None):
+ """ Populate the facts for vlans
+ :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
+
+ if not data:
+ vlans = self.get_vlans()
+ objs = []
+ for vlan_id, vlan_config in vlans.items():
+ obj = self.render_config(self.generated_spec, vlan_config)
+ if obj:
+ objs.append(obj)
+ ansible_facts['ansible_network_resources'].pop('vlans', None)
+ facts = {}
+ if objs:
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ facts['vlans'] = 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)
+ try:
+ config['vlan_id'] = int(conf['vlan_id'])
+ if conf.get('description', None):
+ config['description'] = conf['description']
+ except TypeError:
+ config['vlan_id'] = None
+ config['description'] = None
+ return utils.remove_empties(config)
+
+ def get_vlans(self):
+ """Get all the l2_interfaces available in chassis"""
+ request = [{"path": "data/openconfig-interfaces:interfaces", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ interfaces = {}
+ if "openconfig-interfaces:interfaces" in response[0][1]:
+ interfaces = response[0][1].get("openconfig-interfaces:interfaces", {})
+ if interfaces.get("interface"):
+ interfaces = interfaces['interface']
+
+ ret_vlan_configs = {}
+
+ for interface in interfaces:
+ interface_name = interface.get("config").get("name")
+ description = interface.get("config").get("description", None)
+ if "Vlan" in interface_name:
+ vlan_id = interface_name.split("Vlan")[1]
+ vlan_configs = {"vlan_id": vlan_id,
+ "name": interface_name,
+ }
+ if description:
+ vlan_configs['description'] = description
+
+ ret_vlan_configs.update({vlan_id: vlan_configs})
+
+ return ret_vlan_configs
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py
new file mode 100644
index 00000000..797612bc
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vrfs/vrfs.py
@@ -0,0 +1,120 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic vrfs 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vrfs.vrfs import VrfsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class VrfsFacts(object):
+ """ The sonic vrfs fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = VrfsArgs.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 vrf
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_vrf_interfaces()
+
+ objs = list()
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('vrfs', None)
+ facts = {}
+ if objs:
+ facts['vrfs'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ if params:
+ facts['vrfs'].extend(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
+ """
+ return conf
+
+ def get_all_vrf_interfaces(self):
+ """Get all the interfaces available in chassis"""
+ all_network_instatnces = {}
+ request = [{"path": "data/openconfig-network-instance:network-instances", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-network-instance:network-instances" in response[0][1]:
+ all_network_instatnces = response[0][1].get("openconfig-network-instance:network-instances", {})
+ return self.get_vrf_interfaces_from_network_instances(all_network_instatnces['network-instance'])
+
+ def get_vrf_interfaces_from_network_instances(self, network_instances):
+ vrf_interfaces = []
+
+ for each_ins in network_instances:
+ vrf_interface = dict()
+ name = each_ins['name']
+ if name.startswith('Vrf') or name == 'mgmt':
+ vrf_interface['name'] = name
+ if each_ins.get("interfaces"):
+ interfaces = [{"name": intf.get("id")} for intf in each_ins["interfaces"]["interface"]]
+ vrf_interface["members"] = {"interfaces": interfaces}
+
+ vrf_interfaces.append(vrf_interface)
+ return vrf_interfaces
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py
new file mode 100644
index 00000000..51aec656
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/facts/vxlans/vxlans.py
@@ -0,0 +1,207 @@
+#
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The sonic vxlans 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vxlans.vxlans import VxlansArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+GET = "get"
+
+
+class VxlansFacts(object):
+ """ The sonic vxlans fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = VxlansArgs.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 vxlans
+ :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
+
+ if not data:
+ # typically data is populated from the current device configuration
+ # data = connection.get('show running-config | section ^interface')
+ # using mock data instead
+ data = self.get_all_vxlans()
+
+ objs = list()
+ for conf in data:
+ if conf:
+ obj = self.render_config(self.generated_spec, conf)
+ if obj:
+ objs.append(obj)
+
+ ansible_facts['ansible_network_resources'].pop('vxlans', None)
+ facts = {}
+ if objs:
+ facts['vxlans'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ if params:
+ facts['vxlans'].extend(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
+ """
+ return conf
+
+ def get_all_vxlans(self):
+ vxlans = []
+ vxlan_tunnels = []
+ vxlan_vlan_map = []
+
+ vxlans_tunnels_vlan_map = self.get_all_vxlans_tunnels_vlan_map()
+ vxlans_evpn_nvo_list = self.get_all_vxlans_evpn_nvo_list()
+
+ if vxlans_tunnels_vlan_map.get('VXLAN_TUNNEL'):
+ if vxlans_tunnels_vlan_map['VXLAN_TUNNEL'].get('VXLAN_TUNNEL_LIST'):
+ vxlan_tunnels.extend(vxlans_tunnels_vlan_map['VXLAN_TUNNEL']['VXLAN_TUNNEL_LIST'])
+
+ if vxlans_tunnels_vlan_map.get('VXLAN_TUNNEL_MAP'):
+ if vxlans_tunnels_vlan_map['VXLAN_TUNNEL_MAP'].get('VXLAN_TUNNEL_MAP_LIST'):
+ vxlan_vlan_map.extend(vxlans_tunnels_vlan_map['VXLAN_TUNNEL_MAP']['VXLAN_TUNNEL_MAP_LIST'])
+
+ self.fill_tunnel_source_ip(vxlans, vxlan_tunnels, vxlans_evpn_nvo_list)
+ self.fill_vlan_map(vxlans, vxlan_vlan_map)
+
+ vxlan_vrf_list = self.get_all_vxlans_vrf_list()
+ self.fill_vrf_map(vxlans, vxlan_vrf_list)
+
+ return vxlans
+
+ def get_all_vxlans_vrf_list(self):
+ """Get all the vxlan tunnels and vlan map available """
+ request = [{"path": "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ if "sonic-vrf:VRF_LIST" in response[0][1]:
+ vxlan_vrf_list = response[0][1].get("sonic-vrf:VRF_LIST", {})
+
+ return vxlan_vrf_list
+
+ def get_all_vxlans_evpn_nvo_list(self):
+ """Get all the evpn nvo list available """
+ request = [{"path": "data/sonic-vxlan:sonic-vxlan/EVPN_NVO/EVPN_NVO_LIST", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ vxlans_evpn_nvo_list = []
+ if "sonic-vxlan:EVPN_NVO_LIST" in response[0][1]:
+ vxlans_evpn_nvo_list = response[0][1].get("sonic-vxlan:EVPN_NVO_LIST", [])
+
+ return vxlans_evpn_nvo_list
+
+ def get_all_vxlans_tunnels_vlan_map(self):
+ """Get all the vxlan tunnels and vlan map available """
+ request = [{"path": "data/sonic-vxlan:sonic-vxlan", "method": GET}]
+ try:
+ response = edit_config(self._module, to_request(self._module, request))
+ except ConnectionError as exc:
+ self._module.fail_json(msg=str(exc), code=exc.code)
+
+ vxlans_tunnels_vlan_map = {}
+ if "sonic-vxlan:sonic-vxlan" in response[0][1]:
+ vxlans_tunnels_vlan_map = response[0][1].get("sonic-vxlan:sonic-vxlan", {})
+
+ return vxlans_tunnels_vlan_map
+
+ def fill_tunnel_source_ip(self, vxlans, vxlan_tunnels, vxlans_evpn_nvo_list):
+ for each_tunnel in vxlan_tunnels:
+ vxlan = dict()
+ vxlan['name'] = each_tunnel['name']
+ vxlan['source_ip'] = each_tunnel.get('src_ip', None)
+ vxlan['primary_ip'] = each_tunnel.get('primary_ip', None)
+ vxlan['evpn_nvo'] = None
+ if vxlan['source_ip']:
+ evpn_nvo = next((nvo_map['name'] for nvo_map in vxlans_evpn_nvo_list if nvo_map['source_vtep'] == vxlan['name']), None)
+ if evpn_nvo:
+ vxlan['evpn_nvo'] = evpn_nvo
+ vxlans.append(vxlan)
+
+ def fill_vlan_map(self, vxlans, vxlan_vlan_map):
+ for each_vlan_map in vxlan_vlan_map:
+ name = each_vlan_map['name']
+ matched_vtep = next((each_vxlan for each_vxlan in vxlans if each_vxlan['name'] == name), None)
+ if matched_vtep:
+ vni = int(each_vlan_map['vni'])
+ vlan = int(each_vlan_map['vlan'][4:])
+ vlan_map = matched_vtep.get('vlan_map')
+ if vlan_map:
+ vlan_map.append(dict({'vni': vni, 'vlan': vlan}))
+ else:
+ matched_vtep['vlan_map'] = [dict({'vni': vni, 'vlan': vlan})]
+
+ def fill_vrf_map(self, vxlans, vxlan_vrf_list):
+ for each_vrf in vxlan_vrf_list:
+ vni = each_vrf.get('vni', None)
+ if vni is None:
+ continue
+
+ matched_vtep = None
+ for each_vxlan in vxlans:
+ for each_vlan in each_vxlan.get('vlan_map', []):
+ if vni == each_vlan['vni']:
+ matched_vtep = each_vxlan
+
+ if matched_vtep:
+ vni = int(each_vrf['vni'])
+ vrf = each_vrf['vrf_name']
+ vrf_map = matched_vtep.get('vrf_map')
+ if vrf_map:
+ vrf_map.append(dict({'vni': vni, 'vrf': vrf}))
+ else:
+ matched_vtep['vrf_map'] = [dict({'vni': vni, 'vrf': vrf})]
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py
new file mode 100644
index 00000000..77a63d42
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/sonic.py
@@ -0,0 +1,155 @@
+# 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.
+#
+# (c) 2016 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 json
+import re
+
+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_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, ConfigLine
+
+_DEVICE_CONFIGS = {}
+STANDARD_ETH_REGEXP = r"Eth\d+(/\d+)+"
+PATTERN = re.compile(STANDARD_ETH_REGEXP)
+
+
+def get_connection(module):
+ if hasattr(module, "_sonic_connection"):
+ return module._sonic_connection
+
+ capabilities = get_capabilities(module)
+ network_api = capabilities.get("network_api")
+ if network_api in ["cliconf", "sonic_rest"]:
+ module._sonic_connection = Connection(module._socket_path)
+ else:
+ module.fail_json(msg="Invalid connection type %s" % network_api)
+
+ return module._sonic_connection
+
+
+def get_capabilities(module):
+ if hasattr(module, "_sonic_capabilities"):
+ return module._sonic_capabilities
+ try:
+ capabilities = Connection(module._socket_path).get_capabilities()
+ except ConnectionError as exc:
+ module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
+ module._sonic_capabilities = json.loads(capabilities)
+ return module._sonic_capabilities
+
+
+def get_config(module, flags=None):
+ flags = to_list(flags)
+ flag_str = " ".join(flags)
+
+ try:
+ return _DEVICE_CONFIGS[flag_str]
+ except KeyError:
+ connection = get_connection(module)
+ try:
+ out = connection.get_config(flags=flags)
+ except ConnectionError as exc:
+ module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
+ cfg = to_text(out, errors="surrogate_then_replace").strip()
+ _DEVICE_CONFIGS[flag_str] = cfg
+ return cfg
+
+
+def get_sublevel_config(running_config, module):
+ contents = list()
+ current_config_contents = list()
+ running_config = NetworkConfig(contents=running_config, indent=1)
+ obj = running_config.get_object(module.params['parents'])
+ if obj:
+ contents = obj.children
+ parents = module.params['parents']
+ if parents[2:]:
+ temp = 1
+ for count, item in enumerate(parents[2:], start=2):
+ item = ' ' * temp + item
+ temp = temp + 1
+ parents[count] = item
+ contents[:0] = parents
+ indent = 0
+ for c in contents:
+ if isinstance(c, str):
+ if c in parents:
+ current_config_contents.append(c.rjust(len(c) + indent, ' '))
+ if c not in parents:
+ c = ' ' * (len(parents) - 1) + c
+ current_config_contents.append(c.rjust(len(c) + indent, ' '))
+ if isinstance(c, ConfigLine):
+ current_config_contents.append(c.raw)
+ indent = 1
+ sublevel_config = '\n'.join(current_config_contents)
+ return sublevel_config
+
+
+def run_commands(module, commands, check_rc=True):
+ connection = get_connection(module)
+ try:
+ return connection.run_commands(commands=commands, check_rc=check_rc)
+ except ConnectionError as exc:
+ module.fail_json(msg=to_text(exc))
+
+
+def edit_config(module, commands, skip_code=None):
+ connection = get_connection(module)
+
+ # Start: This is to convert interface name from Eth1/1 to Eth1%2f1
+ for request in commands:
+ # This check is to differenciate between requests and commands
+ if type(request) is dict:
+ url = request.get("path", None)
+ if url:
+ request["path"] = update_url(url)
+ # End
+ return connection.edit_config(commands)
+
+
+def update_url(url):
+ match = re.search(STANDARD_ETH_REGEXP, url)
+ ret_url = url
+ if match:
+ interface_name = match.group()
+ interface_name = interface_name.replace("/", "%2f")
+ ret_url = PATTERN.sub(interface_name, url)
+ return ret_url
+
+
+def to_request(module, requests):
+ transform = ComplexList(dict(path=dict(key=True), method=dict(), data=dict(type='dict')), module)
+ return transform(to_list(requests))
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py
new file mode 100644
index 00000000..7471bcb1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/bgp_utils.py
@@ -0,0 +1,611 @@
+#
+# -*- 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 sonic bgp 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ normalize_interface_name,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp.bgp import BgpArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+afi_safi_types_map = {
+ 'openconfig-bgp-types:IPV4_UNICAST': 'ipv4_unicast',
+ 'openconfig-bgp-types:IPV6_UNICAST': 'ipv6_unicast',
+ 'openconfig-bgp-types:L2VPN_EVPN': 'l2vpn_evpn',
+}
+GET = "get"
+network_instance_path = '/data/openconfig-network-instance:network-instances/network-instance'
+protocol_bgp_path = 'protocols/protocol=BGP,bgp/bgp'
+
+
+def get_all_vrfs(module):
+ """Get all VRF configurations available in chassis"""
+ all_vrfs = []
+ ret = []
+ request = {"path": "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST", "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ if 'sonic-vrf:VRF_LIST' in response[0][1]:
+ all_vrf_data = response[0][1].get('sonic-vrf:VRF_LIST', [])
+ if all_vrf_data:
+ for vrf_data in all_vrf_data:
+ all_vrfs.append(vrf_data['vrf_name'])
+
+ return all_vrfs
+
+
+def get_peergroups(module, vrf_name):
+ peer_groups = []
+ request_path = '%s=%s/protocols/protocol=BGP,bgp/bgp/peer-groups' % (network_instance_path, vrf_name)
+ request = {"path": request_path, "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ resp = response[0][1]
+ if 'openconfig-network-instance:peer-groups' in resp:
+ data = resp['openconfig-network-instance:peer-groups']
+ if 'peer-group' in data:
+ for peer_group in data['peer-group']:
+ pg = {}
+ if 'config' in peer_group:
+ if 'peer-group-name' in peer_group['config']:
+ pg.update({'name': peer_group['config']['peer-group-name']})
+ if 'description' in peer_group['config']:
+ pg.update({'pg_description': peer_group['config']['description']})
+ if 'disable-ebgp-connected-route-check' in peer_group['config']:
+ pg.update({'disable_connected_check': peer_group['config']['disable-ebgp-connected-route-check']})
+ if 'dont-negotiate-capability' in peer_group['config']:
+ pg.update({'dont_negotiate_capability': peer_group['config']['dont-negotiate-capability']})
+ if 'enforce-first-as' in peer_group['config']:
+ pg.update({'enforce_first_as': peer_group['config']['enforce-first-as']})
+ if 'enforce-multihop' in peer_group['config']:
+ pg.update({'enforce_multihop': peer_group['config']['enforce-multihop']})
+ local_as = {}
+ if 'local-as' in peer_group['config']:
+ local_as.update({'as': peer_group['config']['local-as']})
+ if 'local-as-no-prepend' in peer_group['config']:
+ local_as.update({'no_prepend': peer_group['config']['local-as-no-prepend']})
+ if 'local-as-replace-as' in peer_group['config']:
+ local_as.update({'replace_as': peer_group['config']['local-as-replace-as']})
+ if 'override-capability' in peer_group['config']:
+ pg.update({'override_capability': peer_group['config']['override-capability']})
+ if 'shutdown-message' in peer_group['config']:
+ pg.update({'shutdown_msg': peer_group['config']['shutdown-message']})
+ if 'solo-peer' in peer_group['config']:
+ pg.update({'solo': peer_group['config']['solo-peer']})
+ if 'strict-capability-match' in peer_group['config']:
+ pg.update({'strict_capability_match': peer_group['config']['strict-capability-match']})
+ if 'ttl-security-hops' in peer_group['config']:
+ pg.update({'ttl_security': peer_group['config']['ttl-security-hops']})
+ auth_pwd = {}
+ if 'auth-password' in peer_group and 'config' in peer_group['auth-password']:
+ if 'encrypted' in peer_group['auth-password']['config']:
+ auth_pwd.update({'encrypted': peer_group['auth-password']['config']['encrypted']})
+ if 'password' in peer_group['auth-password']['config']:
+ auth_pwd.update({'pwd': peer_group['auth-password']['config']['password']})
+ bfd = {}
+ if 'enable-bfd' in peer_group and 'config' in peer_group['enable-bfd']:
+ if 'enabled' in peer_group['enable-bfd']['config']:
+ bfd.update({'enabled': peer_group['enable-bfd']['config']['enabled']})
+ if 'check-control-plane-failure' in peer_group['enable-bfd']['config']:
+ bfd.update({'check_failure': peer_group['enable-bfd']['config']['check-control-plane-failure']})
+ if 'bfd-profile' in peer_group['enable-bfd']['config']:
+ bfd.update({'profile': peer_group['enable-bfd']['config']['bfd-profile']})
+ ebgp_multihop = {}
+ if 'ebgp-multihop' in peer_group and 'config' in peer_group['ebgp-multihop']:
+ if 'enabled' in peer_group['ebgp-multihop']['config']:
+ ebgp_multihop.update({'enabled': peer_group['ebgp-multihop']['config']['enabled']})
+ if 'multihop-ttl' in peer_group['ebgp-multihop']['config']:
+ ebgp_multihop.update({'multihop_ttl': peer_group['ebgp-multihop']['config']['multihop-ttl']})
+ if 'transport' in peer_group and 'config' in peer_group['transport']:
+ if 'local-address' in peer_group['transport']['config']:
+ pg.update({'local_address': peer_group['transport']['config']['local-address']})
+ if 'passive-mode' in peer_group['transport']['config']:
+ pg.update({'passive': peer_group['transport']['config']['passive-mode']})
+ if 'timers' in peer_group and 'config' in peer_group['timers']:
+ if 'minimum-advertisement-interval' in peer_group['timers']['config']:
+ pg.update({'advertisement_interval': peer_group['timers']['config']['minimum-advertisement-interval']})
+ timers = {}
+ if 'hold-time' in peer_group['timers']['config']:
+ timers.update({'holdtime': peer_group['timers']['config']['hold-time']})
+ if 'keepalive-interval' in peer_group['timers']['config']:
+ timers.update({'keepalive': peer_group['timers']['config']['keepalive-interval']})
+ if 'connect-retry' in peer_group['timers']['config']:
+ timers.update({'connect_retry': peer_group['timers']['config']['connect-retry']})
+ capability = {}
+ if 'config' in peer_group and 'capability-dynamic' in peer_group['config']:
+ capability.update({'dynamic': peer_group['config']['capability-dynamic']})
+ if 'config' in peer_group and 'capability-extended-nexthop' in peer_group['config']:
+ capability.update({'extended_nexthop': peer_group['config']['capability-extended-nexthop']})
+ remote_as = {}
+ if 'config' in peer_group and 'peer-as' in peer_group['config']:
+ remote_as.update({'peer_as': peer_group['config']['peer-as']})
+ if 'config' in peer_group and 'peer-type' in peer_group['config']:
+ remote_as.update({'peer_type': peer_group['config']['peer-type'].lower()})
+ afis = []
+ if 'afi-safis' in peer_group and 'afi-safi' in peer_group['afi-safis']:
+ for each in peer_group['afi-safis']['afi-safi']:
+ samp = {}
+ if 'afi-safi-name' in each and each['afi-safi-name']:
+ tmp = each['afi-safi-name'].split(':')
+ if tmp:
+ split_tmp = tmp[1].split('_')
+ if split_tmp:
+ afi = split_tmp[0].lower()
+ safi = split_tmp[1].lower()
+ if afi and safi:
+ samp.update({'afi': afi})
+ samp.update({'safi': safi})
+ if 'config' in each and 'enabled' in each['config']:
+ samp.update({'activate': each['config']['enabled']})
+ if 'allow-own-as' in each and 'config' in each['allow-own-as']:
+ allowas_in = {}
+ allowas_conf = each['allow-own-as']['config']
+ if 'origin' in allowas_conf and allowas_conf['origin']:
+ allowas_in.update({'origin': allowas_conf['origin']})
+ elif 'as-count' in allowas_conf and allowas_conf['as-count']:
+ allowas_in.update({'value': allowas_conf['as-count']})
+ if allowas_in:
+ samp.update({'allowas_in': allowas_in})
+ if 'ipv4-unicast' in each:
+ if 'config' in each['ipv4-unicast']:
+ ip_afi_conf = each['ipv4-unicast']['config']
+ ip_afi = update_bgp_nbr_pg_ip_afi_dict(ip_afi_conf)
+ if ip_afi:
+ samp.update({'ip_afi': ip_afi})
+ if 'prefix-limit' in each['ipv4-unicast'] and 'config' in each['ipv4-unicast']['prefix-limit']:
+ pfx_lmt_conf = each['ipv4-unicast']['prefix-limit']['config']
+ prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(pfx_lmt_conf)
+ if prefix_limit:
+ samp.update({'prefix_limit': prefix_limit})
+ elif 'ipv6-unicast' in each:
+ if 'config' in each['ipv6-unicast']:
+ ip_afi_conf = each['ipv6-unicast']['config']
+ ip_afi = update_bgp_nbr_pg_ip_afi_dict(ip_afi_conf)
+ if ip_afi:
+ samp.update({'ip_afi': ip_afi})
+ if 'prefix-limit' in each['ipv6-unicast'] and 'config' in each['ipv6-unicast']['prefix-limit']:
+ pfx_lmt_conf = each['ipv6-unicast']['prefix-limit']['config']
+ prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(pfx_lmt_conf)
+ if prefix_limit:
+ samp.update({'prefix_limit': prefix_limit})
+ elif 'l2vpn-evpn' in each and 'prefix-limit' in each['l2vpn-evpn'] and 'config' in each['l2vpn-evpn']['prefix-limit']:
+ pfx_lmt_conf = each['l2vpn-evpn']['prefix-limit']['config']
+ prefix_limit = update_bgp_nbr_pg_prefix_limit_dict(pfx_lmt_conf)
+ if prefix_limit:
+ samp.update({'prefix_limit': prefix_limit})
+ if 'prefix-list' in each and 'config' in each['prefix-list']:
+ pfx_lst_conf = each['prefix-list']['config']
+ if 'import-policy' in pfx_lst_conf and pfx_lst_conf['import-policy']:
+ samp.update({'prefix_list_in': pfx_lst_conf['import-policy']})
+ if 'export-policy' in pfx_lst_conf and pfx_lst_conf['export-policy']:
+ samp.update({'prefix_list_out': pfx_lst_conf['export-policy']})
+ if samp:
+ afis.append(samp)
+ if auth_pwd:
+ pg.update({'auth_pwd': auth_pwd})
+ if bfd:
+ pg.update({'bfd': bfd})
+ if ebgp_multihop:
+ pg.update({'ebgp_multihop': ebgp_multihop})
+ if local_as:
+ pg.update({'local_as': local_as})
+ if timers:
+ pg.update({'timers': timers})
+ if capability:
+ pg.update({'capability': capability})
+ if remote_as:
+ pg.update({'remote_as': remote_as})
+ if afis and len(afis) > 0:
+ afis_dict = {}
+ afis_dict.update({'afis': afis})
+ pg.update({'address_family': afis_dict})
+ peer_groups.append(pg)
+
+ return peer_groups
+
+
+def update_bgp_nbr_pg_ip_afi_dict(ip_afi_conf):
+ ip_afi = {}
+ if 'default-policy-name' in ip_afi_conf and ip_afi_conf['default-policy-name']:
+ ip_afi.update({'default_policy_name': ip_afi_conf['default-policy-name']})
+ if 'send-default-route' in ip_afi_conf and ip_afi_conf['send-default-route']:
+ ip_afi.update({'send_default_route': ip_afi_conf['send-default-route']})
+
+ return ip_afi
+
+
+def update_bgp_nbr_pg_prefix_limit_dict(pfx_lmt_conf):
+ prefix_limit = {}
+ if 'max-prefixes' in pfx_lmt_conf and pfx_lmt_conf['max-prefixes']:
+ prefix_limit.update({'max_prefixes': pfx_lmt_conf['max-prefixes']})
+ if 'prevent-teardown' in pfx_lmt_conf and pfx_lmt_conf['prevent-teardown']:
+ prefix_limit.update({'prevent_teardown': pfx_lmt_conf['prevent-teardown']})
+ if 'warning-threshold-pct' in pfx_lmt_conf and pfx_lmt_conf['warning-threshold-pct']:
+ prefix_limit.update({'warning_threshold': pfx_lmt_conf['warning-threshold-pct']})
+ if 'restart-timer' in pfx_lmt_conf and pfx_lmt_conf['restart-timer']:
+ prefix_limit.update({'restart_timer': pfx_lmt_conf['restart-timer']})
+
+ return prefix_limit
+
+
+def get_ip_afi_cfg_payload(ip_afi):
+ ip_afi_cfg = {}
+
+ if ip_afi.get('default_policy_name', None) is not None:
+ default_policy_name = ip_afi['default_policy_name']
+ ip_afi_cfg.update({'default-policy-name': default_policy_name})
+ if ip_afi.get('send_default_route', None) is not None:
+ send_default_route = ip_afi['send_default_route']
+ ip_afi_cfg.update({'send-default-route': send_default_route})
+
+ return ip_afi_cfg
+
+
+def get_prefix_limit_payload(prefix_limit):
+ pfx_lmt_cfg = {}
+
+ if prefix_limit.get('max_prefixes', None) is not None:
+ max_prefixes = prefix_limit['max_prefixes']
+ pfx_lmt_cfg.update({'max-prefixes': max_prefixes})
+ if prefix_limit.get('prevent_teardown', None) is not None:
+ prevent_teardown = prefix_limit['prevent_teardown']
+ pfx_lmt_cfg.update({'prevent-teardown': prevent_teardown})
+ if prefix_limit.get('warning_threshold', None) is not None:
+ warning_threshold = prefix_limit['warning_threshold']
+ pfx_lmt_cfg.update({'warning-threshold-pct': warning_threshold})
+ if prefix_limit.get('restart_timer', None) is not None:
+ restart_timer = prefix_limit['restart_timer']
+ pfx_lmt_cfg.update({'restart-timer': restart_timer})
+
+ return pfx_lmt_cfg
+
+
+def get_all_bgp_af_redistribute(module, vrfs, af_redis_params_map):
+ """Get all BGP Global Address Family Redistribute configurations available in chassis"""
+ all_af_redis_data = []
+ ret_redis_data = []
+ for vrf_name in vrfs:
+ af_redis_data = {}
+ request_path = '%s=%s/table-connections' % (network_instance_path, vrf_name)
+ request = {"path": request_path, "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ if "openconfig-network-instance:table-connections" in response[0][1]:
+ af_redis_data.update({vrf_name: response[0][1]['openconfig-network-instance:table-connections']})
+
+ if af_redis_data:
+ all_af_redis_data.append(af_redis_data)
+
+ if all_af_redis_data:
+ for vrf_name in vrfs:
+ key = vrf_name
+ val = next((af_redis_data for af_redis_data in all_af_redis_data if vrf_name in af_redis_data), None)
+ if not val:
+ continue
+
+ val = val[vrf_name]
+ redis_data = val.get('table-connection', [])
+ if not redis_data:
+ continue
+ filtered_redis_data = []
+ for e_cfg in redis_data:
+ af_redis_data = get_from_params_map(af_redis_params_map, e_cfg)
+ if af_redis_data:
+ filtered_redis_data.append(af_redis_data)
+
+ if filtered_redis_data:
+ ret_redis_data.append({key: filtered_redis_data})
+
+ return ret_redis_data
+
+
+def get_all_bgp_globals(module, vrfs):
+ """Get all BGP configurations available in chassis"""
+ all_bgp_globals = []
+ for vrf_name in vrfs:
+ get_path = '%s=%s/%s/global' % (network_instance_path, vrf_name, protocol_bgp_path)
+ request = {"path": get_path, "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+ for resp in response:
+ if "openconfig-network-instance:global" in resp[1]:
+ bgp_data = {'global': resp[1].get("openconfig-network-instance:global", {})}
+ bgp_data.update({'vrf_name': vrf_name})
+ all_bgp_globals.append(bgp_data)
+ return all_bgp_globals
+
+
+def get_bgp_global_af_data(data, af_params_map):
+ ret_af_data = {}
+ for key, val in data.items():
+ if key == 'global':
+ if 'afi-safis' in val and 'afi-safi' in val['afi-safis']:
+ global_af_data = []
+ raw_af_data = val['afi-safis']['afi-safi']
+ for each_af_data in raw_af_data:
+ af_data = get_from_params_map(af_params_map, each_af_data)
+ if af_data:
+ global_af_data.append(af_data)
+ ret_af_data.update({'address_family': global_af_data})
+ if 'config' in val and 'as' in val['config']:
+ as_val = val['config']['as']
+ ret_af_data.update({'bgp_as': as_val})
+ if key == 'vrf_name':
+ ret_af_data.update({'vrf_name': val})
+ return ret_af_data
+
+
+def get_bgp_global_data(data, global_params_map):
+ bgp_data = {}
+ for key, val in data.items():
+ if key == 'global':
+ global_data = get_from_params_map(global_params_map, val)
+ bgp_data.update(global_data)
+ if key == 'vrf_name':
+ bgp_data.update({'vrf_name': val})
+ return bgp_data
+
+
+def get_from_params_map(params_map, data):
+ ret_data = {}
+ for want_key, config_key in params_map.items():
+ tmp_data = {}
+ for key, val in data.items():
+ if key == 'config':
+ for k, v in val.items():
+ if k == config_key:
+ val_data = val[config_key]
+ ret_data.update({want_key: val_data})
+ if config_key == 'afi-safi-name':
+ ret_data.pop(want_key)
+ for type_k, type_val in afi_safi_types_map.items():
+ if type_k == val_data:
+ afi_safi = type_val.split('_')
+ val_data = afi_safi[0]
+ ret_data.update({'safi': afi_safi[1]})
+ ret_data.update({want_key: val_data})
+ break
+ else:
+ if key == 'timers' and ('config' in val or 'state' in val):
+ tmp = {}
+ if key in ret_data:
+ tmp = ret_data[key]
+ cfg = val['config'] if 'config' in val else val['state']
+ for k, v in cfg.items():
+ if k == config_key:
+ if k != 'minimum-advertisement-interval':
+ tmp.update({want_key: cfg[config_key]})
+ else:
+ ret_data.update({want_key: cfg[config_key]})
+ if tmp:
+ ret_data.update({key: tmp})
+
+ elif isinstance(config_key, list):
+ i = 0
+ if key == config_key[0]:
+ if key == 'afi-safi':
+ cfg_data = config_key[1]
+ for itm in afi_safi_types_map:
+ if cfg_data in itm:
+ afi_safi = itm[cfg_data].split('_')
+ cfg_data = afi_safi[0]
+ ret_data.update({'safi': afi_safi[1]})
+ ret_data.update({want_key: cfg_data})
+ break
+ else:
+ cfg_data = {key: val}
+ for cfg_key in config_key:
+ if cfg_key == 'config':
+ continue
+ new_data = None
+
+ if cfg_key in cfg_data:
+ new_data = cfg_data[cfg_key]
+ elif isinstance(cfg_data, dict) and 'config' in cfg_data:
+ if cfg_key in cfg_data['config']:
+ new_data = cfg_data['config'][cfg_key]
+
+ if new_data is not None:
+ cfg_data = new_data
+ else:
+ break
+ else:
+ ret_data.update({want_key: cfg_data})
+ else:
+ if key == config_key and val:
+ if config_key != 'afi-safi-name' and config_key != 'timers':
+ cfg_data = val
+ ret_data.update({want_key: cfg_data})
+
+ return ret_data
+
+
+def get_bgp_data(module, global_params_map):
+ vrf_list = get_all_vrfs(module)
+ data = get_all_bgp_globals(module, vrf_list)
+
+ objs = []
+ # operate on a collection of resource x
+ for conf in data:
+ if conf:
+ obj = get_bgp_global_data(conf, global_params_map)
+ if obj:
+ objs.append(obj)
+ return objs
+
+
+def get_bgp_af_data(module, af_params_map):
+ vrf_list = get_all_vrfs(module)
+ data = get_all_bgp_globals(module, vrf_list)
+
+ objs = []
+ # operate on a collection of resource x
+ for conf in data:
+ if conf:
+ obj = get_bgp_global_af_data(conf, af_params_map)
+ if obj:
+ objs.append(obj)
+
+ return objs
+
+
+def get_bgp_as(module, vrf_name):
+ as_val = None
+ get_path = '%s=%s/%s/global/config' % (network_instance_path, vrf_name, protocol_bgp_path)
+ request = {"path": get_path, "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ resp = response[0][1]
+ if "openconfig-network-instance:config" in resp and 'as' in resp['openconfig-network-instance:config']:
+ as_val = resp['openconfig-network-instance:config']['as']
+ return as_val
+
+
+def get_bgp_neighbors(module, vrf_name):
+ neighbors_data = None
+ get_path = '%s=%s/%s/neighbors' % (network_instance_path, vrf_name, protocol_bgp_path)
+ request = {"path": get_path, "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ resp = response[0][1]
+ if "openconfig-network-instance:neighbors" in resp:
+ neighbors_data = resp['openconfig-network-instance:neighbors']
+
+ return neighbors_data
+
+
+def get_all_bgp_neighbors(module):
+ vrf_list = get_all_vrfs(module)
+ """Get all BGP neighbor configurations available in chassis"""
+ all_bgp_neighbors = []
+
+ for vrf_name in vrf_list:
+ neighbors_cfg = {}
+
+ bgp_as = get_bgp_as(module, vrf_name)
+ if bgp_as:
+ neighbors_cfg['bgp_as'] = bgp_as
+ neighbors_cfg['vrf_name'] = vrf_name
+ else:
+ continue
+
+ neighbors = get_bgp_neighbors(module, vrf_name)
+ if neighbors:
+ neighbors_cfg['neighbors'] = neighbors
+
+ if neighbors_cfg:
+ all_bgp_neighbors.append(neighbors_cfg)
+
+ return all_bgp_neighbors
+
+
+def get_undefined_bgps(want, have, check_neighbors=None):
+ if check_neighbors is None:
+ check_neighbors = False
+
+ undefined_resources = []
+
+ if not want:
+ return undefined_resources
+
+ if not have:
+ have = []
+
+ for want_conf in want:
+ undefined = {}
+ want_bgp_as = want_conf['bgp_as']
+ want_vrf = want_conf['vrf_name']
+ have_conf = next((conf for conf in have if (want_bgp_as == conf['bgp_as'] and want_vrf == conf['vrf_name'])), None)
+ if not have_conf:
+ undefined['bgp_as'] = want_bgp_as
+ undefined['vrf_name'] = want_vrf
+ undefined_resources.append(undefined)
+ if check_neighbors and have_conf:
+ want_neighbors = want_conf.get('neighbors', [])
+ have_neighbors = have_conf.get('neighbors', [])
+ undefined_neighbors = get_undefined_neighbors(want_neighbors, have_neighbors)
+ if undefined_neighbors:
+ undefined['bgp_as'] = want_bgp_as
+ undefined['vrf_name'] = want_vrf
+ undefined['neighbors'] = undefined_neighbors
+ undefined_resources.append(undefined)
+
+ return undefined_resources
+
+
+def get_undefined_neighbors(want, have):
+ undefined_neighbors = []
+ if not want:
+ return undefined_neighbors
+
+ if not have:
+ have = []
+
+ for want_neighbor in want:
+ want_neighbor_val = want_neighbor['neighbor']
+ have_neighbor = next((conf for conf in have if want_neighbor_val == conf['neighbor']), None)
+ if not have_neighbor:
+ undefined_neighbors.append({'neighbor': want_neighbor_val})
+
+ return undefined_neighbors
+
+
+def validate_bgps(module, want, have):
+ validate_bgp_resources(module, want, have)
+
+
+def validate_bgp_neighbors(module, want, have):
+ validate_bgp_resources(module, want, have, check_neighbors=True)
+
+
+def validate_bgp_resources(module, want, have, check_neighbors=None):
+ undefined_resources = get_undefined_bgps(want, have, check_neighbors)
+ if undefined_resources:
+ err = "Resource not found! {res}".format(res=undefined_resources)
+ module.fail_json(msg=err, code=404)
+
+
+def normalize_neighbors_interface_name(want, module):
+ if want:
+ for conf in want:
+ neighbors = conf.get('neighbors', None)
+ if neighbors:
+ normalize_interface_name(neighbors, module, 'neighbor')
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py
new file mode 100644
index 00000000..a7f6e906
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/interfaces_util.py
@@ -0,0 +1,55 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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
+
+import traceback
+import json
+
+from ansible.module_utils._text import to_native
+
+try:
+ import jinja2
+ HAS_LIB = True
+except Exception as e:
+ HAS_LIB = False
+ ERR_MSG = to_native(e)
+ LIB_IMP_ERR = traceback.format_exc()
+
+
+# To create Loopback, VLAN interfaces
+def build_interfaces_create_request(interface_name):
+ url = "data/openconfig-interfaces:interfaces"
+ method = "PATCH"
+ payload_template = """{"openconfig-interfaces:interfaces": {"interface": [{"name": "{{interface_name}}", "config": {"name": "{{interface_name}}"}}]}}"""
+ input_data = {"interface_name": interface_name}
+ env = jinja2.Environment(autoescape=False)
+ t = env.from_string(payload_template)
+ intended_payload = t.render(input_data)
+ ret_payload = json.loads(intended_payload)
+ request = {"path": url,
+ "method": method,
+ "data": ret_payload}
+ return request
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py
new file mode 100644
index 00000000..0d6e6d1a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/module_utils/network/sonic/utils/utils.py
@@ -0,0 +1,511 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# utils
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import re
+import json
+import ast
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ is_masklen,
+ to_netmask,
+ remove_empties
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
+ to_request,
+ edit_config
+)
+from ansible.module_utils.connection import ConnectionError
+
+DEFAULT_TEST_KEY = {'config': {'name': ''}}
+GET = 'get'
+
+intf_naming_mode = ""
+
+
+def get_diff(base_data, compare_with_data, test_keys=None, is_skeleton=None):
+ diff = []
+ if is_skeleton is None:
+ is_skeleton = False
+
+ test_keys = normalize_testkeys(test_keys)
+
+ if isinstance(base_data, list) and isinstance(compare_with_data, list):
+ dict_diff = get_diff_dict({"config": base_data}, {"config": compare_with_data}, test_keys, is_skeleton)
+ diff = dict_diff.get("config", [])
+
+ else:
+ new_base, new_compare = convert_dict_to_single_entry_list(base_data, compare_with_data, test_keys)
+ diff = get_diff_dict(new_base, new_compare, test_keys, is_skeleton)
+ if diff:
+ diff = convert_single_entry_list_to_dict(diff)
+ else:
+ diff = {}
+
+ return diff
+
+
+def get_diff_dict(base_data, compare_with_data, test_keys=None, is_skeleton=None):
+ if is_skeleton is None:
+ is_skeleton = False
+
+ if test_keys is None:
+ test_keys = []
+
+ if not base_data:
+ return base_data
+
+ planned_set = set(base_data.keys())
+ discovered_set = set(compare_with_data.keys())
+ intersect_set = planned_set.intersection(discovered_set)
+ changed_dict = {}
+ has_dict_item = None
+ added_set = planned_set - intersect_set
+ # Keys part of added are new and put into changed_dict
+ if added_set:
+ for key in added_set:
+ if is_skeleton:
+ changed_dict[key] = base_data[key]
+ elif base_data[key] is not None:
+ if isinstance(base_data[key], dict):
+ val_dict = remove_empties(base_data[key])
+ if val_dict:
+ changed_dict[key] = remove_empties(base_data[key])
+ elif isinstance(base_data[key], list):
+ val_list = remove_empties_from_list(base_data[key])
+ if val_list:
+ changed_dict[key] = remove_empties_from_list(base_data[key])
+ else:
+ changed_dict[key] = base_data[key]
+ for key in intersect_set:
+ has_dict_item = False
+ value = base_data[key]
+ if isinstance(value, list):
+ p_list = base_data[key] if key in base_data else []
+ d_list = compare_with_data[key] if key in compare_with_data else []
+ keys_to_compare = next((test_key_item[key] for test_key_item in test_keys if key in test_key_item), None)
+ changed_list = []
+ if p_list and d_list:
+ for p_list_item in p_list:
+ matched = False
+ has_diff = False
+ for d_list_item in d_list:
+ if (isinstance(p_list_item, dict) and isinstance(d_list_item, dict)):
+ if keys_to_compare:
+ key_matched_cnt = 0
+ test_keys_present_cnt = 0
+ common_keys = set(p_list_item).intersection(d_list_item)
+ for test_key in keys_to_compare:
+ if test_key in common_keys:
+ test_keys_present_cnt += 1
+ if p_list_item[test_key] == d_list_item[test_key]:
+ key_matched_cnt += 1
+ if key_matched_cnt and key_matched_cnt == test_keys_present_cnt:
+ remaining_keys = [test_key_item for test_key_item in test_keys if key not in test_key_item]
+ dict_diff = get_diff_dict(p_list_item, d_list_item, remaining_keys, is_skeleton)
+ matched = True
+ if dict_diff:
+ has_diff = True
+ for test_key in keys_to_compare:
+ dict_diff.update({test_key: p_list_item[test_key]})
+ break
+ else:
+ dict_diff = get_diff_dict(p_list_item, d_list_item, test_keys, is_skeleton)
+ if not dict_diff:
+ matched = True
+ break
+ else:
+ if p_list_item == d_list_item:
+ matched = True
+ break
+ if not matched:
+ if is_skeleton:
+ changed_list.append(p_list_item)
+ else:
+ if isinstance(p_list_item, dict):
+ val_dict = remove_empties(p_list_item)
+ if val_dict is not None:
+ changed_list.append(val_dict)
+ elif isinstance(p_list_item, list):
+ val_list = remove_empties_from_list(p_list_item)
+ if val_list is not None:
+ changed_list.append(val_list)
+ else:
+ if p_list_item is not None:
+ changed_list.append(p_list_item)
+ elif has_diff and dict_diff:
+ changed_list.append(dict_diff)
+ if changed_list:
+ changed_dict.update({key: changed_list})
+ elif p_list and (not d_list):
+ changed_dict[key] = p_list
+ elif (isinstance(value, dict) and isinstance(compare_with_data[key], dict)):
+ dict_diff = get_diff_dict(base_data[key], compare_with_data[key], test_keys, is_skeleton)
+ if dict_diff:
+ changed_dict[key] = dict_diff
+ elif value is not None:
+ if not is_skeleton:
+ if compare_with_data[key] != base_data[key]:
+ changed_dict[key] = base_data[key]
+ return changed_dict
+
+
+def convert_dict_to_single_entry_list(base_data, compare_with_data, test_keys):
+ # if it is dict comparision convert dict into single entry list by adding 'config' as key
+ new_base = {'config': [base_data]}
+ new_compare = {'config': [compare_with_data]}
+
+ # get testkey of 'config'
+ config_testkey = None
+ for item in test_keys:
+ for key, val in item.items():
+ if key == 'config':
+ config_testkey = list(val)[0]
+ break
+ if config_testkey:
+ break
+ # if testkey of 'config' is not in base data, introduce single entry list
+ # with 'temp_key' as config testkey and base_data as data.
+ if config_testkey and base_data and config_testkey not in base_data:
+ new_base = {'config': [{config_testkey: 'temp_key', 'data': base_data}]}
+ new_compare = {'config': [{config_testkey: 'temp_key', 'data': compare_with_data}]}
+
+ return new_base, new_compare
+
+
+def convert_single_entry_list_to_dict(diff):
+ diff = diff['config'][0]
+ if 'data' in diff:
+ diff = diff['data']
+ return diff
+
+
+def normalize_testkeys(test_keys):
+ if test_keys is None:
+ test_keys = []
+
+ if not any(test_key_item for test_key_item in test_keys if "config" in test_key_item):
+ test_keys.append(DEFAULT_TEST_KEY)
+
+ return test_keys
+
+
+def update_states(commands, state):
+ ret_list = list()
+ if commands:
+ if isinstance(commands, list):
+ for command in commands:
+ ret = command.copy()
+ ret.update({"state": state})
+ ret_list.append(ret)
+ elif isinstance(commands, dict):
+ ret_list.append(commands.copy())
+ ret_list[0].update({"state": state})
+ return ret_list
+
+
+def dict_to_set(sample_dict):
+ # Generate a set with passed dictionary for comparison
+ test_dict = dict()
+ if isinstance(sample_dict, dict):
+ for k, v in iteritems(sample_dict):
+ if v is not None:
+ if isinstance(v, list):
+ if isinstance(v[0], dict):
+ li = []
+ for each in v:
+ for key, value in iteritems(each):
+ if isinstance(value, list):
+ each[key] = tuple(value)
+ li.append(tuple(iteritems(each)))
+ v = tuple(li)
+ else:
+ v = tuple(v)
+ elif isinstance(v, dict):
+ li = []
+ for key, value in iteritems(v):
+ if isinstance(value, list):
+ v[key] = tuple(value)
+ li.extend(tuple(iteritems(v)))
+ v = tuple(li)
+ test_dict.update({k: v})
+ return_set = set(tuple(iteritems(test_dict)))
+ else:
+ return_set = set(sample_dict)
+ return return_set
+
+
+def validate_ipv4(value, module):
+ if value:
+ address = value.split("/")
+ if len(address) != 2:
+ module.fail_json(
+ msg="address format is <ipv4 address>/<mask>, got invalid format {0}".format(
+ value
+ )
+ )
+
+ if not is_masklen(address[1]):
+ module.fail_json(
+ msg="invalid value for mask: {0}, mask should be in range 0-32".format(
+ address[1]
+ )
+ )
+
+
+def validate_ipv6(value, module):
+ if value:
+ address = value.split("/")
+ if len(address) != 2:
+ module.fail_json(
+ msg="address format is <ipv6 address>/<mask>, got invalid format {0}".format(
+ value
+ )
+ )
+ else:
+ if not 0 <= int(address[1]) <= 128:
+ module.fail_json(
+ msg="invalid value for mask: {0}, mask should be in range 0-128".format(
+ address[1]
+ )
+ )
+
+
+def validate_n_expand_ipv4(module, want):
+ # Check if input IPV4 is valid IP and expand IPV4 with its subnet mask
+ ip_addr_want = want.get("address")
+ if len(ip_addr_want.split(" ")) > 1:
+ return ip_addr_want
+ validate_ipv4(ip_addr_want, module)
+ ip = ip_addr_want.split("/")
+ if len(ip) == 2:
+ ip_addr_want = "{0} {1}".format(ip[0], to_netmask(ip[1]))
+
+ return ip_addr_want
+
+
+def netmask_to_cidr(netmask):
+ bit_range = [128, 64, 32, 16, 8, 4, 2, 1]
+ count = 0
+ cidr = 0
+ netmask_list = netmask.split(".")
+ netmask_calc = [i for i in netmask_list if int(i) != 255 and int(i) != 0]
+ if netmask_calc:
+ netmask_calc_index = netmask_list.index(netmask_calc[0])
+ elif sum(list(map(int, netmask_list))) == 0:
+ return "32"
+ else:
+ return "24"
+ for each in bit_range:
+ if cidr == int(netmask.split(".")[2]):
+ if netmask_calc_index == 1:
+ return str(8 + count)
+ elif netmask_calc_index == 2:
+ return str(8 * 2 + count)
+ elif netmask_calc_index == 3:
+ return str(8 * 3 + count)
+ break
+ cidr += each
+ count += 1
+
+
+def remove_empties_from_list(config_list):
+ ret_config = []
+ if not config_list:
+ return ret_config
+ for config in config_list:
+ ret_config.append(remove_empties(config))
+ return ret_config
+
+
+def get_device_interface_naming_mode(module):
+ intf_naming_mode = ""
+ request = {"path": "data/sonic-device-metadata:sonic-device-metadata/DEVICE_METADATA/DEVICE_METADATA_LIST=localhost", "method": GET}
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ if 'sonic-device-metadata:DEVICE_METADATA_LIST' in response[0][1]:
+ device_meta_data = response[0][1].get('sonic-device-metadata:DEVICE_METADATA_LIST', [])
+ if device_meta_data:
+ intf_naming_mode = device_meta_data[0].get('intf_naming_mode', 'native')
+
+ return intf_naming_mode
+
+
+STANDARD_ETH_REGEXP = r"[e|E]th\s*\d+/\d+"
+NATIVE_ETH_REGEXP = r"[e|E]th*\d+$"
+NATIVE_MODE = "native"
+STANDARD_MODE = "standard"
+
+
+def find_intf_naming_mode(intf_name):
+ ret_intf_naming_mode = NATIVE_MODE
+
+ if re.search(STANDARD_ETH_REGEXP, intf_name):
+ ret_intf_naming_mode = STANDARD_MODE
+
+ return ret_intf_naming_mode
+
+
+def validate_intf_naming_mode(intf_name, module):
+ global intf_naming_mode
+ if intf_naming_mode == "":
+ intf_naming_mode = get_device_interface_naming_mode(module)
+
+ if intf_naming_mode != "":
+ ansible_intf_naming_mode = find_intf_naming_mode(intf_name)
+ if intf_naming_mode != ansible_intf_naming_mode:
+ err = "Interface naming mode configured on switch {naming_mode}, {intf_name} is not valid".format(naming_mode=intf_naming_mode, intf_name=intf_name)
+ module.fail_json(msg=err, code=400)
+
+
+def normalize_interface_name(configs, module, namekey=None):
+ if not namekey:
+ namekey = 'name'
+
+ if configs:
+ for conf in configs:
+ if conf.get(namekey, None):
+ conf[namekey] = get_normalize_interface_name(conf[namekey], module)
+
+
+def normalize_interface_name_list(configs, module):
+ norm_configs = []
+ if configs:
+ for conf in configs:
+ conf = get_normalize_interface_name(conf, module)
+ norm_configs.append(conf)
+
+ return norm_configs
+
+
+def get_normalize_interface_name(intf_name, module):
+ change_flag = False
+ # remove the space in the given string
+ ret_intf_name = re.sub(r"\s+", "", intf_name, flags=re.UNICODE)
+ ret_intf_name = ret_intf_name.capitalize()
+
+ # serach the numeric charecter(digit)
+ match = re.search(r"\d", ret_intf_name)
+ if match:
+ change_flag = True
+ start_pos = match.start()
+ name = ret_intf_name[0:start_pos]
+ intf_id = ret_intf_name[start_pos:]
+
+ # Interface naming mode affects only ethernet ports
+ if name.startswith("Eth"):
+ validate_intf_naming_mode(intf_name, module)
+
+ if ret_intf_name.startswith("Management") or ret_intf_name.startswith("Mgmt"):
+ name = "eth"
+ intf_id = "0"
+ elif re.search(STANDARD_ETH_REGEXP, ret_intf_name):
+ name = "Eth"
+ elif re.search(NATIVE_ETH_REGEXP, ret_intf_name):
+ name = "Ethernet"
+ elif name.startswith("Po"):
+ name = "PortChannel"
+ elif name.startswith("Vlan"):
+ name = "Vlan"
+ elif name.startswith("Lo"):
+ name = "Loopback"
+ else:
+ change_flag = False
+
+ ret_intf_name = name + intf_id
+
+ if not change_flag:
+ ret_intf_name = intf_name
+
+ return ret_intf_name
+
+
+def get_speed_from_breakout_mode(breakout_mode):
+ speed = None
+ speed_breakout_mode_map = {
+ "4x10G": "SPEED_10GB", "1x100G": "SPEED_100GB", "1x40G": "SPEED_40GB", "4x25G": "SPEED_25GB", "2x50G": "SPEED_50GB",
+ "1x400G": "SPEED_400GB", "4x100G": "SPEED_100GB", "4x50G": "SPEED_50GB", "2x100G": "SPEED_100GB", "2x200G": "SPEED_200GB"
+ }
+ if breakout_mode in speed_breakout_mode_map:
+ speed = speed_breakout_mode_map[breakout_mode]
+ return speed
+
+
+def get_breakout_mode(module, name):
+ response = None
+ mode = None
+ component_name = name
+ if "/" in name:
+ component_name = name.replace("/", "%2f")
+ url = "data/openconfig-platform:components/component=%s" % (component_name)
+ request = [{"path": url, "method": GET}]
+ try:
+ response = edit_config(module, to_request(module, request))
+ except ConnectionError as exc:
+ try:
+ json_obj = json.loads(str(exc).replace("'", '"'))
+ if json_obj and type(json_obj) is dict and 404 == json_obj['code']:
+ response = None
+ else:
+ module.fail_json(msg=str(exc), code=exc.code)
+ except Exception as err:
+ module.fail_json(msg=str(exc), code=exc.code)
+
+ if response and "openconfig-platform:component" in response[0][1]:
+ raw_port_breakout = response[0][1]['openconfig-platform:component'][0]
+ port_name = raw_port_breakout.get('name', None)
+ port_data = raw_port_breakout.get('port', None)
+ if port_name and port_data and 'openconfig-platform-port:breakout-mode' in port_data:
+ if 'groups' in port_data['openconfig-platform-port:breakout-mode']:
+ group = port_data['openconfig-platform-port:breakout-mode']['groups']['group'][0]
+ if 'config' in group:
+ cfg = group.get('config', None)
+ breakout_speed = cfg.get('breakout-speed', None)
+ num_breakouts = cfg.get('num-breakouts', None)
+ if breakout_speed and num_breakouts:
+ speed = breakout_speed.replace('openconfig-if-ethernet:SPEED_', '')
+ speed = speed.replace('GB', 'G')
+ mode = str(num_breakouts) + 'x' + speed
+ return mode
+
+
+def command_list_str_to_dict(module, warnings, cmd_list_in, exec_cmd=False):
+ cmd_list_out = []
+ for cmd in cmd_list_in:
+ cmd_out = dict()
+ nested_cmd_is_dict = False
+ if isinstance(cmd, dict):
+ cmd_out = cmd
+ else:
+ try:
+ nest_dict = ast.literal_eval(cmd)
+ nested_cmd_is_dict = isinstance(nest_dict, dict)
+ except Exception:
+ nested_cmd_is_dict = False
+
+ if nested_cmd_is_dict:
+ for key, value in nest_dict.items():
+ cmd_out[key] = value
+ else:
+ cmd_out = cmd
+
+ if exec_cmd and module.check_mode and not cmd_out['command'].startswith('show'):
+ warnings.append(
+ 'Only show commands are supported when using check mode, not '
+ 'executing %s' % cmd_out['command']
+ )
+ else:
+ cmd_list_out.append(cmd_out)
+
+ return cmd_list_out
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py
new file mode 100644
index 00000000..ddc71331
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_aaa.py
@@ -0,0 +1,215 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_aaa
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: sonic_aaa
+version_added: 1.1.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Abirami N (@abirami-n)
+short_description: Manage AAA and its parameters
+description:
+ - This module is used for configuration management of aaa parameters on devices running Enterprise SONiC.
+options:
+ config:
+ description:
+ - Specifies the aaa related configurations
+ type: dict
+ suboptions:
+ authentication:
+ description:
+ - Specifies the configurations required for aaa authentication
+ type: dict
+ suboptions:
+ data:
+ description:
+ - Specifies the data required for aaa authentication
+ type: dict
+ suboptions:
+ fail_through:
+ description:
+ - Specifies the state of failthrough
+ type: bool
+ local:
+ description:
+ - Enable or Disable local authentication
+ type: bool
+ group:
+ description:
+ - Specifies the method of aaa authentication
+ type: str
+ choices:
+ - ldap
+ - radius
+ - tacacs+
+
+ state:
+ description:
+ - Specifies the operation to be performed on the aaa parameters configured on the device.
+ - In case of merged, the input configuration will be merged with the existing aaa configuration on the device.
+ - In case of deleted the existing aaa configuration will be removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# do show aaa
+# AAA Authentication Information
+# ---------------------------------------------------------
+# failthrough : True
+# login-method : local
+
+- name: Delete aaa configurations
+ dellemc.enterprise_sonic.sonic_aaa:
+ config:
+ authentication:
+ data:
+ local: True
+ state: deleted
+
+# After state:
+# ------------
+#
+# do show aaa
+# AAA Authentication Information
+# ---------------------------------------------------------
+# failthrough : True
+# login-method :
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# do show aaa
+# AAA Authentication Information
+# ---------------------------------------------------------
+# failthrough : True
+# login-method : local
+
+- name: Delete aaa configurations
+ dellemc.enterprise_sonic.sonic_aaa:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+# do show aaa
+# AAA Authentication Information
+# ---------------------------------------------------------
+# failthrough :
+# login-method :
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+# do show aaa
+# AAA Authentication Information
+# ---------------------------------------------------------
+# failthrough : False
+# login-method :
+
+- name: Merge aaa configurations
+ dellemc.enterprise_sonic.sonic_aaa:
+ config:
+ authentication:
+ data:
+ local: true
+ fail_through: true
+ state: merged
+
+# After state:
+# ------------
+#
+# do show aaa
+# AAA Authentication Information
+# ---------------------------------------------------------
+# failthrough : True
+# login-method : local
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.aaa.aaa import AaaArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.aaa.aaa import Aaa
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=AaaArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Aaa(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_api.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_api.py
new file mode 100644
index 00000000..234603a0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_api.py
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+#
+# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2020 Dell Inc.
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+"""
+The module file for sonic_vlans
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_api
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Abirami N (@abirami-n)
+short_description: Manages REST operations on devices running Enterprise SONiC
+description:
+ - Manages REST operations on devices running Enterprise SONiC Distribution
+ by Dell Technologies. This module provides an implementation for working
+ with SONiC REST operations in a deterministic way.
+options:
+ url:
+ description:
+ - The HTTP path of the request after 'restconf/'.
+ type: path
+ required: true
+ body:
+ description:
+ - The body of the HTTP request/response to the web service which contains the payload.
+ type: raw
+ method:
+ description:
+ - The HTTP method of the request or response. Must be a valid method
+ accepted by the service that handles the request.
+ type: str
+ required: true
+ choices: ['GET', 'PUT', 'POST', 'PATCH', 'DELETE']
+ status_code:
+ description:
+ - A list of valid, numeric, HTTP status codes that signifies the success of a request.
+ type: list
+ elements: int
+ required: true
+"""
+EXAMPLES = """
+- name: Checks that you can connect (GET) to a page and it returns a status 200
+ dellemc.enterprise_sonic.sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet60
+ method: "GET"
+ status_code: 200
+
+- name: Appends data to an existing interface using PATCH and verifies if it returns status 204
+ dellemc.enterprise_sonic.sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet60/config/description
+ method: "PATCH"
+ body: {"openconfig-interfaces:description": "Eth-60"}
+ status_code: 204
+
+- name: Deletes an associated IP address using DELETE and verifies if it returns status 204
+ dellemc.enterprise_sonic.sonic_api:
+ url: >
+ data/openconfig-interfaces:interfaces/interface=Ethernet64/subinterfaces/subinterface=0/
+ openconfig-if-ip:ipv4/addresses/address=1.1.1.1/config/prefix-length
+ method: "DELETE"
+ status_code: 204
+
+- name: Adds a VLAN network instance using PUT and verifies if it returns status 204
+ dellemc.enterprise_sonic.sonic_api:
+ url: data/openconfig-network-instance:network-instances/network-instance=Vlan100/
+ method: "PUT"
+ body: {"openconfig-network-instance:network-instance": [{"name": "Vlan100","config": {"name": "Vlan100"}}]}
+ status_code: 204
+
+- name: Adds a prefix-set to a routing policy using POST and verifies if it returns 201
+ dellemc.enterprise_sonic.sonic_api:
+ url: data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets/prefix-set=p1
+ method: "POST"
+ body: {"openconfig-routing-policy:config": {"name": "p1","mode": "IPV4" }}
+ status_code: 201
+
+"""
+RETURN = """
+response:
+ description: The response at the network device end for the REST call which contains the status code.
+ returned: always
+ type: list
+ sample: {"response": [ 204,{""}]}
+msg:
+ description: The HTTP error message from the request.
+ returned: HTTP Error
+ type: str
+"""
+
+from ansible.module_utils.connection import ConnectionError
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import edit_config, to_request
+
+
+def initiate_request(module):
+ """Get all the data available in chassis"""
+ url = module.params['url']
+ body = module.params['body']
+ method = module.params['method']
+ if method == "GET" or method == "DELETE":
+ request = to_request(module, [{"path": url, "method": method}])
+ elif method == "PATCH" or method == "PUT" or method == "POST":
+ request = to_request(module, [{"path": url, "method": method, "data": body}])
+
+ try:
+ response = edit_config(module, request)
+ except ConnectionError as exc:
+ module.fail_json(msg=to_text(exc))
+ return response
+
+
+def main():
+
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ argument_spec = dict(
+ url=dict(type='path', required=True),
+ body=dict(type='raw', required=False),
+ method=dict(type='str', choices=['GET', 'PUT', 'PATCH', 'DELETE', 'POST'], required=True),
+ status_code=dict(type='list', elements='int', required=True),
+ )
+
+ module = AnsibleModule(argument_spec=argument_spec,
+ supports_check_mode=True)
+
+ result = dict(
+ changed=False,
+ )
+ response = initiate_request(module)
+ response_code = response[0][0]
+ status_code = module.params['status_code']
+ if response_code == int(status_code[0]) and response_code in (201, 204):
+ result.update({'changed': True})
+
+ result.update({
+ 'response': response,
+ })
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py
new file mode 100644
index 00000000..bc53ca40
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp.py
@@ -0,0 +1,390 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_bgp
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Dhivya P (@dhivayp)
+short_description: Manage global BGP and its parameters
+description:
+ - This module provides configuration management of global BGP parameters on devices running Enterprise SONiC Distribution by Dell Technologies.
+options:
+ config:
+ description:
+ - Specifies the BGP-related configuration.
+ type: list
+ elements: dict
+ suboptions:
+ bgp_as:
+ description:
+ - Specifies the BGP autonomous system (AS) number to configure on the device.
+ type: str
+ required: true
+ vrf_name:
+ description:
+ - Specifies the VRF name.
+ type: str
+ default: 'default'
+ router_id:
+ description:
+ - Configures the BGP routing process router-id value.
+ type: str
+ log_neighbor_changes:
+ description:
+ - Enables/disables logging neighbor up/down and reset reason.
+ type: bool
+ max_med:
+ description:
+ - Configure max med and its parameters
+ type: dict
+ suboptions:
+ on_startup:
+ description:
+ - On startup time and max-med value
+ type: dict
+ suboptions:
+ timer:
+ description:
+ - Configures on startup time
+ type: int
+ med_val:
+ description:
+ - on startup med value
+ type: int
+ timers:
+ description:
+ - Adjust routing timers
+ type: dict
+ suboptions:
+ holdtime:
+ description:
+ - Configures hold-time
+ type: int
+ keepalive_interval:
+ description:
+ - Configures keepalive-interval
+ type: int
+ bestpath:
+ description:
+ - Configures the BGP best-path.
+ type: dict
+ suboptions:
+ as_path:
+ description:
+ - Configures the as-path values.
+ type: dict
+ suboptions:
+ confed:
+ description:
+ - Configures the confed values of as-path.
+ type: bool
+ ignore:
+ description:
+ - Configures the ignore values of as-path.
+ type: bool
+ multipath_relax:
+ description:
+ - Configures the multipath_relax values of as-path.
+ type: bool
+ multipath_relax_as_set:
+ description:
+ - Configures the multipath_relax_as_set values of as-path.
+ type: bool
+ compare_routerid:
+ description:
+ - Configures the compare_routerid.
+ type: bool
+ med:
+ description:
+ - Configures the med values.
+ type: dict
+ suboptions:
+ confed:
+ description:
+ - Configures the confed values of med.
+ type: bool
+ missing_as_worst:
+ description:
+ - Configures the missing_as_worst values of as-path.
+ type: bool
+ always_compare_med:
+ description:
+ - Allows comparing meds from different neighbors if set to true
+ type: bool
+ state:
+ description:
+ - Specifies the operation to be performed on the BGP process that is configured on the device.
+ - In case of merged, the input configuration is merged with the existing BGP configuration on the device.
+ - In case of deleted, the existing BGP configuration is removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#!
+#router bgp 10 vrf VrfCheck1
+# router-id 10.2.2.32
+# log-neighbor-changes
+#!
+#router bgp 11 vrf VrfCheck2
+# log-neighbor-changes
+# bestpath as-path ignore
+# bestpath med missing-as-worst confed
+# bestpath compare-routerid
+#!
+#router bgp 4
+# router-id 10.2.2.4
+# bestpath as-path ignore
+# bestpath as-path confed
+# bestpath med missing-as-worst confed
+# bestpath compare-routerid
+#!
+#
+- name: Delete BGP Global attributes
+ dellemc.enterprise_sonic.sonic_bgp:
+ config:
+ - bgp_as: 4
+ router_id: 10.2.2.4
+ log_neighbor_changes: False
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: False
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ - bgp_as: 10
+ router_id: 10.2.2.32
+ log_neighbor_changes: True
+ vrf_name: 'VrfCheck1'
+ - bgp_as: 11
+ log_neighbor_changes: True
+ vrf_name: 'VrfCheck2'
+ bestpath:
+ as_path:
+ confed: False
+ ignore: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ state: deleted
+
+
+# After state:
+# ------------
+#
+#!
+#router bgp 10 vrf VrfCheck1
+# log-neighbor-changes
+#!
+#router bgp 11 vrf VrfCheck2
+# log-neighbor-changes
+# bestpath compare-routerid
+#!
+#router bgp 4
+# log-neighbor-changes
+# bestpath compare-routerid
+#!
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#!
+#router bgp 10 vrf VrfCheck1
+# router-id 10.2.2.32
+# log-neighbor-changes
+#!
+#router bgp 11 vrf VrfCheck2
+# log-neighbor-changes
+# bestpath as-path ignore
+# bestpath med missing-as-worst confed
+# bestpath compare-routerid
+#!
+#router bgp 4
+# router-id 10.2.2.4
+# bestpath as-path ignore
+# bestpath as-path confed
+# bestpath med missing-as-worst confed
+# bestpath compare-routerid
+#!
+
+- name: Deletes all the bgp global configurations
+ dellemc.enterprise_sonic.sonic_bgp:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+#!
+#!
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+#!
+#router bgp 4
+# router-id 10.1.1.4
+#!
+#
+- name: Merges provided configuration with device configuration
+ dellemc.enterprise_sonic.sonic_bgp:
+ config:
+ - bgp_as: 4
+ router_id: 10.2.2.4
+ log_neighbor_changes: False
+ timers:
+ holdtime: 20
+ keepalive_interval: 30
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: False
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ always_compare_med: True
+ max_med:
+ on_startup:
+ timer: 667
+ med_val: 7878
+ - bgp_as: 10
+ router_id: 10.2.2.32
+ log_neighbor_changes: True
+ vrf_name: 'VrfCheck1'
+ - bgp_as: 11
+ log_neighbor_changes: True
+ vrf_name: 'VrfCheck2'
+ bestpath:
+ as_path:
+ confed: False
+ ignore: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ state: merged
+#
+# After state:
+# ------------
+#
+#!
+#router bgp 10 vrf VrfCheck1
+# router-id 10.2.2.32
+# log-neighbor-changes
+#!
+#router bgp 11 vrf VrfCheck2
+# log-neighbor-changes
+# bestpath as-path ignore
+# bestpath med missing-as-worst confed
+# bestpath compare-routerid
+#!
+#router bgp 4
+# router-id 10.2.2.4
+# bestpath as-path ignore
+# bestpath as-path confed
+# bestpath med missing-as-worst confed
+# bestpath compare-routerid
+# always-compare-med
+# max-med on-startup 667 7878
+# timers 20 30
+#
+#!
+
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp.bgp import BgpArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp.bgp import Bgp
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=BgpArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py
new file mode 100644
index 00000000..6d55355c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_af.py
@@ -0,0 +1,414 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_af
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: sonic_bgp_af
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+short_description: Manage global BGP address-family and its parameters
+description:
+ - This module provides configuration management of global BGP_AF parameters on devices running Enterprise SONiC.
+ - bgp_as and vrf_name must be created in advance on the device.
+options:
+ config:
+ description:
+ - Specifies the BGP_AF related configuration.
+ type: list
+ elements: dict
+ suboptions:
+ bgp_as:
+ description:
+ - Specifies the BGP autonomous system (AS) number which is already configured on the device.
+ type: str
+ required: true
+ vrf_name:
+ description:
+ - Specifies the VRF name which is already configured on the device.
+ type: str
+ default: 'default'
+ address_family:
+ description:
+ - Specifies BGP address family related configurations.
+ type: dict
+ suboptions:
+ afis:
+ description:
+ - List of address families, such as ipv4, ipv6, and l2vpn.
+ - afi and safi are required together.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Type of address family to configure.
+ type: str
+ choices:
+ - ipv4
+ - ipv6
+ - l2vpn
+ required: True
+ safi:
+ description:
+ - Specifies the type of communication for the address family.
+ type: str
+ choices:
+ - unicast
+ - evpn
+ default: unicast
+ dampening:
+ description:
+ - Enable route flap dampening if set to true
+ type: bool
+ network:
+ description:
+ - Enable routing on an IP network for each prefix provided in the network
+ type: list
+ elements: str
+ redistribute:
+ description:
+ - Specifies the redistribute information from another routing protocol.
+ type: list
+ elements: dict
+ suboptions:
+ protocol:
+ description:
+ - Specifies the protocol for configuring redistribute information.
+ type: str
+ choices: ['ospf', 'static', 'connected']
+ required: True
+ metric:
+ description:
+ - Specifies the metric for redistributed routes.
+ type: str
+ route_map:
+ description:
+ - Specifies the route map reference.
+ type: str
+ advertise_pip:
+ description:
+ - Enables advertise PIP
+ type: bool
+ advertise_pip_ip:
+ description:
+ - PIP IPv4 address
+ type: str
+ advertise_pip_peer_ip:
+ description:
+ - PIP peer IPv4 address
+ type: str
+ advertise_svi_ip:
+ description:
+ - Enables advertise SVI MACIP routes
+ type: bool
+ route_advertise_list:
+ description:
+ - List of advertise routes
+ type: list
+ elements: dict
+ suboptions:
+ advertise_afi:
+ required: True
+ type: str
+ choices:
+ - ipv4
+ - ipv6
+ description:
+ - Specifies the address family
+ route_map:
+ type: str
+ description:
+ - Specifies the route-map reference
+ advertise_default_gw:
+ description:
+ - Specifies the advertise default gateway flag.
+ type: bool
+ advertise_all_vni:
+ description:
+ - Specifies the advertise all vni flag.
+ type: bool
+ max_path:
+ description:
+ - Specifies the maximum paths of ibgp and ebgp count.
+ type: dict
+ suboptions:
+ ibgp:
+ description:
+ - Specifies the count of the ibgp multipaths count.
+ type: int
+ ebgp:
+ description:
+ - Specifies the count of the ebgp multipaths count.
+ type: int
+ state:
+ description:
+ - Specifies the operation to be performed on the BGP_AF process configured on the device.
+ - In case of merged, the input configuration is merged with the existing BGP_AF configuration on the device.
+ - In case of deleted, the existing BGP_AF configuration is removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show running-configuration bgp
+#!
+#router bgp 51
+# router-id 111.2.2.41
+# timers 60 180
+# !
+# address-family ipv4 unicast
+# maximum-paths 1
+# maximum-paths ibgp 1
+# dampening
+# !
+# address-family ipv6 unicast
+# redistribute connected route-map bb metric 21
+# redistribute ospf route-map aa metric 27
+# redistribute static route-map bb metric 26
+# maximum-paths 4
+# maximum-paths ibgp 5
+# !
+# address-family l2vpn evpn
+# advertise-svi-ip
+# advertise ipv6 unicast route-map aa
+# advertise-pip ip 1.1.1.1 peer-ip 2.2.2.2
+#!
+#
+- name: Delete BGP Address family configuration from the device
+ dellemc.enterprise_sonic.sonic_bgp_af:
+ config:
+ - bgp_as: 51
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ advertise_pip: True
+ advertise_pip_ip: "1.1.1.1"
+ advertise_pip_peer_ip: "2.2.2.2"
+ advertise_svi_ip: True
+ advertise_all_vni: False
+ advertise_default_gw: False
+ route_advertise_list:
+ - advertise_afi: ipv6
+ route_map: aa
+ - afi: ipv4
+ safi: unicast
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 2
+ ibgp: 5
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: bb
+ - metric: "27"
+ protocol: ospf
+ route_map: aa
+ - metric: "26"
+ protocol: static
+ route_map: bb
+ state: deleted
+
+# After state:
+# ------------
+#
+#do show running-configuration bgp
+#!
+#router bgp 51
+# router-id 111.2.2.41
+# timers 60 180
+# !
+# address-family ipv6 unicast
+# !
+# address-family l2vpn evpn
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show running-configuration bgp
+#!
+#router bgp 51
+# router-id 111.2.2.41
+# timers 60 180
+# !
+# address-family ipv6 unicast
+# !
+# address-family l2vpn evpn
+#
+- name: Delete All BGP address family configurations
+ dellemc.enterprise_sonic.sonic_bgp_af:
+ config:
+ state: deleted
+
+
+# After state:
+# ------------
+#
+#do show running-configuration bgp
+#!
+#router bgp 51
+# router-id 111.2.2.41
+# timers 60 180
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#do show running-configuration bgp
+#!
+#router bgp 51
+# router-id 111.2.2.41
+# timers 60 180
+# !
+# address-family l2vpn evpn
+#
+- name: Merge provided BGP address family configuration on the device.
+ dellemc.enterprise_sonic.sonic_bgp_af:
+ config:
+ - bgp_as: 51
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ advertise_pip: True
+ advertise_pip_ip: "3.3.3.3"
+ advertise_pip_peer_ip: "4.4.4.4"
+ advertise_svi_ip: True
+ advertise_all_vni: False
+ advertise_default_gw: False
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: bb
+ - afi: ipv4
+ safi: unicast
+ network:
+ - 2.2.2.2/16
+ - 192.168.10.1/32
+ dampening: True
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 4
+ ibgp: 5
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: bb
+ - metric: "27"
+ protocol: ospf
+ route_map: aa
+ - metric: "26"
+ protocol: static
+ route_map: bb
+ state: merged
+# After state:
+# ------------
+#
+#do show running-configuration bgp
+#!
+#router bgp 51
+# router-id 111.2.2.41
+# timers 60 180
+# !
+# address-family ipv4 unicast
+# network 2.2.2.2/16
+# network 192.168.10.1/32
+# dampening
+# !
+# address-family ipv6 unicast
+# redistribute connected route-map bb metric 21
+# redistribute ospf route-map aa metric 27
+# redistribute static route-map bb metric 26
+# maximum-paths 4
+# maximum-paths ibgp 5
+# !
+# address-family l2vpn evpn
+# advertise-svi-ip
+# advertise ipv4 unicast route-map bb
+# advertise-pip ip 3.3.3.3 peer-ip 4.4.4.4
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_af.bgp_af import Bgp_afArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_af.bgp_af import Bgp_af
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Bgp_afArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp_af(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py
new file mode 100644
index 00000000..bd2ff74a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_as_paths.py
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_as_paths
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_bgp_as_paths
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage BGP autonomous system path (or as-path-list) and its parameters
+description:
+ - This module provides configuration management of BGP bgp_as_paths for devices
+ running Enterprise SONiC Distribution by Dell Technologies.
+author: Kumaraguru Narayanan (@nkumaraguru)
+options:
+ config:
+ description: A list of 'bgp_as_paths' configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ required: True
+ type: str
+ description:
+ - Name of as-path-list.
+ members:
+ required: False
+ type: list
+ elements: str
+ description:
+ - Members of this BGP as-path; regular expression string can be provided.
+ permit:
+ required: False
+ type: bool
+ description:
+ - Permits or denies this as path.
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# action: permit
+# members: 808.*,909.*
+
+- name: Delete BGP as path list
+ dellemc.enterprise_sonic.sonic_bgp_as_paths:
+ config:
+ - name: test
+ members:
+ - 909.*
+ permit: true
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# action:
+# members: 808.*
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# action: permit
+# members: 808.*,909.*
+# AS path list test1:
+# action: deny
+# members: 608.*,709.*
+
+- name: Deletes BGP as-path list
+ dellemc.enterprise_sonic.sonic_bgp_as_paths:
+ config:
+ - name: test
+ members:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp as-path-access-list
+# AS path list test1:
+# action: deny
+# members: 608.*,709.*
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# action: permit
+# members: 808.*,909.*
+
+- name: Deletes BGP as-path list
+ dellemc.enterprise_sonic.sonic_bgp_as_paths:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp as-path-access-list
+# (No bgp as-path-access-list configuration present)
+
+
+# Using merged
+
+# Before state:
+# -------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+
+- name: Adds 909.* to test as-path list
+ dellemc.enterprise_sonic.sonic_bgp_as_paths:
+ config:
+ - name: test
+ members:
+ - 909.*
+ permit: true
+ state: merged
+
+# After state:
+# ------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# action: permit
+# members: 909.*
+
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_as_paths.bgp_as_paths import Bgp_as_pathsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_as_paths.bgp_as_paths import Bgp_as_paths
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Bgp_as_pathsArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp_as_paths(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py
new file mode 100644
index 00000000..08c8dcc7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_communities.py
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_communities
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: sonic_bgp_communities
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage BGP community and its parameters
+description:
+ - This module provides configuration management of BGP bgp_communities for device
+ running Enterprise SONiC Distribution by Dell Technologies.
+author: Kumaraguru Narayanan (@nkumaraguru)
+options:
+ config:
+ description: A list of 'bgp_communities' configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ required: True
+ type: str
+ description:
+ - Name of the BGP communitylist.
+ type:
+ type: str
+ description:
+ - Whether it is a standard or expanded community-list entry.
+ required: False
+ choices:
+ - standard
+ - expanded
+ default: standard
+ permit:
+ required: False
+ type: bool
+ description:
+ - Permits or denies this community.
+ aann:
+ required: False
+ type: str
+ description:
+ - Community number aa:nn format 0..65535:0..65535; applicable for standard BGP community type.
+ local_as:
+ required: False
+ type: bool
+ description:
+ - Do not send outside local AS (well-known community); applicable for standard BGP community type.
+ no_advertise:
+ required: False
+ type: bool
+ description:
+ - Do not advertise to any peer (well-known community); applicable for standard BGP community type.
+ no_export:
+ required: False
+ type: bool
+ description:
+ - Do not export to next AS (well-known community); applicable for standard BGP community type.
+ no_peer:
+ required: False
+ type: bool
+ description:
+ - Do not export to next AS (well-known community); applicable for standard BGP community type.
+ members:
+ required: False
+ type: dict
+ suboptions:
+ regex:
+ type: list
+ elements: str
+ required: False
+ description:
+ - Members of this BGP community list. Regular expression string can be given here. Applicable for expanded BGP community type.
+ description:
+ - Members of this BGP community list.
+ match:
+ required: False
+ type: str
+ description:
+ - Matches any/all of the members.
+ choices:
+ - ALL
+ - ANY
+ default: ANY
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp community-list
+# Standard community list test: match: ANY
+# 101
+# 201
+# Standard community list test1: match: ANY
+# 301
+
+- name: Deletes BGP community member
+ dellemc.enterprise_sonic.sonic_bgp_communities:
+ config:
+ - name: test
+ members:
+ regex:
+ - 201
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp community-list
+# Standard community list test: match: ANY
+# 101
+# Standard community list test1: match: ANY
+# 301
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp community-list
+# Standard community list test: match: ANY
+# 101
+# Expanded community list test1: match: ANY
+# 201
+
+- name: Deletes a single BGP community
+ dellemc.enterprise_sonic.sonic_bgp_communities:
+ config:
+ - name: test
+ members:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp community-list
+# Expanded community list test1: match: ANY
+# 201
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp community-list
+# Standard community list test: match: ANY
+# 101
+# Expanded community list test1: match: ANY
+# 201
+
+- name: Delete All BGP communities
+ dellemc.enterprise_sonic.sonic_bgp_communities:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp community-list
+#
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp community-list
+# Standard community list test: match: ANY
+# 101
+# Expanded community list test1: match: ANY
+# 201
+
+- name: Deletes all members in a single BGP community
+ dellemc.enterprise_sonic.sonic_bgp_communities:
+ config:
+ - name: test
+ members:
+ regex:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp community-list
+# Expanded community list test: match: ANY
+# Expanded community list test1: match: ANY
+# 201
+
+
+# Using merged
+
+# Before state:
+# -------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+
+- name: Adds 909.* to test as-path list
+ dellemc.enterprise_sonic.sonic_bgp_as_paths:
+ config:
+ - name: test
+ members:
+ - 909.*
+ state: merged
+
+# After state:
+# ------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# members: 909.*
+
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration that is returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration that is returned is always in the same format
+ of the parameters above.
+commands:
+ description: The set of commands that are 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_communities.bgp_communities import Bgp_communitiesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_communities.bgp_communities import Bgp_communities
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Bgp_communitiesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp_communities(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py
new file mode 100644
index 00000000..c2af0c48
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_ext_communities.py
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_ext_communities
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_bgp_ext_communities
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage BGP extended community-list and its parameters
+description:
+ - This module provides configuration management of BGP extcommunity-list for devices running
+ Enterprise SONiC Distribution by Dell Technologies.
+author: Kumaraguru Narayanan (@nkumaraguru)
+options:
+ config:
+ description: A list of 'bgp_extcommunity_list' configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ required: True
+ type: str
+ description:
+ - Name of the BGP ext communitylist.
+ type:
+ type: str
+ description:
+ - Whether it is a standard or expanded ext community_list entry.
+ required: False
+ choices:
+ - standard
+ - expanded
+ default: standard
+ permit:
+ required: False
+ type: bool
+ description:
+ - Permits or denies this community.
+ members:
+ required: False
+ type: dict
+ suboptions:
+ regex:
+ type: list
+ elements: str
+ required: False
+ description:
+ - Members of this BGP ext community list. Regular expression string can be given here. Applicable for expanded ext BGP community type.
+ route_target:
+ type: list
+ elements: str
+ required: False
+ description:
+ - Members of this BGP ext community list. The format of route_target is in either 0..65535:0..65535 or A.B.C.D:[1..65535] format.
+ route_origin:
+ type: list
+ elements: str
+ required: False
+ description:
+ - Members of this BGP ext community list. The format of route_origin is in either 0..65535:0..65535 or A.B.C.D:[1..65535] format.
+ description:
+ - Members of this BGP ext community list.
+ match:
+ required: False
+ type: str
+ description:
+ - Matches any/all of the the members.
+ choices:
+ - all
+ - any
+ default: any
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# rt:101:101
+# rt:201:201
+
+- name: Deletes a BGP ext community member
+ dellemc.enterprise_sonic.sonic_bgp_ext_communities:
+ config:
+ - name: test
+ members:
+ regex:
+ - 201:201
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# rt:101:101
+#
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# 101
+# Expanded extended community list test1: match: ANY
+# 201
+
+- name: Deletes a single BGP extended community
+ dellemc.enterprise_sonic.sonic_bgp_ext_communities:
+ config:
+ - name: test1
+ members:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# 101
+#
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# 101
+# Expanded extended community list test1: match: ANY
+# 201
+
+- name: Deletes all BGP extended communities
+ dellemc.enterprise_sonic.sonic_bgp_ext_communities:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp ext-community-list
+#
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# 101
+# Expanded extended community list test1: match: ANY
+# 201
+
+- name: Deletes all members in a single BGP extended community
+ dellemc.enterprise_sonic.sonic_bgp_ext_communities:
+ config:
+ - name: test1
+ members:
+ regex:
+ state: deleted
+
+# After state:
+# ------------
+#
+# show bgp ext-community-list
+# Standard extended community list test: match: ANY
+# 101
+# Expanded extended community list test1: match: ANY
+#
+
+
+# Using merged
+
+# Before state:
+# -------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+
+- name: Adds 909.* to test as-path list
+ dellemc.enterprise_sonic.sonic_bgp_as_paths:
+ config:
+ - name: test
+ members:
+ - 909.*
+ state: merged
+
+# After state:
+# ------------
+#
+# show bgp as-path-access-list
+# AS path list test:
+# members: 909.*
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_ext_communities.bgp_ext_communities import (
+ Bgp_ext_communitiesArgs,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_ext_communities.bgp_ext_communities import Bgp_ext_communities
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Bgp_ext_communitiesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp_ext_communities(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py
new file mode 100644
index 00000000..19aeb6fc
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors.py
@@ -0,0 +1,1112 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_bgp_neighbors
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_bgp_neighbors
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage a BGP neighbor and its parameters
+description:
+ - This module provides configuration management of global BGP_NEIGHBORS parameters on devices running Enterprise SONiC.
+ - bgp_as and vrf_name must be created on the device in advance.
+author: Abirami N (@abirami-n)
+options:
+ config:
+ description: Specifies the BGP neighbors related configuration.
+ type: list
+ elements: dict
+ suboptions:
+ bgp_as:
+ description:
+ - Specifies the BGP autonomous system (AS) number which is already configured on the device.
+ type: str
+ required: True
+ vrf_name:
+ description:
+ - Specifies the VRF name which is already configured on the device.
+ default: default
+ type: str
+ peer_group:
+ description: Specifies the list of peer groups.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description: Name of the peer group.
+ type: str
+ required: True
+ remote_as:
+ description:
+ - Remote AS of the BGP peer group to configure.
+ - peer_as and peer_type are mutually exclusive.
+ type: dict
+ suboptions:
+ peer_as:
+ description:
+ - Specifies remote AS number.
+ - The range is from 1 to 4294967295.
+ type: int
+ peer_type:
+ description:
+ - Specifies the type of BGP peer.
+ type: str
+ choices:
+ - internal
+ - external
+ bfd:
+ description:
+ - Enables or disables BFD.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - Enables BFD liveliness check for a BGP peer.
+ type: bool
+ check_failure:
+ description:
+ - Link dataplane status with control plane.
+ type: bool
+ profile:
+ description:
+ - BFD Profile name.
+ type: str
+ advertisement_interval:
+ description:
+ - Specifies the minimum interval between sending BGP routing updates.
+ - The range is from 0 to 600.
+ type: int
+ timers:
+ description:
+ - Specifies BGP peer group timer related configurations.
+ type: dict
+ suboptions:
+ keepalive:
+ description:
+ - Frequency with which the device sends keepalive messages to its peer, in seconds.
+ - The range is from 0 to 65535.
+ type: int
+ holdtime:
+ description:
+ - Interval after not receiving a keepalive message that Enterprise SONiC declares a peer dead, in seconds.
+ - The range is from 0 to 65535.
+ type: int
+ connect_retry:
+ description:
+ - Time interval in seconds between attempts to establish a session with the peer.
+ - The range is from 1 to 65535.
+ type: int
+ capability:
+ description:
+ - Specifies capability attributes to this peer group.
+ type: dict
+ suboptions:
+ dynamic:
+ description:
+ - Enables or disables dynamic capability to this peer group.
+ type: bool
+ extended_nexthop:
+ description:
+ - Enables or disables advertise extended next-hop capability to the peer.
+ type: bool
+ auth_pwd:
+ description:
+ - Configuration for peer group authentication password.
+ type: dict
+ suboptions:
+ pwd:
+ description:
+ - Authentication password for the peer group.
+ type: str
+ required: True
+ encrypted:
+ description:
+ - Indicates whether the password is encrypted text.
+ type: bool
+ default: False
+ pg_description:
+ description:
+ - A textual description of the peer group.
+ type: str
+ disable_connected_check:
+ description:
+ - Disables EBGP conntected route check.
+ type: bool
+ dont_negotiate_capability:
+ description:
+ - Disables capability negotiation.
+ type: bool
+ ebgp_multihop:
+ description:
+ - Allow EBGP peers not on directly connected networks.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - Enables the referenced group or peers to be indirectly connected.
+ type: bool
+ default: False
+ multihop_ttl:
+ description:
+ - Time-to-live value to use when packets are sent to the referenced group or peers and ebgp-multihop is enabled.
+ type: int
+ enforce_first_as:
+ description:
+ - Enforces the first AS for EBGP routes.
+ type: bool
+ enforce_multihop:
+ description:
+ - Enforces EBGP multihop performance for peer.
+ type: bool
+ local_address:
+ description:
+ - Set the local IP address to use for the session when sending BGP update messages.
+ type: str
+ local_as:
+ description:
+ - Specifies local autonomous system number.
+ type: dict
+ suboptions:
+ as:
+ description:
+ - Local autonomous system number.
+ type: int
+ required: True
+ no_prepend:
+ description:
+ - Do not prepend the local-as number in AS-Path advertisements.
+ type: bool
+ replace_as:
+ description:
+ - Replace the configured AS Number with the local-as number in AS-Path advertisements.
+ type: bool
+ override_capability:
+ description:
+ - Override capability negotiation result.
+ type: bool
+ passive:
+ description:
+ - Do not send open messages to this peer.
+ type: bool
+ default: False
+ shutdown_msg:
+ description:
+ - Add a shutdown message.
+ type: str
+ solo:
+ description:
+ - Indicates that routes advertised by the peer should not be reflected back to the peer.
+ type: bool
+ strict_capability_match:
+ description:
+ - Enables strict capability negotiation match.
+ type: bool
+ ttl_security:
+ description:
+ - Enforces only the peers that are specified number of hops away will be allowed to become peers.
+ type: int
+ address_family:
+ description:
+ - Holds of list of address families associated to the peergroup.
+ type: dict
+ suboptions:
+ afis:
+ description:
+ - List of address families with afi, safi, activate and allowas-in parameters.
+ - afi and safi are required together.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Holds afi mode.
+ type: str
+ choices:
+ - ipv4
+ - ipv6
+ - l2vpn
+ safi:
+ description:
+ - Holds safi mode.
+ type: str
+ choices:
+ - unicast
+ - evpn
+ activate:
+ description:
+ - Enable or disable activate.
+ type: bool
+ allowas_in:
+ description:
+ - Holds AS value.
+ - The origin and value are mutually exclusive.
+ type: dict
+ suboptions:
+ origin:
+ description:
+ - Set AS as the origin.
+ type: bool
+ value:
+ description:
+ - Holds AS number in the range 1-10.
+ type: int
+ ip_afi:
+ description:
+ - Common configuration attributes for IPv4 and IPv6 unicast address families.
+ type: dict
+ suboptions:
+ default_policy_name:
+ description:
+ - Specifies routing policy definition.
+ type: str
+ send_default_route:
+ description:
+ - Enable or disable sending of default-route to the peer.
+ type: bool
+ default: False
+ prefix_limit:
+ description:
+ - Specifies prefix limit attributes.
+ type: dict
+ suboptions:
+ max_prefixes:
+ description:
+ - Maximum number of prefixes that will be accepted from the peer.
+ type: int
+ prevent_teardown:
+ description:
+ - Enable or disable teardown of BGP session when maximum prefix limit is exceeded.
+ type: bool
+ default: False
+ warning_threshold:
+ description:
+ - Threshold on number of prefixes that can be received from a peer before generation of warning messages.
+ - Expressed as a percentage of max-prefixes.
+ type: int
+ restart_timer:
+ description:
+ - Time interval in seconds after which the BGP session is re-established after being torn down.
+ type: int
+ prefix_list_in:
+ description:
+ - Inbound route filtering policy for a peer.
+ type: str
+ prefix_list_out:
+ description:
+ - Outbound route filtering policy for a peer.
+ type: str
+ neighbors:
+ description: Specifies BGP neighbor-related configurations.
+ type: list
+ elements: dict
+ suboptions:
+ neighbor:
+ description:
+ - Neighbor router address.
+ type: str
+ required: True
+ remote_as:
+ description:
+ - Remote AS of the BGP neighbor to configure.
+ - peer_as and peer_type are mutually exclusive.
+ type: dict
+ suboptions:
+ peer_as:
+ description:
+ - Specifies remote AS number.
+ - The range is from 1 to 4294967295.
+ type: int
+ peer_type:
+ description:
+ - Specifies the type of BGP peer.
+ type: str
+ choices:
+ - internal
+ - external
+ bfd:
+ description:
+ - Enables or disables BFD.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - Enables BFD liveliness check for a BGP neighbor.
+ type: bool
+ check_failure:
+ description:
+ - Link dataplane status with control plane.
+ type: bool
+ profile:
+ description:
+ - BFD Profile name.
+ type: str
+ advertisement_interval:
+ description:
+ - Specifies the minimum interval between sending BGP routing updates.
+ - The range is from 0 to 600.
+ type: int
+ peer_group:
+ description:
+ - The name of the peer group that the neighbor is a member of.
+ type: str
+ timers:
+ description:
+ - Specifies BGP neighbor timer-related configurations.
+ type: dict
+ suboptions:
+ keepalive:
+ description:
+ - Frequency with which the device sends keepalive messages to its peer, in seconds.
+ - The range is from 0 to 65535.
+ type: int
+ holdtime:
+ description:
+ - Interval after not receiving a keepalive message that SONiC declares a peer dead, in seconds.
+ - The range is from 0 to 65535.
+ type: int
+ connect_retry:
+ description:
+ - Time interval in seconds between attempts to establish a session with the peer.
+ - The range is from 1 to 65535.
+ type: int
+ capability:
+ description:
+ - Specifies capability attributes to this neighbor.
+ type: dict
+ suboptions:
+ dynamic:
+ description:
+ - Enables or disables dynamic capability to this neighbor.
+ type: bool
+ extended_nexthop:
+ description:
+ - Enables or disables advertise extended next-hop capability to the peer.
+ type: bool
+ auth_pwd:
+ description:
+ - Configuration for neighbor group authentication password.
+ type: dict
+ suboptions:
+ pwd:
+ description:
+ - Authentication password for the neighbor group.
+ type: str
+ required: True
+ encrypted:
+ description:
+ - Indicates whether the password is encrypted text.
+ type: bool
+ default: False
+ nbr_description:
+ description:
+ - A textual description of the neighbor.
+ type: str
+ disable_connected_check:
+ description:
+ - Disables EBGP conntected route check.
+ type: bool
+ dont_negotiate_capability:
+ description:
+ - Disables capability negotiation.
+ type: bool
+ ebgp_multihop:
+ description:
+ - Allow EBGP neighbors not on directly connected networks.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - Enables the referenced group or neighbors to be indirectly connected.
+ type: bool
+ default: False
+ multihop_ttl:
+ description:
+ - Time-to-live value to use when packets are sent to the referenced group or neighbors and ebgp-multihop is enabled.
+ type: int
+ enforce_first_as:
+ description:
+ - Enforces the first AS for EBGP routes.
+ type: bool
+ enforce_multihop:
+ description:
+ - Enforces EBGP multihop performance for neighbor.
+ type: bool
+ local_address:
+ description:
+ - Set the local IP address to use for the session when sending BGP update messages.
+ type: str
+ local_as:
+ description:
+ - Specifies local autonomous system number.
+ type: dict
+ suboptions:
+ as:
+ description:
+ - Local autonomous system number.
+ type: int
+ required: True
+ no_prepend:
+ description:
+ - Do not prepend the local-as number in AS-Path advertisements.
+ type: bool
+ replace_as:
+ description:
+ - Replace the configured AS Number with the local-as number in AS-Path advertisements.
+ type: bool
+ override_capability:
+ description:
+ - Override capability negotiation result.
+ type: bool
+ passive:
+ description:
+ - Do not send open messages to this neighbor.
+ type: bool
+ default: False
+ port:
+ description:
+ - Neighbor's BGP port.
+ type: int
+ shutdown_msg:
+ description:
+ - Add a shutdown message.
+ type: str
+ solo:
+ description:
+ - Indicates that routes advertised by the peer should not be reflected back to the peer.
+ type: bool
+ strict_capability_match:
+ description:
+ - Enables strict capability negotiation match.
+ type: bool
+ ttl_security:
+ description:
+ - Enforces only the neighbors that are specified number of hops away will be allowed to become neighbors.
+ type: int
+ v6only:
+ description:
+ - Enables BGP with v6 link-local only.
+ type: bool
+
+ state:
+ description:
+ - Specifies the operation to be performed on the BGP process that is configured on the device.
+ - In case of merged, the input configuration is merged with the existing BGP configuration on the device.
+ - In case of deleted, the existing BGP configuration is removed from the device.
+ default: merged
+ type: str
+ choices:
+ - merged
+ - deleted
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#router bgp 11 vrf VrfCheck2
+# network import-check
+# timers 60 180
+#!
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+# !
+# neighbor interface Eth1/3
+#!
+#router bgp 11
+# network import-check
+# timers 60 180
+# !
+# neighbor 192.168.1.4
+# !
+# peer-group SP1
+# bfd
+# capability dynamic
+# !
+# peer-group SP2
+# !
+#
+- name: Deletes all BGP neighbors
+ dellemc.enterprise_sonic.sonic_bgp_neighbors:
+ config:
+ state: deleted
+
+#
+# After state:
+# -------------
+#router bgp 11 vrf VrfCheck2
+# network import-check
+# timers 60 180
+#!
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+#!
+#router bgp 11
+# network import-check
+# timers 60 180
+# !
+#
+# Using merged
+#
+# Before state:
+# ------------
+#router bgp 11 vrf VrfCheck2
+# network import-check
+# timers 60 180
+#!
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+#!
+#router bgp 11
+# network import-check
+# timers 60 180
+# !
+
+- name: "Adds sonic_bgp_neighbors"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors:
+ config:
+ - bgp_as: 51
+ neighbors:
+ - neighbor: Eth1/2
+ auth_pwd:
+ pwd: 'pw123'
+ encrypted: false
+ dont_negotiate_capability: true
+ ebgp_multihop:
+ enabled: true
+ multihop_ttl: 1
+ enforce_first_as: true
+ enforce_multihop: true
+ local_address: 'Ethernet4'
+ local_as:
+ as: 2
+ no_prepend: true
+ replace_as: true
+ nbr_description: "description 1"
+ override_capability: true
+ passive: true
+ port: 3
+ shutdown_msg: 'msg1'
+ solo: true
+ - neighbor: 1.1.1.1
+ disable_connected_check: true
+ ttl_security: 5
+ - bgp_as: 51
+ vrf_name: VrfReg1
+ peer_group:
+ - name: SPINE
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile 1'
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ auth_pwd:
+ pwd: 'U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M='
+ encrypted: true
+ dont_negotiate_capability: true
+ ebgp_multihop:
+ enabled: true
+ multihop_ttl: 1
+ enforce_first_as: true
+ enforce_multihop: true
+ local_address: 'Ethernet4'
+ local_as:
+ as: 2
+ no_prepend: true
+ replace_as: true
+ pg_description: 'description 1'
+ override_capability: true
+ passive: true
+ solo: true
+ remote_as:
+ peer_as: 4
+ - name: SPINE1
+ disable_connected_check: true
+ shutdown_msg: "msg1"
+ strict_capability_match: true
+ timers:
+ keepalive: 30
+ holdtime: 15
+ connect_retry: 25
+ ttl_security: 5
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ activate: true
+ allowas_in:
+ origin: true
+ - afi: ipv6
+ safi: unicast
+ activate: true
+ allowas_in:
+ value: 5
+ neighbors:
+ - neighbor: Eth1/3
+ remote_as:
+ peer_as: 10
+ peer_group: SPINE
+ advertisement_interval: 15
+ timers:
+ keepalive: 30
+ holdtime: 15
+ connect_retry: 25
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile 1'
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ auth_pwd:
+ pwd: 'U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg='
+ encrypted: true
+ nbr_description: 'description 2'
+ strict_capability_match: true
+ v6only: true
+ - neighbor: 192.168.1.4
+ state: merged
+#
+# After state:
+# ------------
+#!
+#router bgp 11 vrf VrfCheck2
+# network import-check
+# timers 60 180
+#!
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+# !
+# peer-group SPINE1
+# timers 15 30
+# timers connect 25
+# shutdown message msg1
+# disable-connected-check
+# strict-capability-match
+# ttl-security hops 5
+# !
+# peer-group SPINE
+# description "description 1"
+# ebgp-multihop 1
+# remote-as 4
+# bfd check-control-plane-failure profile "profile 1"
+# update-source interface Ethernet4
+# capability dynamic
+# capability extended-nexthop
+# dont-capability-negotiate
+# enforce-first-as
+# enforce-multihop
+# local-as 2 no-prepend replace-as
+# override-capability
+# passive
+# password U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M= encrypted
+# solo
+# address-family ipv4 unicast
+# activate
+# allowas-in origin
+# send-community both
+# !
+# address-family ipv6 unicast
+# activate
+# allowas-in 5
+# send-community both
+# !
+# neighbor interface Eth1/3
+# description "description 2"
+# peer-group SPINE
+# remote-as 10
+# timers 15 30
+# timers connect 25
+# bfd check-control-plane-failure profile "profile 1"
+# advertisement-interval 15
+# capability extended-nexthop
+# capability dynamic
+# v6only
+# password U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg= encrypted
+# strict-capability-match
+# !
+# neighbor 192.168.1.4
+#!
+# router bgp 51
+# timers 60 180
+# neighbor interface Eth1/2
+# description "description 1"
+# shutdown message msg1
+# ebgp-multihop 1
+# remote-as external
+# update-source interface Ethernet4
+# dont-capability-negotiate
+# enforce-first-as
+# enforce-multihop
+# local-as 2 no-prepend replace-as
+# override-capability
+# passive
+# password U2FsdGVkX1+bxMf9TKOhaXRNNaHmywiEVDF2lJ2c000= encrypted
+# port 3
+# solo
+# neighbor 1.1.1.1
+# disable-connected-check
+# ttl-security hops 5
+#router bgp 11
+# network import-check
+# timers 60 180
+#
+# Using deleted
+#
+# Before state:
+# ------------
+#!
+#router bgp 11 vrf VrfCheck2
+# network import-check
+# timers 60 180
+#!
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+# !
+# peer-group SPINE
+# bfd
+# remote-as 4
+# !
+# neighbor interface Eth1/3
+# peer-group SPINE
+# remote-as 10
+# timers 15 30
+# advertisement-interval 15
+# bfd
+# capability extended-nexthop
+# capability dynamic
+# !
+# neighbor 192.168.1.4
+#!
+#router bgp 11
+# network import-check
+# timers 60 18
+# !
+# peer-group SP
+# !
+# neighbor interface Eth1/3
+#
+- name: "Deletes sonic_bgp_neighbors and peer-groups specific to vrfname"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors:
+ config:
+ - bgp_as: 51
+ vrf_name: VrfReg1
+ state: deleted
+
+# After state:
+# ------------
+#!
+#router bgp 11 vrf VrfCheck2
+# network import-check
+# timers 60 180
+#!
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+# !
+#router bgp 11
+# network import-check
+# timers 60 18
+# !
+# peer-group SP
+# !
+# neighbor interface Eth1/3
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+# !
+# peer-group SPINE
+# bfd
+# remote-as 4
+# !
+# neighbor interface Eth1/3
+# peer-group SPINE
+# remote-as 10
+# timers 15 30
+# advertisement-interval 15
+# bfd
+# capability extended-nexthop
+# capability dynamic
+# !
+# neighbor 192.168.1.4
+# !
+
+- name: "Deletes specific sonic_bgp_neighbors"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors:
+ config:
+ - bgp_as: 51
+ neighbors:
+ - neighbor: Eth1/2
+ auth_pwd:
+ pwd: 'pw123'
+ encrypted: false
+ dont_negotiate_capability: true
+ ebgp_multihop:
+ enabled: true
+ multihop_ttl: 1
+ enforce_first_as: true
+ enforce_multihop: true
+ local_address: 'Ethernet4'
+ local_as:
+ as: 2
+ no_prepend: true
+ replace_as: true
+ nbr_description: 'description 1'
+ override_capability: true
+ passive: true
+ port: 3
+ shutdown_msg: 'msg1'
+ solo: true
+ - neighbor: 1.1.1.1
+ disable_connected_check: true
+ ttl_security: 5
+ - bgp_as: 51
+ vrf_name: VrfReg1
+ peer_group:
+ - name: SPINE
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile 1'
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ auth_pwd:
+ pwd: 'U2FsdGVkX1/4sRsZ624wbAJfDmagPLq2LsGDOcW/47M='
+ encrypted: true
+ dont_negotiate_capability: true
+ ebgp_multihop:
+ enabled: true
+ multihop_ttl: 1
+ enforce_first_as: true
+ enforce_multihop: true
+ local_address: 'Ethernet4'
+ local_as:
+ as: 2
+ no_prepend: true
+ replace_as: true
+ pg_description: 'description 1'
+ override_capability: true
+ passive: true
+ solo: true
+ remote_as:
+ peer_as: 4
+ - name: SPINE1
+ disable_connected_check: true
+ shutdown_msg: "msg1"
+ strict_capability_match: true
+ timers:
+ keepalive: 30
+ holdtime: 15
+ connect_retry: 25
+ ttl_security: 5
+ neighbors:
+ - neighbor: Eth1/3
+ remote_as:
+ peer_as: 10
+ peer_group: SPINE
+ advertisement_interval: 15
+ timers:
+ keepalive: 30
+ holdtime: 15
+ connect_retry: 25
+ bfd:
+ check_failure: true
+ enabled: true
+ profile: 'profile 1'
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ auth_pwd:
+ pwd: 'U2FsdGVkX199MZ7YOPkOR9O6wEZmtGSgiDfnlcN9hBg='
+ encrypted: true
+ nbr_description: 'description 2'
+ strict_capability_match: true
+ v6only: true
+ - neighbor: 192.168.1.4
+ state: deleted
+#
+# After state:
+# -------------
+#
+#router bgp 51 vrf VrfReg1
+# network import-check
+# timers 60 180
+# !
+# peer-group SPINE1
+# !
+# peer-group SPINE
+# !
+# neighbor interface Eth1/3
+# !
+# neighbor interface Eth1/2
+# neighbor 1.1.1.1
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+# sonic# show running-configuration bgp peer-group vrf default
+# (No bgp peer-group configuration present)
+
+- name: "Configure BGP peer-group prefix-list attributes"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors:
+ config:
+ - bgp_as: 51
+ peer_group:
+ - name: SPINE
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 1
+ prevent_teardown: true
+ warning_threshold: 80
+ prefix_list_in: p1
+ prefix_list_out: p2
+ state: merged
+
+# After state:
+# ------------
+#
+# sonic# show running-configuration bgp peer-group vrf default
+# !
+# peer-group SPINE
+# !
+# address-family ipv4 unicast
+# default-originate route-map rmap_reg1
+# prefix-list p1 in
+# prefix-list p2 out
+# send-community both
+# maximum-prefix 1 80 warning-only
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# sonic# show running-configuration bgp peer-group vrf default
+# !
+# peer-group SPINE
+# !
+# address-family ipv6 unicast
+# default-originate route-map rmap_reg2
+# prefix-list p1 in
+# prefix-list p2 out
+# send-community both
+# maximum-prefix 5 90 restart 2
+
+- name: "Delete BGP peer-group prefix-list attributes"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors:
+ config:
+ - bgp_as: 51
+ peer_group:
+ - name: SPINE
+ address_family:
+ afis:
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 5
+ warning_threshold: 90
+ restart-timer: 2
+ prefix_list_in: p1
+ prefix_list_out: p2
+ state: deleted
+
+# sonic# show running-configuration bgp peer-group vrf default
+# (No bgp peer-group configuration present)
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_neighbors.bgp_neighbors import Bgp_neighborsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_neighbors.bgp_neighbors import Bgp_neighbors
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Bgp_neighborsArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp_neighbors(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py
new file mode 100644
index 00000000..10400cfe
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_bgp_neighbors_af.py
@@ -0,0 +1,451 @@
+#!/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 sonic_bgp_neighbors_af
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: sonic_bgp_neighbors_af
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+short_description: Manage the BGP neighbor address-family and its parameters
+description:
+ - This module provides configuration management of BGP neighbors address-family parameters on devices running Enterprise SONiC.
+ - bgp_as, vrf_name and neighbors need be created in advance on the device.
+options:
+ config:
+ description:
+ - Specifies the BGP neighbors address-family related configuration.
+ type: list
+ elements: dict
+ suboptions:
+ bgp_as:
+ description:
+ - Specifies the BGP autonomous system (AS) number which is already configured on the device.
+ type: str
+ required: true
+ vrf_name:
+ description:
+ - Specifies the VRF name which is already configured on the device.
+ type: str
+ default: 'default'
+ neighbors:
+ description:
+ - Specifies BGP neighbor related configurations in address-family configuration mode.
+ type: list
+ elements: dict
+ suboptions:
+ neighbor:
+ description:
+ - Neighbor router address which is already configured on the device.
+ type: str
+ required: True
+ address_family:
+ description:
+ - Specifies BGP address-family related configurations.
+ - afi and safi are required together.
+ type: list
+ elements: dict
+ suboptions:
+ afi:
+ description:
+ - Type of address-family to configure.
+ type: str
+ choices:
+ - ipv4
+ - ipv6
+ - l2vpn
+ required: True
+ safi:
+ description:
+ - Specifies the type of cast for the address-family.
+ type: str
+ choices:
+ - unicast
+ - evpn
+ default: unicast
+ activate:
+ description:
+ - Enables the address-family for this neighbor.
+ type: bool
+ allowas_in:
+ description:
+ - Specifies the allowas in values.
+ type: dict
+ suboptions:
+ value:
+ description:
+ - Specifies the value of the allowas in.
+ type: int
+ origin:
+ description:
+ - Specifies the origin value.
+ type: bool
+ ip_afi:
+ description:
+ - Common configuration attributes for IPv4 and IPv6 unicast address families.
+ type: dict
+ suboptions:
+ default_policy_name:
+ description:
+ - Specifies routing policy definition.
+ type: str
+ send_default_route:
+ description:
+ - Enable or disable sending of default-route to the neighbor.
+ type: bool
+ default: False
+ prefix_limit:
+ description:
+ - Specifies prefix limit attributes.
+ type: dict
+ suboptions:
+ max_prefixes:
+ description:
+ - Maximum number of prefixes that will be accepted from the neighbor.
+ type: int
+ prevent_teardown:
+ description:
+ - Enable or disable teardown of BGP session when maximum prefix limit is exceeded.
+ type: bool
+ default: False
+ warning_threshold:
+ description:
+ - Threshold on number of prefixes that can be received from a neighbor before generation of warning messages.
+ - Expressed as a percentage of max-prefixes.
+ type: int
+ restart_timer:
+ description:
+ - Time interval in seconds after which the BGP session is re-established after being torn down.
+ type: int
+ prefix_list_in:
+ description:
+ - Inbound route filtering policy for a neighbor.
+ type: str
+ prefix_list_out:
+ description:
+ - Outbound route filtering policy for a neighbor.
+ type: str
+ route_map:
+ description:
+ - Specifies the route-map.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Specifies the name of the route-map.
+ type: str
+ direction:
+ description:
+ - Specifies the direction of the route-map.
+ type: str
+ route_reflector_client:
+ description:
+ - Specifies a neighbor as a route-reflector client.
+ type: bool
+ route_server_client:
+ description:
+ - Specifies a neighbor as a route-server client.
+ type: bool
+ state:
+ description:
+ - Specifies the operation to be performed on the BGP process that is configured on the device.
+ - In case of merged, the input configuration is merged with the existing BGP configuration on the device.
+ - In case of deleted, the existing BGP configuration is removed from the device.
+ default: merged
+ type: str
+ choices: ['merged', 'deleted']
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#!
+#router bgp 4
+# !
+# neighbor interface Eth1/3
+# !
+# address-family ipv4 unicast
+# activate
+# allowas-in 4
+# route-map aa in
+# route-map aa out
+# route-reflector-client
+# route-server-client
+# send-community both
+#!
+#
+- name: Deletes neighbors address-family with specific values
+ dellemc.enterprise_sonic.sonic_bgp_neighbors_af:
+ config:
+ - bgp_as: 4
+ neighbors:
+ - neighbor: Eth1/3
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 4
+ route_map:
+ - name: aa
+ direction: in
+ - name: aa
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ state: deleted
+
+# After state:
+# ------------
+#!
+#router bgp 4
+# !
+# neighbor interface Eth1/3
+# !
+# address-family ipv4 unicast
+# send-community both
+#!
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#!
+#router bgp 4
+# !
+# neighbor interface Eth1/3
+# !
+# address-family ipv4 unicast
+# activate
+# allowas-in 4
+# route-map aa in
+# route-map aa out
+# route-reflector-client
+# route-server-client
+# send-community both
+#!
+# neighbor interface Eth1/5
+# !
+# address-family ipv4 unicast
+# activate
+# allowas-in origin
+# send-community both
+#!
+#
+- name: Deletes neighbors address-family with specific values
+ dellemc.enterprise_sonic.sonic_bgp_neighbors_af:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#!
+#router bgp 4
+#!
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#!
+#router bgp 4
+# !
+# neighbor interface Eth1/3
+#!
+#
+- name: Merges neighbors address-family with specific values
+ dellemc.enterprise_sonic.sonic_bgp_neighbors_af:
+ config:
+ - bgp_as: 4
+ neighbors:
+ - neighbor: Eth1/3
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 4
+ route_map:
+ - name: aa
+ direction: in
+ - name: aa
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ state: merged
+
+# After state:
+# ------------
+#!
+#router bgp 4
+# !
+# neighbor interface Eth1/3
+# !
+# address-family ipv4 unicast
+# activate
+# allowas-in 4
+# route-map aa in
+# route-map aa out
+# route-reflector-client
+# route-server-client
+# send-community both
+#!
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+# sonic# show running-configuration bgp neighbor vrf default 1.1.1.1
+# (No bgp neighbor configuration present)
+- name: "Configure BGP neighbor prefix-list attributes"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors_af:
+ config:
+ - bgp_as: 51
+ neighbors:
+ - neighbor: 1.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 1
+ prevent_teardown: true
+ warning_threshold: 80
+ prefix_list_in: p1
+ prefix_list_out: p2
+ state: merged
+# After state:
+# ------------
+#
+# sonic# show running-configuration bgp neighbor vrf default 1.1.1.1
+# !
+# neighbor 1.1.1.1
+# !
+# address-family ipv4 unicast
+# default-originate route-map rmap_reg1
+# prefix-list p1 in
+# prefix-list p2 out
+# send-community both
+# maximum-prefix 1 80 warning-only
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# sonic# show running-configuration bgp neighbor vrf default 1.1.1.1
+# !
+# neighbor 1.1.1.1
+# !
+# address-family ipv6 unicast
+# default-originate route-map rmap_reg2
+# prefix-list p1 in
+# prefix-list p2 out
+# send-community both
+# maximum-prefix 5 90 restart 2
+- name: "Delete BGP neighbor prefix-list attributes"
+ dellemc.enterprise_sonic.sonic_bgp_neighbors_af:
+ config:
+ - bgp_as: 51
+ neighbors:
+ - neighbor: 1.1.1.1
+ address_family:
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 5
+ warning_threshold: 90
+ restart-timer: 2
+ prefix_list_in: p1
+ prefix_list_out: p2
+ state: deleted
+# sonic# show running-configuration bgp neighbor vrf default 1.1.1.1
+# (No bgp neighbor configuration present)
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.bgp_neighbors_af.bgp_neighbors_af import Bgp_neighbors_afArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.bgp_neighbors_af.bgp_neighbors_af import Bgp_neighbors_af
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Bgp_neighbors_afArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Bgp_neighbors_af(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_command.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_command.py
new file mode 100644
index 00000000..b0ce8781
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_command.py
@@ -0,0 +1,235 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Peter Sprygada <psprygada@ansible.com>
+# Copyright: (c) 2020, Dell 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: sonic_command
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Dhivya P (@dhivayp)
+short_description: Runs commands on devices running Enterprise SONiC
+description:
+ - Runs commands on remote devices running Enterprise SONiC Distribution
+ by Dell Technologies. Sends arbitrary commands to an Enterprise SONiC node and
+ returns the results that are read from the device. This module includes an
+ argument that causes the module to wait for a specific condition
+ before returning or time out if the condition is not met.
+ - This module does not support running commands in configuration mode.
+ To configure SONiC devices, use M(dellemc.enterprise_sonic.sonic_config).
+options:
+ commands:
+ description:
+ - List of commands to send to the remote Enterprise SONiC devices over the
+ configured provider. 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 retries has expired. If a command sent to the
+ device requires answering a prompt, it is possible to pass
+ a dict containing I(command), I(answer) and I(prompt).
+ Common answers are 'yes' or "\\r" (carriage return, must be
+ double quotes). See examples.
+ type: list
+ elements: str
+ required: true
+ wait_for:
+ description:
+ - List of conditions to evaluate against the output of the
+ command. The task waits for each condition to be true
+ before moving forward. If the conditional is not true
+ within the configured number of retries, the task fails.
+ See examples.
+ 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 wait_for must be satisfied. If
+ the value is set to C(any) then only one of the values must be
+ satisfied.
+ type: str
+ default: all
+ choices: [ 'all', 'any' ]
+ retries:
+ description:
+ - Specifies the number of retries a command should be run
+ before it is considered failed. The command is run on the
+ target device every retry and evaluated against the
+ I(wait_for) conditions.
+ type: int
+ default: 10
+ interval:
+ description:
+ - Configures the interval in seconds to wait between retries
+ of the command. If the command does not pass the specified
+ conditions, the interval indicates how long to wait before
+ trying the command again.
+ type: int
+ default: 1
+"""
+
+EXAMPLES = """
+ - name: Runs show version on remote devices
+ dellemc.enterprise_sonic.sonic_command:
+ commands: show version
+
+ - name: Runs show version and checks to see if output contains 'Dell'
+ dellemc.enterprise_sonic.sonic_command:
+ commands: show version
+ wait_for: result[0] contains Dell
+
+ - name: Runs multiple commands on remote nodes
+ dellemc.enterprise_sonic.sonic_command:
+ commands:
+ - show version
+ - show interface
+
+ - name: Runs multiple commands and evaluate the output
+ dellemc.enterprise_sonic.sonic_command:
+ commands:
+ - 'show version'
+ - 'show system'
+ wait_for:
+ - result[0] contains Dell
+ - result[1] contains Hostname
+
+ - name: Runs commands that require answering a prompt
+ dellemc.enterprise_sonic.sonic_command:
+ commands:
+ - command: 'reload'
+ prompt: '[confirm yes/no]: ?$'
+ answer: 'no'
+"""
+
+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: ['...', '...']
+warnings:
+ description: The list of warnings (if any) generated by module based on arguments.
+ returned: always
+ 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,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+ EntityCollection,
+ to_lines,
+)
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import run_commands
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import command_list_str_to_dict
+
+
+def transform_commands_dict(module, commands_dict):
+ transform = EntityCollection(
+ module,
+ dict(
+ command=dict(key=True),
+ output=dict(),
+ 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),
+ ),
+ )
+
+ return transform(commands_dict)
+
+
+def parse_commands(module, warnings):
+ commands_dict = command_list_str_to_dict(module, warnings, module.params["commands"])
+ commands = transform_commands_dict(module, commands_dict)
+ return commands
+
+
+def main():
+ """main entry point for module execution
+ """
+ argument_spec = dict(
+ # { command: <str>, prompt: <str>, response: <str> }
+ commands=dict(type='list', required=True, elements="str"),
+
+ wait_for=dict(type='list', elements="str"),
+ match=dict(default='all', choices=['all', 'any']),
+
+ retries=dict(default=10, type='int'),
+ interval=dict(default=1, type='int')
+ )
+
+ module = AnsibleModule(argument_spec=argument_spec,
+ supports_check_mode=True)
+ result = {'changed': False}
+
+ warnings = list()
+# check_args(module, warnings)
+ commands = parse_commands(module, warnings)
+ result['warnings'] = 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):
+ if item(responses):
+ if match == 'any':
+ conditionals = list()
+ break
+ conditionals.remove(item)
+
+ 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/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py
new file mode 100644
index 00000000..dd054419
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_config.py
@@ -0,0 +1,329 @@
+#!/usr/bin/python
+#
+# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2020 Dell 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: sonic_config
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Abirami N (@abirami-n)
+short_description: Manages configuration sections on devices running Enterprise SONiC
+description:
+ - Manages configuration sections of Enterprise SONiC Distribution
+ by Dell Technologies. SONiC configurations use a simple block indent
+ file syntax for segmenting configuration into sections. This module
+ provides an implementation for working with SONiC configuration
+ sections in a deterministic way.
+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-configuration. Be sure to note the configuration
+ command syntax as some commands are automatically modified by the
+ device configuration parser. This argument is mutually exclusive
+ with I(src).
+ type: list
+ elements: str
+ aliases: ['commands']
+ 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:
+ - Specifies the source path to the file that contains the configuration
+ or configuration template to load. The path to the source file can
+ either be the full path on the Ansible control host, or a relative
+ path from the playbook or role root directory. This argument is
+ mutually exclusive with I(lines).
+ type: path
+ 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
+ save:
+ description:
+ - The C(save) argument instructs the module to save the running-
+ configuration to the startup-configuration at the conclusion of
+ the module running. If check mode is specified, this argument
+ is ignored.
+ type: bool
+ default: 'no'
+ match:
+ description:
+ - Instructs the module on the way to perform the matching of
+ the set of commands against the current device configuration.
+ 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. If match is set to I(none), the
+ module does not attempt to compare the source configuration with
+ the running-configuration on the remote device.
+ type: str
+ default: line
+ choices: ['line', 'strict', 'exact', 'none']
+ replace:
+ description:
+ - Instructs the module how to perform a 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.
+ type: str
+ default: line
+ choices: ['line', 'block']
+ update:
+ description:
+ - The I(update) argument controls how the configuration statements
+ are processed on the remote device. Valid choices for the I(update)
+ argument are I(merge) and I(check). When you set this argument to
+ I(merge), the configuration changes merge with the current
+ device running-configuration. When you set this argument to I(check),
+ the configuration updates are determined but not configured
+ on the remote device.
+ type: str
+ default: merge
+ choices: ['merge', 'check']
+ config:
+ description:
+ - The module, by default, connects to the remote device and
+ retrieves the current running-configuration 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-configuration for every task in a playbook. The I(config)
+ argument allows the implementer to pass in the configuration to
+ use as the base configuration for comparison.
+ type: str
+ backup:
+ description:
+ - This argument causes the module to create a full backup of
+ the current C(running-configuration) 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. If the directory does not exist, it is created.
+ type: bool
+ default: 'no'
+ backup_options:
+ description:
+ - This is a dictionary object containing configurable options related to backup file path.
+ The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set
+ to I(no) this option is ignored.
+ suboptions:
+ filename:
+ description:
+ - The filename to be used to store the backup configuration. If the filename
+ is not given, it is generated based on the hostname, current time, and date
+ in the 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 is stored. If the directory does not exist it is first
+ 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,
+ an I(backup) directory is created in the current working directory
+ and backup configuration is copied in C(filename) within the I(backup) directory.
+ type: path
+ type: dict
+"""
+
+EXAMPLES = """
+- dellemc.enterprise_sonic.sonic_config:
+ lines: ['username {{ user_name }} password {{ user_password }} role {{ user_role }}']
+
+- dellemc.enterprise_sonic.sonic_config:
+ lines:
+ - description 'SONiC'
+ parents: ['interface Eth1/10']
+
+- dellemc.enterprise_sonic.sonic_config:
+ lines:
+ - seq 2 permit udp any any
+ - seq 3 deny icmp any any
+ parents: ['ip access-list test']
+ before: ['no ip access-list test']
+
+"""
+
+RETURN = """
+updates:
+ description: The set of commands that is pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['username foo password foo role admin', 'router bgp 1', 'router-id 1.1.1.1']
+commands:
+ description: The set of commands that is pushed to the remote device.
+ returned: always
+ type: list
+ sample: ['username foo password foo role admin', 'router bgp 1', 'router-id 1.1.1.1']
+saved:
+ description: Returns whether the configuration is saved to the startup
+ configuration or not.
+ returned: When not check_mode.
+ type: bool
+ sample: True
+"""
+
+from ansible.module_utils.connection import ConnectionError
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import get_config, get_sublevel_config
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import edit_config, run_commands
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import command_list_str_to_dict
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig, dumps
+
+
+def get_candidate(module):
+ candidate = NetworkConfig(indent=1)
+ if module.params['src']:
+ candidate.load(module.params['src'])
+ elif module.params['lines']:
+ parents = module.params['parents'] or list()
+ commands = module.params['lines'][0]
+ if (isinstance(commands, dict)) and (isinstance((commands['command']), list)):
+ candidate.add(commands['command'], parents=parents)
+ elif (isinstance(commands, dict)) and (isinstance((commands['command']), str)):
+ candidate.add([commands['command']], parents=parents)
+ else:
+ candidate.add(module.params['lines'], parents=parents)
+ return candidate
+
+
+def get_running_config(module):
+ contents = module.params['config']
+ if not contents:
+ contents = get_config(module)
+ return contents
+
+
+def main():
+
+ backup_spec = dict(
+ filename=dict(),
+ dir_path=dict(type='path')
+ )
+
+ argument_spec = dict(
+ lines=dict(aliases=['commands'], type='list', elements="str"),
+ parents=dict(type='list', elements="str"),
+
+ src=dict(type='path'),
+
+ before=dict(type='list', elements="str"),
+ after=dict(type='list', elements="str"),
+ save=dict(type='bool', default=False),
+ match=dict(default='line',
+ choices=['line', 'strict', 'exact', 'none']),
+ replace=dict(default='line', choices=['line', 'block']),
+
+ update=dict(choices=['merge', 'check'], default='merge'),
+ config=dict(),
+ backup=dict(type='bool', default=False),
+ backup_options=dict(type='dict', options=backup_spec)
+
+ )
+
+ mutually_exclusive = [('lines', 'src')]
+
+ module = AnsibleModule(argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True)
+ parents = module.params['parents'] or list()
+ match = module.params['match']
+ replace = module.params['replace']
+
+ warnings = list()
+# check_args(module, warnings)
+
+ result = dict(changed=False, saved=False, warnings=warnings)
+ if module.params['backup']:
+ if not module.check_mode:
+ result['__backup__'] = get_config(module)
+
+ commands = list()
+ candidate = get_candidate(module)
+ if any((module.params['lines'], module.params['src'])):
+ if match != 'none':
+ config = get_running_config(module)
+ if parents:
+ contents = get_sublevel_config(config, module)
+ config = NetworkConfig(contents=contents, indent=1)
+ else:
+ config = NetworkConfig(contents=config, indent=1)
+ configobjs = candidate.difference(config, match=match, replace=replace)
+ else:
+
+ configobjs = candidate.items
+ if configobjs:
+ commands = dumps(configobjs, 'commands')
+ if ((isinstance((module.params['lines']), list)) and
+ (isinstance((module.params['lines'][0]), dict)) and
+ (set(['prompt', 'answer']).issubset(module.params['lines'][0]))):
+
+ cmd = {'command': commands,
+ 'prompt': module.params['lines'][0]['prompt'],
+ 'answer': module.params['lines'][0]['answer']}
+ commands = [cmd]
+ else:
+ commands = commands.split('\n')
+ cmd_list_out = command_list_str_to_dict(module, warnings, commands)
+ if cmd_list_out and cmd_list_out != []:
+ commands = cmd_list_out
+
+ if module.params['before']:
+ commands[:0] = module.params['before']
+
+ if module.params['after']:
+ commands.extend(module.params['after'])
+
+ if not module.check_mode and module.params['update'] == 'merge':
+ try:
+ edit_config(module, commands)
+ except ConnectionError as exc:
+ module.fail_json(msg=to_text(exc))
+
+ result['changed'] = True
+ result['commands'] = commands
+ result['updates'] = commands
+
+ if module.params['save']:
+ result['changed'] = True
+ if not module.check_mode:
+ cmd = {r'command': ' write memory'}
+ run_commands(module, [cmd])
+ result['saved'] = True
+
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py
new file mode 100644
index 00000000..f13e9def
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_facts.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The module file for sonic_facts
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_facts
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Collects facts on devices running Enterprise SONiC
+description:
+ - Collects facts from devices running Enterprise SONiC Distribution by
+ Dell Technologies. This module places the facts gathered in the fact tree
+ keyed by the respective resource name. The facts module always collects
+ a base set of facts from the device and can enable or disable collection
+ of additional facts.
+author:
+- Mohamed Javeed (@javeedf)
+- Abirami N (@abirami-n)
+options:
+ gather_subset:
+ description:
+ - When supplied, this argument restricts the facts collected
+ to a given subset. Possible values for this argument include
+ all, min, hardware, config, legacy, and interfaces. Can specify a
+ list of values to include a larger subset. Values can also be used
+ with an initial '!' to specify that a specific subset should
+ not be collected.
+ required: false
+ type: list
+ elements: str
+ default: '!config'
+ gather_network_resources:
+ description:
+ - When supplied, this argument restricts the facts collected
+ to a given subset. Possible values for this argument include
+ all and the resources like 'all', 'interfaces', 'vlans', 'lag_interfaces', 'l2_interfaces', 'l3_interfaces'.
+ Can specify a list of values to include a larger subset. Values
+ can also be used with an initial '!' to specify that a
+ specific subset should not be collected.
+ required: false
+ type: list
+ elements: str
+ choices:
+ - all
+ - vlans
+ - interfaces
+ - l2_interfaces
+ - l3_interfaces
+ - lag_interfaces
+ - bgp
+ - bgp_af
+ - bgp_neighbors
+ - bgp_neighbors_af
+ - bgp_as_paths
+ - bgp_communities
+ - bgp_ext_communities
+ - mclag
+ - prefix_lists
+ - vrfs
+ - vxlans
+ - users
+ - system
+ - port_breakout
+ - aaa
+ - tacacs_server
+ - radius_server
+ - static_routes
+ - ntp
+"""
+
+EXAMPLES = """
+- name: Gather all facts
+ dellemc.enterprise_sonic.sonic_facts:
+ gather_subset: all
+ gather_network_resources: all
+- name: Collects VLAN and interfaces facts
+ dellemc.enterprise_sonic.sonic_facts:
+ gather_subset:
+ - min
+ gather_network_resources:
+ - vlans
+ - interfaces
+- name: Do not collects VLAN and interfaces facts
+ dellemc.enterprise_sonic.sonic_facts:
+ gather_network_resources:
+ - "!vlans"
+ - "!interfaces"
+- name: Collects VLAN and minimal default facts
+ dellemc.enterprise_sonic.sonic_facts:
+ gather_subset: min
+ gather_network_resources: vlans
+- name: Collect lag_interfaces and minimal default facts
+ dellemc.enterprise_sonic.sonic_facts:
+ gather_subset: min
+ gather_network_resources: lag_interfaces
+"""
+
+RETURN = """
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.facts.facts import FactsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
+
+
+def main():
+ """
+ Main entry point for module execution
+ :returns: ansible_facts
+ """
+ module = AnsibleModule(argument_spec=FactsArgs.argument_spec,
+ supports_check_mode=True)
+ warnings = ['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/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py
new file mode 100644
index 00000000..0cd6a189
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_interfaces.py
@@ -0,0 +1,230 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_interfaces
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Configure Interface attributes on interfaces such as, Eth, LAG, VLAN, and loopback.
+ (create a loopback interface if it does not exist.)
+description: Configure Interface attributes such as, MTU, admin statu, and so on, on interfaces
+ such as, Eth, LAG, VLAN, and loopback. (create a loopback interface if it does not exist.)
+author: Niraimadaiselvam M(@niraimadaiselvamm)
+options:
+ config:
+ description: A list of interface configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ type: str
+ description: The name of the interface, for example, 'Eth1/15'.
+ required: true
+ description:
+ type: str
+ description:
+ - Description about the interface.
+ enabled:
+ description:
+ - Administrative state of the interface.
+ type: bool
+ mtu:
+ description:
+ - MTU of the interface.
+ type: int
+ state:
+ description:
+ - The state the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# show interface status | no-more
+#------------------------------------------------------------------------------------------
+#Name Description Admin Oper Speed MTU
+#------------------------------------------------------------------------------------------
+#Eth1/1 - up 100000 9100
+#Eth1/2 - up 100000 9100
+#Eth1/3 - down 100000 9100
+#Eth1/3 - down 1000 5000
+#Eth1/5 - down 100000 9100
+#
+- name: Configures interfaces
+ dellemc.enterprise_sonic.sonic_interfaces:
+ config:
+ name: Eth1/3
+ state: deleted
+#
+# After state:
+# -------------
+#
+# show interface status | no-more
+#------------------------------------------------------------------------------------------
+#Name Description Admin Oper Speed MTU
+#------------------------------------------------------------------------------------------
+#Eth1/1 - up 100000 9100
+#Eth1/2 - up 100000 9100
+#Eth1/3 - down 100000 9100
+#Eth1/3 - up 100000 9100
+#Eth1/5 - down 100000 9100
+#
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# show interface status | no-more
+#------------------------------------------------------------------------------------------
+#Name Description Admin Oper Speed MTU
+#------------------------------------------------------------------------------------------
+#Eth1/1 - up 100000 9100
+#Eth1/2 - up 100000 9100
+#Eth1/3 - down 100000 9100
+#Eth1/3 - down 1000 9100
+#Eth1/5 - down 100000 9100
+#
+
+- name: Configures interfaces
+ dellemc.enterprise_sonic.sonic_interfaces:
+ config:
+ state: deleted
+
+#
+# After state:
+# -------------
+#
+# show interface status | no-more
+#------------------------------------------------------------------------------------------
+#Name Description Admin Oper Speed MTU
+#------------------------------------------------------------------------------------------
+#Eth1/1 - up 100000 9100
+#Eth1/2 - up 100000 9100
+#Eth1/3 - up 100000 9100
+#Eth1/3 - up 100000 9100
+#Eth1/5 - up 100000 9100
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+# show interface status | no-more
+#------------------------------------------------------------------------------------------
+#Name Description Admin Oper Speed MTU
+#------------------------------------------------------------------------------------------
+#Eth1/1 - up 100000 9100
+#Eth1/2 - up 100000 9100
+#Eth1/3 - down 100000 9100
+#Eth1/3 - down 1000 9100
+#
+- name: Configures interfaces
+ dellemc.enterprise_sonic.sonic_interfaces:
+ config:
+ - name: Eth1/3
+ description: 'Ethernet Twelve'
+ - name: Eth1/5
+ description: 'Ethernet Sixteen'
+ enable: True
+ mtu: 3500
+ state: merged
+#
+#
+# After state:
+# ------------
+#
+# show interface status | no-more
+#------------------------------------------------------------------------------------------
+#Name Description Admin Oper Speed MTU
+#------------------------------------------------------------------------------------------
+#Eth1/1 - up 100000 9100
+#Eth1/2 - up 100000 9100
+#Eth1/3 - down 100000 9100
+#Eth1/4 - down 1000 9100
+#Eth1/5 - down 100000 3500
+#
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.interfaces.interfaces import InterfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.interfaces.interfaces import Interfaces
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=InterfacesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py
new file mode 100644
index 00000000..34a8ff72
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l2_interfaces.py
@@ -0,0 +1,296 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_l2_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_l2_interfaces
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Configure interface-to-VLAN association that is based on access or trunk mode
+description: Manages Layer 2 interface attributes of Enterprise SONiC Distribution by Dell Technologies.
+author: Niraimadaiselvam M(@niraimadaiselvamm)
+options:
+ config:
+ description: A list of Layer 2 interface configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ type: str
+ description: Full name of the interface, for example, 'Eth1/26'.
+ required: true
+ trunk:
+ type: dict
+ description: Configures trunking parameters on an interface.
+ suboptions:
+ allowed_vlans:
+ description: Specifies list of allowed VLANs of trunk mode on the interface.
+ type: list
+ elements: dict
+ suboptions:
+ vlan:
+ type: int
+ description: Configures the specified VLAN in trunk mode.
+ access:
+ type: dict
+ description: Configures access mode characteristics of the interface.
+ suboptions:
+ vlan:
+ type: int
+ description: Configures the specified VLAN in access mode.
+ state:
+ type: str
+ description: The state that the configuration should be left in.
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive A Eth1/3
+#11 Inactive T Eth1/3
+#12 Inactive A Eth1/4
+#13 Inactive T Eth1/4
+#14 Inactive A Eth1/5
+#15 Inactive T Eth1/5
+#
+- name: Configures switch port of interfaces
+ dellemc.enterprise_sonic.sonic_l2_interfaces:
+ config:
+ - name: Eth1/3
+ - name: Eth1/4
+ state: deleted
+#
+# After state:
+# ------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#11 Inactive
+#12 Inactive
+#13 Inactive
+#14 Inactive A Eth1/5
+#15 Inactive T Eth1/5
+#
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive A Eth1/3
+#11 Inactive T Eth1/3
+#12 Inactive A Eth1/4
+#13 Inactive T Eth1/4
+#14 Inactive A Eth1/5
+#15 Inactive T Eth1/5
+#
+- name: Configures switch port of interfaces
+ dellemc.enterprise_sonic.sonic_l2_interfaces:
+ config:
+ state: deleted
+#
+# After state:
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#11 Inactive
+#12 Inactive
+#13 Inactive
+#14 Inactive
+#15 Inactive
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#11 Inactive T Eth1/7
+#12 Inactive T Eth1/7
+#
+- name: Configures switch port of interfaces
+ dellemc.enterprise_sonic.sonic_l2_interfaces:
+ config:
+ - name: Eth1/3
+ access:
+ vlan: 10
+ state: merged
+#
+# After state:
+# ------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive A Eth1/3
+#11 Inactive T Eth1/7
+#12 Inactive T Eth1/7
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive A Eth1/3
+#
+- name: Configures switch port of interfaces
+ dellemc.enterprise_sonic.sonic_l2_interfaces:
+ config:
+ - name: Eth1/3
+ trunk:
+ allowed_vlans:
+ - vlan: 11
+ - vlan: 12
+ state: merged
+#
+# After state:
+# ------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive A Eth1/3
+#11 Inactive T Eth1/7
+#12 Inactive T Eth1/7
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#11 Inactive
+#12 Inactive A Eth1/4
+#13 Inactive T Eth1/4
+#14 Inactive A Eth1/5
+#15 Inactive T Eth1/5
+#
+- name: Configures switch port of interfaces
+ dellemc.enterprise_sonic.sonic_l2_interfaces:
+ config:
+ - name: Eth1/3
+ access:
+ vlan: 12
+ trunk:
+ allowed_vlans:
+ - vlan: 13
+ - vlan: 14
+ state: merged
+#
+# After state:
+# ------------
+#
+#do show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#11 Inactive
+#12 Inactive A Eth1/3
+# A Eth1/4
+#13 Inactive T Eth1/3
+# T Eth1/4
+#14 Inactive A Eth1/3
+# A Eth1/5
+#15 Inactive T Eth1/5
+#
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l2_interfaces.l2_interfaces import L2_interfaces
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=L2_interfacesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = L2_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py
new file mode 100644
index 00000000..e796897a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_l3_interfaces.py
@@ -0,0 +1,375 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_l3_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_l3_interfaces
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Configure the IPv4 and IPv6 parameters on Interfaces such as, Eth, LAG, VLAN, and loopback
+description:
+ - Configures Layer 3 interface settings on devices running Enterprise SONiC
+ Distribution by Dell Technologies. This module provides configuration management
+ of IPv4 and IPv6 parameters on Ethernet interfaces of devices running Enterprise SONiC.
+author: Kumaraguru Narayanan (@nkumaraguru)
+options:
+ config:
+ description: A list of l3_interfaces configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ required: True
+ type: str
+ description:
+ - Full name of the interface, for example, Eth1/3.
+ ipv4:
+ description:
+ - ipv4 configurations to be set for the Layer 3 interface mentioned in name option.
+ type: dict
+ suboptions:
+ addresses:
+ description:
+ - List of IPv4 addresses to be set.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description:
+ - IPv4 address to be set in the format <ipv4 address>/<mask>
+ for example, 192.0.2.1/24.
+ type: str
+ secondary:
+ description:
+ - secondary flag of the ip address.
+ type: bool
+ default: 'False'
+ anycast_addresses:
+ description:
+ - List of IPv4 addresses to be set for anycast.
+ type: list
+ elements: str
+ ipv6:
+ description:
+ - ipv6 configurations to be set for the Layer 3 interface mentioned in name option.
+ type: dict
+ suboptions:
+ addresses:
+ description:
+ - List of IPv6 addresses to be set.
+ type: list
+ elements: dict
+ suboptions:
+ address:
+ description:
+ - IPv6 address to be set in the address format is <ipv6 address>/<mask>
+ for example, 2001:db8:2201:1::1/64.
+ type: str
+ enabled:
+ description:
+ - enabled flag of the ipv6.
+ type: bool
+ state:
+ description:
+ - The state that the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#rno-dctor-1ar01c01sw02# show running-configuration interface
+#!
+#interface Ethernet20
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 83.1.1.1/16
+# ip address 84.1.1.1/16 secondary
+# ipv6 address 83::1/16
+# ipv6 address 84::1/16
+# ipv6 enable
+#!
+#interface Ethernet24
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 91.1.1.1/16
+# ip address 92.1.1.1/16 secondary
+# ipv6 address 90::1/16
+# ipv6 address 91::1/16
+# ipv6 address 92::1/16
+# ipv6 address 93::1/16
+#!
+#interface Vlan501
+# ip anycast-address 11.12.13.14/12
+# ip anycast-address 1.2.3.4/22
+#!
+#
+#
+- name: delete one l3 interface.
+ dellemc.enterprise_sonic.sonic_l3_interfaces:
+ config:
+ - name: Ethernet20
+ ipv4:
+ addresses:
+ - address: 83.1.1.1/16
+ - address: 84.1.1.1/16
+ - name: Ethernet24
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 91::1/16
+ - name: Vlan501
+ ipv4:
+ anycast_addresses:
+ - 11.12.13.14/12
+ state: deleted
+
+# After state:
+# ------------
+#
+#rno-dctor-1ar01c01sw02# show running-configuration interface
+#!
+#interface Ethernet20
+# mtu 9100
+# speed 100000
+# shutdown
+# ipv6 address 83::1/16
+# ipv6 address 84::1/16
+# ipv6 enable
+#!
+#interface Ethernet24
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 91.1.1.1/16
+# ip address 92.1.1.1/16 secondary
+# ipv6 address 90::1/16
+# ipv6 address 92::1/16
+# ipv6 address 93::1/16
+#!
+#interface Vlan501
+# ip anycast-address 1.2.3.4/22
+#!
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#rno-dctor-1ar01c01sw02# show running-configuration interface
+#!
+#interface Ethernet20
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 83.1.1.1/16
+# ip address 84.1.1.1/16 secondary
+# ipv6 address 83::1/16
+# ipv6 address 84::1/16
+# ipv6 enable
+#!
+#interface Ethernet24
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 91.1.1.1/16
+# ipv6 address 90::1/16
+# ipv6 address 91::1/16
+# ipv6 address 92::1/16
+# ipv6 address 93::1/16
+#!
+#interface Vlan501
+# ip anycast-address 11.12.13.14/12
+# ip anycast-address 1.2.3.4/22
+#!
+#
+#
+- name: delete all l3 interface
+ dellemc.enterprise_sonic.sonic_l3_interfaces:
+ config:
+ state: deleted
+#
+# After state:
+# ------------
+#
+#rno-dctor-1ar01c01sw02# show running-configuration interface
+#!
+#interface Ethernet20
+# mtu 9100
+# speed 100000
+# shutdown
+#!
+#interface Ethernet24
+# mtu 9100
+# speed 100000
+# shutdown
+#!
+#interface Vlan501
+#!
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#rno-dctor-1ar01c01sw02# show running-configuration interface
+#!
+#interface Ethernet20
+# mtu 9100
+# speed 100000
+# shutdown
+#!
+#interface Ethernet24
+# mtu 9100
+# speed 100000
+# shutdown
+#!
+#interface Vlan501
+# ip anycast-address 1.2.3.4/22
+#!
+#
+- name: Add l3 interface configurations
+ dellemc.enterprise_sonic.sonic_l3_interfaces:
+ config:
+ - name: Ethernet20
+ ipv4:
+ addresses:
+ - address: 83.1.1.1/16
+ - address: 84.1.1.1/16
+ secondary: True
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 83::1/16
+ - address: 84::1/16
+ secondary: True
+ - name: Ethernet24
+ ipv4:
+ addresses:
+ - address: 91.1.1.1/16
+ ipv6:
+ addresses:
+ - address: 90::1/16
+ - address: 91::1/16
+ - address: 92::1/16
+ - address: 93::1/16
+ - name: Vlan501
+ ipv4:
+ anycast_addresses:
+ - 11.12.13.14/12
+ state: merged
+#
+# After state:
+# ------------
+#
+#rno-dctor-1ar01c01sw02# show running-configuration interface
+#!
+#interface Ethernet20
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 83.1.1.1/16
+# ip address 84.1.1.1/16 secondary
+# ipv6 address 83::1/16
+# ipv6 address 84::1/16
+# ipv6 enable
+#!
+#interface Ethernet24
+# mtu 9100
+# speed 100000
+# shutdown
+# ip address 91.1.1.1/16
+# ipv6 address 90::1/16
+# ipv6 address 91::1/16
+# ipv6 address 92::1/16
+# ipv6 address 93::1/16
+#!
+#interface Vlan501
+# ip anycast-address 1.2.3.4/22
+# ip anycast-address 11.12.13.14/12
+#!
+#
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.l3_interfaces.l3_interfaces import L3_interfaces
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=L3_interfacesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = L3_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py
new file mode 100644
index 00000000..630db798
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_lag_interfaces.py
@@ -0,0 +1,238 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_lag_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_lag_interfaces
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage link aggregation group (LAG) interface parameters
+description:
+ - This module manages attributes of link aggregation group (LAG) interfaces of
+ devices running Enterprise SONiC Distribution by Dell Technologies.
+author: Abirami N (@abirami-n)
+
+options:
+ config:
+ description: A list of LAG configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - ID of the LAG.
+ type: str
+ required: True
+ members:
+ description:
+ - The list of interfaces that are part of the group.
+ type: dict
+ suboptions:
+ interfaces:
+ 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:
+ - Specifies mode of the port-channel while creation.
+ type: str
+ choices:
+ - static
+ - lacp
+ state:
+ description:
+ - The state that the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+#
+# Before state:
+# -------------
+#
+# interface Eth1/10
+# mtu 9100
+# speed 100000
+# no shutdown
+# !
+# interface Eth1/15
+# channel-group 12
+# mtu 9100
+# speed 100000
+# no shutdown
+#
+- name: Merges provided configuration with device configuration
+ dellemc.enterprise_sonic.sonic_lag_interfaces:
+ config:
+ - name: PortChannel10
+ members:
+ interfaces:
+ - member: Eth1/10
+ state: merged
+#
+# After state:
+# ------------
+#
+# interface Eth1/10
+# channel-group 10
+# mtu 9100
+# speed 100000
+# no shutdown
+# !
+# interface Eth1/15
+# channel-group 12
+# mtu 9100
+# speed 100000
+# no shutdown
+#
+# Using deleted
+#
+# Before state:
+# -------------
+# interface PortChannel10
+# !
+# interface Eth1/10
+# channel-group 10
+# mtu 9100
+# speed 100000
+# no shutdown
+#
+- name: Deletes LAG attributes of a given interface, This does not delete the port-channel itself
+ dellemc.enterprise_sonic.sonic_lag_interfaces:
+ config:
+ - name: PortChannel10
+ members:
+ interfaces:
+ state: deleted
+#
+# After state:
+# ------------
+# interface PortChannel10
+# !
+# interface Eth1/10
+# mtu 9100
+# speed 100000
+# no shutdown
+#
+# Using deleted
+#
+# Before state:
+# -------------
+# interface PortChannel 10
+# !
+# interface PortChannel 12
+# !
+# interface Eth1/10
+# channel-group 10
+# mtu 9100
+# speed 100000
+# no shutdown
+# !
+# interface Eth1/15
+# channel-group 12
+# mtu 9100
+# speed 100000
+# no shutdown
+#
+- name: Deletes all LAGs and LAG attributes of all interfaces
+ dellemc.enterprise_sonic.sonic_lag_interfaces:
+ config:
+ state: deleted
+#
+# After state:
+# -------------
+#
+# interface Eth1/10
+# mtu 9100
+# speed 100000
+# no shutdown
+# !
+# interface Eth1/15
+# mtu 9100
+# speed 100000
+# no shutdown
+#
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration that is returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.lag_interfaces.lag_interfaces import Lag_interfaces
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Lag_interfacesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Lag_interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py
new file mode 100644
index 00000000..28d3dbb5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_mclag.py
@@ -0,0 +1,516 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_mclag
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_mclag
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage multi chassis link aggregation groups domain (MCLAG) and its parameters
+description:
+ - Manage multi chassis link aggregation groups domain (MCLAG) and its parameters
+author: Abirami N (@abirami-n)
+
+options:
+ config:
+ description: Dict of mclag domain configurations.
+ type: dict
+ suboptions:
+ domain_id:
+ description:
+ - ID of the mclag domain (MCLAG domain).
+ type: int
+ required: True
+ peer_address:
+ description:
+ - The IPV4 peer-ip for corresponding MCLAG.
+ type: str
+ source_address:
+ description:
+ - The IPV4 source-ip for corresponding MCLAG.
+ type: str
+ peer_link:
+ description:
+ - Peer-link for corresponding MCLAG.
+ type: str
+ system_mac:
+ description:
+ - Mac address of MCLAG.
+ type: str
+ keepalive:
+ description:
+ - MCLAG session keepalive-interval in secs.
+ type: int
+ session_timeout:
+ description:
+ - MCLAG session timeout value in secs.
+ type: int
+ unique_ip:
+ description: Holds Vlan dictionary for mclag unique ip.
+ suboptions:
+ vlans:
+ description:
+ - Holds list of VLANs for which a separate IP addresses is enabled for Layer 3 protocol support over MCLAG.
+ type: list
+ elements: dict
+ suboptions:
+ vlan:
+ description: Holds a VLAN ID.
+ type: str
+ type: dict
+ members:
+ description: Holds portchannels dictionary for an MCLAG domain.
+ suboptions:
+ portchannels:
+ description:
+ - Holds a list of portchannels for configuring as an MCLAG interface.
+ type: list
+ elements: dict
+ suboptions:
+ lag:
+ description: Holds a PortChannel ID.
+ type: str
+ type: dict
+ state:
+ description:
+ - The state that the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+#
+# Before state:
+# -------------
+#
+# sonic# show mclag brief
+# MCLAG Not Configured
+#
+- name: Merge provided configuration with device configuration
+ dellemc.enterprise_sonic.sonic_mclag:
+ config:
+ domain_id: 1
+ peer_address: 1.1.1.1
+ source_address: 2.2.2.2
+ peer_link: 'Portchannel1'
+ keepalive: 1
+ session_timeout: 3
+ unique_ip:
+ vlans:
+ - vlan: Vlan4
+ members:
+ portchannles:
+ - lag: PortChannel10
+ state: merged
+#
+# After state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address : 2.2.2.2
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 1 secs
+# Session Timeout : 3 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:1
+#-----------------------------------------------------------
+# MLAG Interface Local/Remote Status
+#-----------------------------------------------------------
+# PortChannel10 down/down
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+#
+# Using merged
+#
+# Before state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address : 2.2.2.2
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 1 secs
+# Session Timeout : 3 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:1
+#-----------------------------------------------------------
+# MLAG Interface Local/Remote Status
+#-----------------------------------------------------------
+# PortChannel10 down/down
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+#
+- name: Merge device configuration with the provided configuration
+ dellemc.enterprise_sonic.sonic_mclag:
+ config:
+ domain_id: 1
+ source_address: 3.3.3.3
+ keepalive: 10
+ session_timeout: 30
+ unique_ip:
+ vlans:
+ - vlan: Vlan5
+ members:
+ portchannels:
+ - lag: PortChannel12
+ state: merged
+#
+# After state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address : 3.3.3.3
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 10 secs
+# Session Timeout : 30 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:2
+#-----------------------------------------------------------
+# MLAG Interface Local/Remote Status
+#-----------------------------------------------------------
+# PortChannel10 down/down
+# PortChannel12 down/down
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# },
+# "Vlan5": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+#
+# Using deleted
+#
+# Before state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address : 3.3.3.3
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 10 secs
+# Session Timeout : 30 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:1
+#-----------------------------------------------------------
+# MLAG Interface Local/Remote Status
+#-----------------------------------------------------------
+# PortChannel10 down/down
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+- name: Delete device configuration based on the provided configuration
+ dellemc.enterprise_sonic.sonic_mclag:
+ config:
+ domain_id: 1
+ source_address: 3.3.3.3
+ keepalive: 10
+ members:
+ portchannels:
+ - lag: PortChannel10
+ state: deleted
+#
+# After state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address :
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 1 secs
+# Session Timeout : 15 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:0
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+#
+#
+# Using deleted
+#
+# Before state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address : 3.3.3.3
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 10 secs
+# Session Timeout : 30 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:1
+#-----------------------------------------------------------
+# MLAG Interface Local/Remote Status
+#-----------------------------------------------------------
+# PortChannel10 down/down
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+- name: Delete all device configuration
+ dellemc.enterprise_sonic.sonic_mclag:
+ config:
+ state: deleted
+#
+# After state:
+# ------------
+#
+# sonic# show mclag brief
+# MCLAG Not Configured
+#
+# admin@sonic:~$ show runningconfiguration all | grep MCLAG_UNIQUE_IP
+# admin@sonic:~$
+#
+#
+# Using deleted
+#
+# Before state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address : 3.3.3.3
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 10 secs
+# Session Timeout : 30 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:2
+#-----------------------------------------------------------
+# MLAG Interface Local/Remote Status
+#-----------------------------------------------------------
+# PortChannel10 down/down
+# PortChannel12 down/sown
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+- name: Delete device configuration based on the provided configuration
+ dellemc.enterprise_sonic.sonic_mclag:
+ config:
+ domain_id: 1
+ source_address: 3.3.3.3
+ keepalive: 10
+ members:
+ portchannels:
+ - lag: PortChannel10
+ state: deleted
+#
+# After state:
+# ------------
+#
+# sonic# show mclag brief
+#
+# Domain ID : 1
+# Role : standby
+# Session Status : down
+# Peer Link Status : down
+# Source Address :
+# Peer Address : 1.1.1.1
+# Peer Link : PortChannel1
+# Keepalive Interval : 1 secs
+# Session Timeout : 15 secs
+# System Mac : 20:04:0f:37:bd:c9
+#
+#
+# Number of MLAG Interfaces:0
+#
+# admin@sonic:~$ show runningconfiguration all
+# {
+# ...
+# "MCLAG_UNIQUE_IP": {
+# "Vlan4": {
+# "unique_ip": "enable"
+# }
+# },
+# ...
+# }
+#
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.mclag.mclag import MclagArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.mclag.mclag import Mclag
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=MclagArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Mclag(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py
new file mode 100644
index 00000000..87db8bb0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_ntp.py
@@ -0,0 +1,360 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_ntp
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: sonic_ntp
+version_added: 2.0.0
+short_description: Manage NTP configuration on SONiC.
+description:
+ - This module provides configuration management of NTP for devices running SONiC.
+author: "M. Zhang (@mingjunzhang2019)"
+options:
+ config:
+ description:
+ - Specifies NTP related configurations.
+ type: dict
+ suboptions:
+ source_interfaces:
+ type: list
+ elements: str
+ description:
+ - List of names of NTP source interfaces.
+ enable_ntp_auth:
+ type: bool
+ description:
+ - Enable or disable NTP authentication.
+ trusted_keys:
+ type: list
+ elements: int
+ description:
+ - List of trusted NTP authentication keys.
+ vrf:
+ type: str
+ description:
+ - VRF name on which NTP is enabled.
+ servers:
+ type: list
+ elements: dict
+ description:
+ - List of NTP servers.
+ suboptions:
+ address:
+ type: str
+ description:
+ - IPv4/IPv6 address or host name of NTP server.
+ required: true
+ key_id:
+ type: int
+ description:
+ - NTP authentication key used by server.
+ - Key_id can not be deleted.
+ minpoll:
+ type: int
+ description:
+ - Minimum poll interval to poll NTP server.
+ - minpoll can not be deleted.
+ maxpoll:
+ type: int
+ description:
+ - Maximum poll interval to poll NTP server.
+ - maxpoll can not be deleted.
+ ntp_keys:
+ type: list
+ elements: dict
+ description:
+ - List of NTP authentication keys.
+ suboptions:
+ key_id:
+ type: int
+ description:
+ - NTP authentication key identifier.
+ required: true
+ key_type:
+ type: str
+ description:
+ - NTP authentication key type.
+ - key_type can not be deleted.
+ - When "state" is "merged", "key_type" is required.
+ choices:
+ - NTP_AUTH_SHA1
+ - NTP_AUTH_MD5
+ - NTP_AUTH_SHA2_256
+ key_value:
+ type: str
+ description:
+ - NTP authentication key value.
+ - key_value can not be deleted.
+ - When "state" is "merged", "key_value" is required.
+ encrypted:
+ type: bool
+ description:
+ - NTP authentication key_value is encrypted.
+ - encrypted can not be deleted.
+ - When "state" is "merged", "encrypted" is required.
+
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#sonic# show ntp server
+#----------------------------------------------------------------------
+#NTP Servers minpoll maxpoll Authentication key ID
+#----------------------------------------------------------------------
+#10.11.0.1 6 10
+#10.11.0.2 5 9
+#dell.com 6 9
+#dell.org 7 10
+#
+- name: Delete NTP server configuration
+ ntp:
+ config:
+ servers:
+ - address: 10.11.0.2
+ - address: dell.org
+ state: deleted
+
+# After state:
+# ------------
+#
+#sonic# show ntp server
+#----------------------------------------------------------------------
+#NTP Servers minpoll maxpoll Authentication key ID
+#----------------------------------------------------------------------
+#10.11.0.1 6 10
+#dell.com 6 9
+#
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#sonic# show ntp global
+#----------------------------------------------
+#NTP Global Configuration
+#----------------------------------------------
+#NTP source-interfaces: Ethernet0, Ethernet4, Ethernet8, Ethernet16
+#
+- name: Delete NTP source-interface configuration
+ ntp:
+ config:
+ source_interfaces:
+ - Ethernet8
+ - Ethernet16
+ state: deleted
+
+# After state:
+# ------------
+#
+#sonic# show ntp global
+#----------------------------------------------
+#NTP Global Configuration
+#----------------------------------------------
+#NTP source-interfaces: Ethernet0, Ethernet4
+#
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#sonic# show running-configuration | grep ntp
+#ntp authentication-key 8 sha1 U2FsdGVkX1/NpJrdOeyMeUHEkSohY6azY9VwbAqXRTY= encrypted
+#ntp authentication-key 10 md5 U2FsdGVkX1/Gxds/5pscCvIKbVngGaKka4SQineS51Y= encrypted
+#ntp authentication-key 20 sha2-256 U2FsdGVkX1/eAzKj1teKhYWD7tnzOsYOijGeFAT0rKM= encrypted
+#
+- name: Delete NTP key configuration
+ ntp:
+ config:
+ ntp_keys:
+ - key_id: 10
+ - key_id: 20
+ state: deleted
+#
+# After state:
+# ------------
+#
+#sonic# show running-configuration | grep ntp
+#ntp authentication-key 8 sha1 U2FsdGVkX1/NpJrdOeyMeUHEkSohY6azY9VwbAqXRTY= encrypted
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#sonic# show ntp server
+#----------------------------------------------------------------------
+#NTP Servers minpoll maxpoll Authentication key ID
+#----------------------------------------------------------------------
+#10.11.0.1 6 10
+#dell.com 6 9
+#
+- name: Merge NTP server configuration
+ ntp:
+ config:
+ servers:
+ - address: 10.11.0.2
+ minpoll: 5
+ - address: dell.org
+ minpoll: 7
+ maxpoll: 10
+ state: merged
+
+# After state:
+# ------------
+#
+#sonic# show ntp server
+#----------------------------------------------------------------------
+#NTP Servers minpoll maxpoll Authentication key ID
+#----------------------------------------------------------------------
+#10.11.0.1 6 10
+#10.11.0.2 5 9
+#dell.com 6 9
+#dell.org 7 10
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#sonic# show ntp global
+#----------------------------------------------
+#NTP Global Configuration
+#----------------------------------------------
+#NTP source-interfaces: Ethernet0, Ethernet4
+#
+- name: Merge NTP source-interface configuration
+ ntp:
+ config:
+ source_interfaces:
+ - Ethernet8
+ - Ethernet16
+ state: merged
+
+# After state:
+# ------------
+#
+#sonic# show ntp global
+#----------------------------------------------
+#NTP Global Configuration
+#----------------------------------------------
+#NTP source-interfaces: Ethernet0, Ethernet4, Ethernet8, Ethernet16
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#sonic# show running-configuration | grep ntp
+#ntp authentication-key 8 sha1 U2FsdGVkX1/NpJrdOeyMeUHEkSohY6azY9VwbAqXRTY= encrypted
+#
+- name: Merge NTP key configuration
+ ntp:
+ config:
+ ntp_keys:
+ - key_id: 10
+ key_type: NTP_AUTH_MD5
+ key_value: dellemc10
+ encrypted: false
+ - key_id: 20
+ key_type: NTP_AUTH_SHA2_256
+ key_value: dellemc20
+ encrypted: false
+ state: merged
+#
+# After state:
+# ------------
+#
+#sonic# show running-configuration | grep ntp
+#ntp authentication-key 8 sha1 U2FsdGVkX1/NpJrdOeyMeUHEkSohY6azY9VwbAqXRTY= encrypted
+#ntp authentication-key 10 md5 U2FsdGVkX1/Gxds/5pscCvIKbVngGaKka4SQineS51Y= encrypted
+#ntp authentication-key 20 sha2-256 U2FsdGVkX1/eAzKj1teKhYWD7tnzOsYOijGeFAT0rKM= encrypted
+#
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.ntp.ntp import NtpArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.ntp.ntp import Ntp
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=NtpArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Ntp(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py
new file mode 100644
index 00000000..66ea0047
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_port_breakout.py
@@ -0,0 +1,228 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_port_breakout
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_port_breakout
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+short_description: Configure port breakout settings on physical interfaces
+description:
+ - This module provides configuration management of port breakout parameters on devices running Enterprise SONiC.
+options:
+ config:
+ description:
+ - Specifies the port breakout related configuration.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Specifies the name of the port breakout.
+ type: str
+ required: true
+ mode:
+ description:
+ - Specifies the mode of the port breakout.
+ type: str
+ choices:
+ - 1x100G
+ - 1x400G
+ - 1x40G
+ - 2x100G
+ - 2x200G
+ - 2x50G
+ - 4x100G
+ - 4x10G
+ - 4x25G
+ - 4x50G
+ state:
+ description:
+ - Specifies the operation to be performed on the port breakout configured on the device.
+ - In case of merged, the input mode configuration will be merged with the existing port breakout configuration on the device.
+ - In case of deleted the existing port breakout mode configuration will be removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show interface breakout
+#-----------------------------------------------
+#Port Breakout Mode Status Interfaces
+#-----------------------------------------------
+#1/1 4x10G Completed Eth1/1/1
+# Eth1/1/2
+# Eth1/1/3
+# Eth1/1/4
+#1/11 1x100G Completed Eth1/11
+#
+
+- name: Merge users configurations
+ dellemc.enterprise_sonic.sonic_port_breakout:
+ config:
+ - name: 1/11
+ mode: 1x100G
+ state: deleted
+
+# After state:
+# ------------
+#
+#do show interface breakout
+#-----------------------------------------------
+#Port Breakout Mode Status Interfaces
+#-----------------------------------------------
+#1/1 4x10G Completed Eth1/1/1
+# Eth1/1/2
+# Eth1/1/3
+# Eth1/1/4
+#1/11 Default Completed Ethernet40
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show interface breakout
+#-----------------------------------------------
+#Port Breakout Mode Status Interfaces
+#-----------------------------------------------
+#1/1 4x10G Completed Eth1/1/1
+# Eth1/1/2
+# Eth1/1/3
+# Eth1/1/4
+#1/11 1x100G Completed Eth1/11
+#
+- name: Merge users configurations
+ dellemc.enterprise_sonic.sonic_port_breakout:
+ config:
+ state: deleted
+
+
+# After state:
+# ------------
+#
+#do show interface breakout
+#-----------------------------------------------
+#Port Breakout Mode Status Interfaces
+#-----------------------------------------------
+#1/1 Default Completed Ethernet0
+#1/11 Default Completed Ethernet40
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+#do show interface breakout
+#-----------------------------------------------
+#Port Breakout Mode Status Interfaces
+#-----------------------------------------------
+#1/1 4x10G Completed Eth1/1/1
+# Eth1/1/2
+# Eth1/1/3
+# Eth1/1/4
+#
+- name: Merge users configurations
+ dellemc.enterprise_sonic.sonic_port_breakout:
+ config:
+ - name: 1/11
+ mode: 1x100G
+ state: merged
+
+
+# After state:
+# ------------
+#
+#do show interface breakout
+#-----------------------------------------------
+#Port Breakout Mode Status Interfaces
+#-----------------------------------------------
+#1/1 4x10G Completed Eth1/1/1
+# Eth1/1/2
+# Eth1/1/3
+# Eth1/1/4
+#1/11 1x100G Completed Eth1/11
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.port_breakout.port_breakout import Port_breakoutArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.port_breakout.port_breakout import Port_breakout
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Port_breakoutArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Port_breakout(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py
new file mode 100644
index 00000000..5a734e8b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_prefix_lists.py
@@ -0,0 +1,423 @@
+#!/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 sonic_prefix_lists
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_prefix_lists
+version_added: "2.0.0"
+author: Kerry Meyer (@kerry-meyer)
+short_description: prefix list configuration handling for SONiC
+description:
+ - This module provides configuration management for prefix list parameters on devices running SONiC.
+options:
+ config:
+ description:
+ - Specifies a list of prefix set configuration dictionaries
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Name of a prefix set (a list of prefix entries)
+ type: str
+ required: true
+ afi:
+ description:
+ - Specifies the Address Family for addresses in the prefix list entries
+ type: str
+ choices: ["ipv4", "ipv6"]
+ default: "ipv4"
+ prefixes:
+ description:
+ - A list of prefix entries
+ type: list
+ elements: dict
+ suboptions:
+ sequence:
+ description:
+ - Precedence for this prefix entry (unique within the prefix list)
+ type: int
+ required: true
+ action:
+ description:
+ - Action to be taken for addresses matching this prefix entry
+ type: str
+ required: true
+ choices: ["permit", "deny"]
+ prefix:
+ description:
+ - IPv4 or IPv6 prefix in A.B.C.D/LEN or A:B::C:D/LEN format
+ type: str
+ required: true
+ ge:
+ description: Minimum prefix length to be matched
+ type: int
+ le:
+ description: Maximum prefix length to be matched
+ type: int
+ state:
+ description:
+ - Specifies the type of configuration update to be performed on the device.
+ - For "merged", merge specified attributes with existing configured attributes.
+ - For "deleted", delete the specified attributes from exiting configuration.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using "merged" state to create initial configuration
+#
+# Before state:
+# -------------
+#
+# sonic# show running-configuration ip prefix-list
+# sonic#
+# (No configuration present)
+#
+# -------------
+#
+- name: Merge initial prefix-list configuration
+ dellemc.enterprise_sonic.sonic_prefix_lists:
+ config:
+ - name: pfx1
+ afi: "ipv4"
+ prefixes:
+ - sequence: 10
+ prefix: "1.2.3.4/24"
+ action: "permit"
+ ge: 26
+ le: 30
+ state: merged
+
+# After state:
+# ------------
+#
+# sonic# show running-configuration ip prefix-list
+# !
+# ip prefix-list pfx1 seq 10 permit 1.2.3.4/24 ge 26 le 30
+# ------------
+#
+# ***************************************************************
+# Using "merged" state to update and add configuration
+#
+# Before state:
+# ------------
+#
+# sonic# show running-configuration ip prefix-list
+# !
+# ip prefix-list pfx1 seq 10 permit 1.2.3.4/24 ge 26 le 30
+#
+# sonic# show running-configuration ipv6 prefix-list
+# sonic#
+# (no IPv6 prefix-list configuration present)
+#
+# ------------
+#
+- name: Merge additional prefix-list configuration
+ dellemc.enterprise_sonic.sonic_prefix_lists:
+ config:
+ - name: pfx1
+ afi: "ipv4"
+ prefixes:
+ - sequence: 20
+ action: "deny"
+ prefix: "1.2.3.12/26"
+ - sequence: 30
+ action: "permit"
+ prefix: "7.8.9.0/24"
+ - name: pfx6
+ afi: "ipv6"
+ prefixes:
+ - sequence: 25
+ action: "permit"
+ prefix: "40::300/124"
+ state: merged
+
+# After state:
+# ------------
+#
+# sonic# show running-configuration ip prefix-list
+# !
+# ip prefix-list pfx1 seq 10 permit 1.2.3.4/24 ge 26 le 30
+# ip prefix-list pfx1 seq 20 deny 1.2.3.12/26
+# ip prefix-list pfx1 seq 30 permit 7.8.9.0/24
+#
+# sonic# show running-configuration ipv6 prefix-list
+# !
+# ipv6 prefix-list pfx6 seq 25 permit 40::300/124
+#
+# ***************************************************************
+# Using "deleted" state to remove configuration
+#
+# Before state:
+# ------------
+#
+# sonic# show running-configuration ip prefix-list
+# !
+# ip prefix-list pfx1 seq 10 permit 1.2.3.4/24 ge 26 le 30
+# ip prefix-list pfx1 seq 20 deny 1.2.3.12/26
+# ip prefix-list pfx1 seq 30 permit 7.8.9.0/24
+#
+# sonic# show running-configuration ipv6 prefix-list
+# !
+# ipv6 prefix-list pfx6 seq 25 permit 40::300/124
+#
+# ------------
+#
+- name: Delete selected prefix-list configuration
+ dellemc.enterprise_sonic.sonic_prefix_lists:
+ config:
+ - name: pfx1
+ afi: "ipv4"
+ prefixes:
+ - sequence: 10
+ prefix: "1.2.3.4/24"
+ action: "permit"
+ ge: 26
+ le: 30
+ - sequence: 20
+ action: "deny"
+ prefix: "1.2.3.12/26"
+ - name: pfx6
+ afi: "ipv6"
+ prefixes:
+ - sequence: 25
+ action: "permit"
+ prefix: "40::300/124"
+ state: deleted
+
+# After state:
+# ------------
+#
+# sonic# show running-configuration ip prefix-list
+# !
+# ip prefix-list pfx1 seq 30 permit 7.8.9.0/24
+#
+# sonic# show running-configuration ipv6 prefix-list
+# sonic#
+# (no IPv6 prefix-list configuration present)
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+
+# "before": [
+# {
+# "afi": "ipv6",
+# "name": "pf4",
+# "prefixes": [
+# {
+# "action": "permit",
+# "ge": null,
+# "le": null,
+# "prefix": "50:60::/64",
+# "sequence": 40
+# }
+# ]
+# },
+# {
+# "afi": "ipv4",
+# "name": "pf3",
+# "prefixes": [
+# {
+# "action": "deny",
+# "ge": null,
+# "le": 27,
+# "prefix": "1.2.3.128/25",
+# "sequence": 30
+# }
+# ]
+# },
+# {
+# "afi": "ipv4",
+# "name": "pf2",
+# "prefixes": [
+# {
+# "action": "permit",
+# "ge": 27,
+# "le": 29,
+# "prefix": "10.20.30.128/25",
+# "sequence": 50
+# },
+# {
+# "action": "deny",
+# "ge": 26,
+# "le": null,
+# "prefix": "10.20.30.0/24",
+# "sequence": 20
+# }
+# ]
+# },
+# {
+# "afi": "ipv4",
+# "name": "pf1",
+# "prefixes": [
+# {
+# "action": "deny",
+# "ge": 25,
+# "le": 27,
+# "prefix": "1.2.3.0/24",
+# "sequence": 10
+# }
+# ]
+# }
+# ]
+
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+
+# "after": [
+# {
+# "afi": "ipv4",
+# "name": "pf5",
+# "prefixes": [
+# {
+# "action": "permit",
+# "ge": null,
+# "le": null,
+# "prefix": "15.25.35.0/24",
+# "sequence": 15
+# }
+# ]
+# },
+# {
+# "afi": "ipv4",
+# "name": "pf1",
+# "prefixes": [
+# {
+# "action": "deny",
+# "ge": 25,
+# "le": 27,
+# "prefix": "1.2.3.0/24",
+# "sequence": 10
+# }
+# ]
+# },
+# {
+# "afi": "ipv6",
+# "name": "pf4",
+# "prefixes": [
+# {
+# "action": "permit",
+# "ge": null,
+# "le": null,
+# "prefix": "50:60::/64",
+# "sequence": 40
+# }
+# ]
+# },
+# {
+# "afi": "ipv4",
+# "name": "pf3",
+# "prefixes": [
+# {
+# "action": "deny",
+# "ge": null,
+# "le": 27,
+# "prefix": "1.2.3.128/25",
+# "sequence": 30
+# }
+# ]
+# },
+# {
+# "afi": "ipv4",
+# "name": "pf2",
+# "prefixes": [
+# {
+# "action": "permit",
+# "ge": 27,
+# "le": 29,
+# "prefix": "10.20.30.128/25",
+# "sequence": 50
+# },
+# {
+# "action": "deny",
+# "ge": 26,
+# "le": null,
+# "prefix": "10.20.30.0/24",
+# "sequence": 20
+# }
+# ]
+# }
+# ]
+
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+
+# "commands": [
+# {
+# "afi": "ipv4",
+# "name": "pf5",
+# "prefixes": [
+# {
+# "action": "permit",
+# "prefix": "15.25.35.0/24",
+# "sequence": 15
+# }
+# ],
+# "state": "merged"
+# }
+# ],
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.prefix_lists.prefix_lists import Prefix_listsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.prefix_lists.prefix_lists import Prefix_lists
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Prefix_listsArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Prefix_lists(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py
new file mode 100644
index 00000000..1df4aff6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_radius_server.py
@@ -0,0 +1,328 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_radius_server
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_radius_server
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+short_description: Manage RADIUS server and its parameters
+description:
+ - This module provides configuration management of radius server parameters on devices running Enterprise SONiC.
+options:
+ config:
+ description:
+ - Specifies the radius server related configuration.
+ type: dict
+ suboptions:
+ auth_type:
+ description:
+ - Specifies the authentication type of the radius server.
+ type: str
+ choices:
+ - pap
+ - chap
+ - mschapv2
+ default: pap
+ key:
+ description:
+ - Specifies the key of the radius server.
+ type: str
+ nas_ip:
+ description:
+ - Specifies the network access server of the radius server.
+ type: str
+ statistics:
+ description:
+ - Specifies the statistics flag of the radius server.
+ type: bool
+ timeout:
+ description:
+ - Specifies the timeout of the radius server.
+ type: int
+ retransmit:
+ description:
+ - Specifies the re-transmit value of the radius server.
+ type: int
+ servers:
+ description:
+ - Specifies the servers list of the radius server.
+ type: dict
+ suboptions:
+ host:
+ description:
+ - Specifies the host details of the radius servers list.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Specifies the name of the radius server host.
+ type: str
+ auth_type:
+ description:
+ - Specifies the authentication type of the radius server host.
+ type: str
+ choices:
+ - pap
+ - chap
+ - mschapv2
+ key:
+ description:
+ - Specifies the key of the radius server host.
+ type: str
+ priority:
+ description:
+ - Specifies the priority of the radius server host.
+ type: int
+ port:
+ description:
+ - Specifies the port of the radius server host.
+ type: int
+ timeout:
+ description:
+ - Specifies the timeout of the radius server host.
+ type: int
+ retransmit:
+ description:
+ - Specifies the retransmit of the radius server host.
+ type: int
+ source_interface:
+ description:
+ - Specifies the source interface of the radius server host.
+ type: str
+ vrf:
+ description:
+ - Specifies the vrf of the radius server host.
+ type: str
+ state:
+ description:
+ - Specifies the operation to be performed on the radius server configured on the device.
+ - In case of merged, the input mode configuration will be merged with the existing radius server configuration on the device.
+ - In case of deleted the existing radius server mode configuration will be removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#sonic(config)# do show radius-server
+#---------------------------------------------------------
+#RADIUS Global Configuration
+#---------------------------------------------------------
+#nas-ip-addr: 1.2.3.4
+#statistics : True
+#timeout : 10
+#auth-type : chap
+#key : chap
+#retransmit : 3
+#--------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI
+#--------------------------------------------------------------------------------
+#localhost mschapv2 local 52 2 20 2 mgmt Ethernet12
+#myhost chap local 53 3 23 3 mgmt Ethernet24
+#---------------------------------------------------------
+#RADIUS Statistics
+#---------------------------------------------------------
+#
+
+- name: Merge radius configurations
+ dellemc.enterprise_sonic.sonic_radius_server:
+ config:
+ auth_type: chap
+ nas_ip: 1.2.3.4
+ statistics: true
+ timeout: 10
+ servers:
+ host:
+ - name: localhost
+ state: deleted
+
+# After state:
+# ------------
+#sonic(config)# do show radius-server
+#---------------------------------------------------------
+#RADIUS Global Configuration
+#---------------------------------------------------------
+#timeout : 5
+#auth-type : pap
+#key : chap
+#retransmit : 3
+#--------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI
+#--------------------------------------------------------------------------------
+#myhost chap local 53 3 23 3 mgmt Ethernet24
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#sonic(config)# do show radius-server
+#---------------------------------------------------------
+#RADIUS Global Configuration
+#---------------------------------------------------------
+#nas-ip-addr: 1.2.3.4
+#statistics : True
+#timeout : 10
+#auth-type : chap
+#key : chap
+#retransmit : 3
+#--------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI
+#--------------------------------------------------------------------------------
+#localhost mschapv2 local 52 2 20 2 mgmt Ethernet12
+#myhost chap local 53 3 23 3 mgmt Ethernet24
+#---------------------------------------------------------
+#RADIUS Statistics
+#---------------------------------------------------------
+#
+- name: Merge radius configurations
+ dellemc.enterprise_sonic.sonic_radius_server:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#sonic(config)# do show radius-server
+#---------------------------------------------------------
+#RADIUS Global Configuration
+#---------------------------------------------------------
+#timeout : 5
+#auth-type : pap
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+#sonic(config)# do show radius-server
+#---------------------------------------------------------
+#RADIUS Global Configuration
+#---------------------------------------------------------
+#
+- name: Merge radius configurations
+ dellemc.enterprise_sonic.sonic_radius_server:
+ config:
+ auth_type: chap
+ key: chap
+ nas_ip: 1.2.3.4
+ statistics: true
+ timeout: 10
+ retransmit: 3
+ servers:
+ host:
+ - name: localhost
+ auth_type: mschapv2
+ key: local
+ priority: 2
+ port: 52
+ retransmit: 2
+ timeout: 20
+ source_interface: Eth 12
+ vrf: mgmt
+ state: merged
+
+# After state:
+# ------------
+#
+#sonic(config)# do show radius-server
+#---------------------------------------------------------
+#RADIUS Global Configuration
+#---------------------------------------------------------
+#nas-ip-addr: 1.2.3.4
+#statistics : True
+#timeout : 10
+#auth-type : chap
+#key : chap
+#retransmit : 3
+#--------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY AUTH-PORT PRIORITY TIMEOUT RTSMT VRF SI
+#--------------------------------------------------------------------------------
+#localhost mschapv2 local 52 2 20 2 mgmt Ethernet12
+#---------------------------------------------------------
+#RADIUS Statistics
+#---------------------------------------------------------
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.radius_server.radius_server import Radius_serverArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.radius_server.radius_server import Radius_server
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Radius_serverArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Radius_server(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py
new file mode 100644
index 00000000..7a528cdf
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_static_routes.py
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2022 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_static_routes
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_static_routes
+version_added: 2.0.0
+short_description: Manage static routes configuration on SONiC
+description:
+ - This module provides configuration management of static routes for devices running SONiC
+author: "Shade Talabi (@stalabi1)"
+options:
+ config:
+ type: list
+ elements: dict
+ description:
+ - Manages 'static_routes' configurations
+ suboptions:
+ vrf_name:
+ required: True
+ type: str
+ description:
+ - Name of the configured VRF on the device.
+ static_list:
+ type: list
+ elements: dict
+ description:
+ - A list of 'static_routes' configurations.
+ suboptions:
+ prefix:
+ required: True
+ type: str
+ description:
+ - Destination prefix for the static route, either IPv4 or IPv6.
+ next_hops:
+ type: list
+ elements: dict
+ description:
+ - A list of next-hops to be utilised for the static route being specified.
+ suboptions:
+ index:
+ required: True
+ type: dict
+ description:
+ - An identifier utilised to uniquely reference the next-hop.
+ suboptions:
+ blackhole:
+ type: bool
+ default: False
+ description:
+ - Indicates that packets matching this route should be discarded.
+ interface:
+ type: str
+ description:
+ - The reference to a base interface.
+ nexthop_vrf:
+ type: str
+ description:
+ - Name of the next-hop network instance for leaked routes.
+ next_hop:
+ type: str
+ description:
+ - The next-hop that is to be used for the static route.
+ metric:
+ type: int
+ description:
+ - Specifies the preference of the next-hop entry when it is injected into the RIB.
+ track:
+ type: int
+ description:
+ - The IP SLA track ID for static route.
+ tag:
+ type: int
+ description:
+ - The tag value for the static route.
+ state:
+ description:
+ - The state of the configuration after module completion.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+
+# Using merged
+#
+# Before State:
+# -------------
+#
+# sonic# show running-configuration | grep "ip route"
+# (No "ip route" configuration present)
+
+ - name: Merge static routes configurations
+ dellemc.enterprise_sonic.sonic_static_routes:
+ config:
+ - vrf_name: 'default'
+ static_list:
+ - prefix: '2.0.0.0/8'
+ next_hops:
+ - index:
+ interface: 'Ethernet4'
+ metric: 1
+ tag: 2
+ track: 3
+ - index:
+ next_hop: '3.0.0.0'
+ metric: 2
+ tag: 4
+ track: 8
+ - vrf_name: '{{vrf_1}}'
+ static_list:
+ - prefix: '3.0.0.0/8'
+ next_hops:
+ - index:
+ interface: 'eth0'
+ nexthop_vrf: '{{vrf_2}}'
+ next_hop: '4.0.0.0'
+ metric: 4
+ tag: 5
+ track: 6
+ - index:
+ blackhole: True
+ metric: 10
+ tag: 20
+ track: 30
+ state: merged
+
+# After State:
+# ------------
+#
+# sonic# show running-configuration | grep "ip route"
+# ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2
+# ip route 2.0.0.0/8 interface Ethernet4 tag 2 track 3 1
+# ip route vrf VrfReg1 3.0.0.0/8 4.0.0.0 interface Management 0 nexthop-vrf VrfReg2 tag 5 track 6 4
+# ip route vrf VrfREg1 3.0.0.0/8 blackhole tag 20 track 30 10
+#
+#
+# Modifying previous merge
+
+ - name: Modify static routes configurations
+ dellemc.enterprise_sonic.sonic_static_routes:
+ config:
+ - vrf_name: '{{vrf_1}}'
+ static_list:
+ - prefix: '3.0.0.0/8'
+ next_hops:
+ - index:
+ blackhole: True
+ metric: 11
+ tag: 22
+ track: 33
+ state: merged
+
+# After State:
+# ------------
+#
+# sonic# show running-configuration | grep "ip route"
+# ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2
+# ip route 2.0.0.0/8 interface Ethernet4 tag 2 track 3 1
+# ip route vrf VrfReg1 3.0.0.0/8 4.0.0.0 interface Management 0 nexthop-vrf VrfReg2 tag 5 track 6 4
+# ip route vrf VrfREg1 3.0.0.0/8 blackhole tag 22 track 33 11
+
+
+# Using deleted
+#
+# Before State:
+# -------------
+#
+# sonic# show running-configuration | grep "ip route"
+# ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2
+# ip route 2.0.0.0/8 interface Ethernet4 tag 2 track 3 1
+# ip route vrf VrfReg1 3.0.0.0/8 4.0.0.0 interface Management 0 nexthop-vrf VrfReg2 tag 5 track 6 4
+# ip route vrf VrfREg1 3.0.0.0/8 blackhole tag 22 track 33 11
+
+ - name: Delete static routes configurations
+ dellemc.enterprise_sonic.sonic_static_routes:
+ config:
+ - vrf_name: 'default'
+ static_list:
+ - prefix: '2.0.0.0/8'
+ next_hops:
+ - index:
+ interface: 'Ethernet4'
+ - vrf_name: '{{vrf_1}}'
+ state: deleted
+
+# After State:
+# ------------
+#
+# sonic# show running-configuration | grep "ip route"
+# ip route 2.0.0.0/8 3.0.0.0 tag 4 track 8 2
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.static_routes.static_routes import Static_routesArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.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/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py
new file mode 100644
index 00000000..efb285a1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_system.py
@@ -0,0 +1,214 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_system
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_system
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Abirami N (@abirami-n)
+short_description: Configure system parameters
+description:
+ - This module is used for configuration management of global system parameters on devices running Enterprise SONiC.
+options:
+ config:
+ description:
+ - Specifies the system related configurations
+ type: dict
+ suboptions:
+ hostname:
+ description:
+ - Specifies the hostname of the SONiC device
+ type: str
+ interface_naming:
+ description:
+ - Specifies the type of interface-naming in device
+ type: str
+ choices:
+ - standard
+ - native
+ anycast_address:
+ description:
+ - Specifies different types of anycast address that can be configured on the device
+ type: dict
+ suboptions:
+ ipv4:
+ description:
+ - Enable or disable ipv4 anycast-address
+ type: bool
+ ipv6:
+ description:
+ - Enable or disable ipv6 anycast-address
+ type: bool
+ mac_address:
+ description:
+ - Specifies the mac anycast-address
+ type: str
+ state:
+ description:
+ - Specifies the operation to be performed on the system parameters configured on the device.
+ - In case of merged, the input configuration will be merged with the existing system configuration on the device.
+ - In case of deleted the existing system configuration will be removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#!
+#SONIC(config)#do show running-configuration
+#!
+#ip anycast-mac-address aa:bb:cc:dd:ee:ff
+#ip anycast-address enable
+#ipv6 anycast-address enable
+#interface-naming standard
+
+- name: Merge provided configuration with device configuration
+ dellemc.enterprise_sonic.sonic_system:
+ config:
+ hostname: SONIC
+ interface_naming: standard
+ anycast_address:
+ ipv6: true
+ state: deleted
+
+# After state:
+# ------------
+#!
+#sonic(config)#do show running-configuration
+#!
+#ip anycast-mac-address aa:bb:cc:dd:ee:ff
+#ip anycast-address enable
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#!
+#SONIC(config)#do show running-configuration
+#!
+#ip anycast-mac-address aa:bb:cc:dd:ee:ff
+#ip anycast-address enable
+#ipv6 anycast-address enable
+#interface-naming standard
+
+- name: Delete all system related configs in device configuration
+ dellemc.enterprise_sonic.sonic_system:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#!
+#sonic(config)#do show running-configuration
+#!
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#!
+#sonic(config)#do show running-configuration
+#!
+
+- name: Merge provided configuration with device configuration
+ dellemc.enterprise_sonic.sonic_system:
+ config:
+ hostname: SONIC
+ interface_naming: standard
+ anycast_address:
+ ipv6: true
+ ipv4: true
+ mac_address: aa:bb:cc:dd:ee:ff
+ state: merged
+
+# After state:
+# ------------
+#!
+#SONIC(config)#do show running-configuration
+#!
+#ip anycast-mac-address aa:bb:cc:dd:ee:ff
+#ip anycast-address enable
+#ipv6 anycast-address enable
+#interface-naming standard
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.system.system import SystemArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.system.system import System
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=SystemArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = System(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py
new file mode 100644
index 00000000..3295e11b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_tacacs_server.py
@@ -0,0 +1,297 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2021 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_tacacs_server
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_tacacs_server
+version_added: 1.1.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+short_description: Manage TACACS server and its parameters
+description:
+ - This module provides configuration management of tacacs server parameters on devices running Enterprise SONiC.
+options:
+ config:
+ description:
+ - Specifies the tacacs server related configuration.
+ type: dict
+ suboptions:
+ auth_type:
+ description:
+ - Specifies the authentication type of the tacacs server.
+ type: str
+ choices:
+ - pap
+ - chap
+ - mschap
+ - login
+ default: pap
+ key:
+ description:
+ - Specifies the key of the tacacs server.
+ type: str
+ timeout:
+ description:
+ - Specifies the timeout of the tacacs server.
+ type: int
+ source_interface:
+ description:
+ - Specifies the source interface of the tacacs server.
+ type: str
+ servers:
+ description:
+ - Specifies the servers list of the tacacs server.
+ type: dict
+ suboptions:
+ host:
+ description:
+ - Specifies the host details of the tacacs servers list.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Specifies the name of the tacacs server host.
+ type: str
+ auth_type:
+ description:
+ - Specifies the authentication type of the tacacs server host.
+ type: str
+ choices:
+ - pap
+ - chap
+ - mschap
+ - login
+ default: pap
+ key:
+ description:
+ - Specifies the key of the tacacs server host.
+ type: str
+ priority:
+ description:
+ - Specifies the priority of the tacacs server host.
+ type: int
+ default: 1
+ port:
+ description:
+ - Specifies the port of the tacacs server host.
+ type: int
+ default: 49
+ timeout:
+ description:
+ - Specifies the timeout of the tacacs server host.
+ type: int
+ default: 5
+ vrf:
+ description:
+ - Specifies the vrf of the tacacs server host.
+ type: str
+ default: default
+ state:
+ description:
+ - Specifies the operation to be performed on the tacacs server configured on the device.
+ - In case of merged, the input mode configuration will be merged with the existing tacacs server configuration on the device.
+ - In case of deleted the existing tacacs server mode configuration will be removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# do show tacacs-server
+#---------------------------------------------------------
+#TACACS Global Configuration
+#---------------------------------------------------------
+#source-interface : Ethernet12
+#timeout : 10
+#auth-type : login
+#key : login
+#------------------------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY PORT PRIORITY TIMEOUT VRF
+#------------------------------------------------------------------------------------------------
+#1.2.3.4 pap ***** 50 2 10 mgmt
+#localhost pap 49 1 5 default
+#
+
+- name: Merge tacacs configurations
+ dellemc.enterprise_sonic.sonic_tacacs_server:
+ config:
+ auth_type: login
+ key: login
+ source_interface: Ethernet 12
+ timeout: 10
+ servers:
+ host:
+ - name: 1.2.3.4
+ state: deleted
+
+# After state:
+# ------------
+#
+#do show tacacs-server
+#---------------------------------------------------------
+#TACACS Global Configuration
+#---------------------------------------------------------
+#timeout : 5
+#auth-type : pap
+#------------------------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY PORT PRIORITY TIMEOUT VRF
+#------------------------------------------------------------------------------------------------
+#localhost pap 49 1 5 default
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# do show tacacs-server
+#---------------------------------------------------------
+#TACACS Global Configuration
+#---------------------------------------------------------
+#source-interface : Ethernet12
+#timeout : 10
+#auth-type : login
+#key : login
+#------------------------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY PORT PRIORITY TIMEOUT VRF
+#------------------------------------------------------------------------------------------------
+#1.2.3.4 pap ***** 50 2 10 mgmt
+#localhost pap 49 1 5 default
+#
+
+- name: Merge tacacs configurations
+ dellemc.enterprise_sonic.sonic_tacacs_server:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+#do show tacacs-server
+#---------------------------------------------------------
+#TACACS Global Configuration
+#---------------------------------------------------------
+#timeout : 5
+#auth-type : pap
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+#sonic(config)# do show tacacs-server
+#---------------------------------------------------------
+#TACACS Global Configuration
+#---------------------------------------------------------
+#
+- name: Merge tacacs configurations
+ dellemc.enterprise_sonic.sonic_tacacs_server:
+ config:
+ auth_type: pap
+ key: pap
+ source_interface: Ethernet 12
+ timeout: 10
+ servers:
+ host:
+ - name: 1.2.3.4
+ auth_type: pap
+ key: 1234
+ state: merged
+
+# After state:
+# ------------
+#
+#sonic(config)# do show tacacs-server
+#---------------------------------------------------------
+#TACACS Global Configuration
+#---------------------------------------------------------
+#source-interface : Ethernet12
+#timeout : 10
+#auth-type : pap
+#key : pap
+#------------------------------------------------------------------------------------------------
+#HOST AUTH-TYPE KEY PORT PRIORITY TIMEOUT VRF
+#------------------------------------------------------------------------------------------------
+#1.2.3.4 pap 1234 49 1 5 default
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.tacacs_server.tacacs_server import Tacacs_serverArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.tacacs_server.tacacs_server import Tacacs_server
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=Tacacs_serverArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Tacacs_server(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py
new file mode 100644
index 00000000..7f0855a9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_users.py
@@ -0,0 +1,210 @@
+#!/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 sonic_users
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_users
+version_added: 1.1.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+short_description: Manage users and its parameters
+description:
+ - This module provides configuration management of users parameters on devices running Enterprise SONiC.
+options:
+ config:
+ description:
+ - Specifies the users related configuration.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Specifies the name of the user.
+ type: str
+ required: true
+ role:
+ description:
+ - Specifies the role of the user.
+ type: str
+ choices:
+ - admin
+ - operator
+ password:
+ description:
+ - Specifies the password of the user.
+ type: str
+ update_password:
+ description:
+ - Specifies the update password flag.
+ - In case of always, password will be updated every time.
+ - In case of on_create, password will be updated only when user is created.
+ type: str
+ choices:
+ - always
+ - on_create
+ default: always
+ state:
+ description:
+ - Specifies the operation to be performed on the users configured on the device.
+ - In case of merged, the input configuration will be merged with the existing users configuration on the device.
+ - In case of deleted the existing users configuration will be removed from the device.
+ default: merged
+ choices: ['merged', 'deleted']
+ type: str
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show running-configuration
+#!
+#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin
+#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin
+#username sysoperator password $6$s1eTVjcX4Udi69gY$zlYgqwoKRGC6hGL5iKDImN/4BL7LXKNsx9e5PoSsBLs6C80ShYj2LoJAUZ58ia2WNjcHXhTD1p8eU9wyRTCiE0 role operator
+#
+- name: Merge users configurations
+ dellemc.enterprise_sonic.sonic_users:
+ config:
+ - name: sysoperator
+ state: deleted
+# After state:
+# ------------
+#
+#do show running-configuration
+#!
+#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin
+#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin
+
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#do show running-configuration
+#!
+#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin
+#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin
+#username sysoperator password $6$s1eTVjcX4Udi69gY$zlYgqwoKRGC6hGL5iKDImN/4BL7LXKNsx9e5PoSsBLs6C80ShYj2LoJAUZ58ia2WNjcHXhTD1p8eU9wyRTCiE0 role operator
+#
+- name: Merge users configurations
+ dellemc.enterprise_sonic.sonic_users:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+#do show running-configuration
+#!
+#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin
+
+
+# Using merged
+#
+# Before state:
+# -------------
+#
+#do show running-configuration
+#!
+#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin
+#
+- name: Merge users configurations
+ dellemc.enterprise_sonic.sonic_users:
+ config:
+ - name: sysadmin
+ role: admin
+ password: admin
+ update_password: always
+ - name: sysoperator
+ role: operator
+ password: operator
+ update_password: always
+ state: merged
+
+# After state:
+# ------------
+#!
+#do show running-configuration
+#!
+#username admin password $6$sdZt2C7F$3oPSRkkJyLZtsKlFNGWdwssblQWBj5dXM6qAJAQl7dgOfqLSpZJ/n6xf8zPRcqPUFCu5ZKpEtynJ9sZ/S8Mgj. role admin
+#username sysadmin password $6$3QNqJzpFAPL9JqHA$417xFKw6SRn.CiqMFJkDfQJXKJGjeYwi2A8BIyfuWjGimvunOOjTRunVluudey/W9l8jhzN1oewBW5iLxmq2Q1 role admin
+#username sysoperator password $6$s1eTVjcX4Udi69gY$zlYgqwoKRGC6hGL5iKDImN/4BL7LXKNsx9e5PoSsBLs6C80ShYj2LoJAUZ58ia2WNjcHXhTD1p8eU9wyRTCiE0 role operator
+
+
+"""
+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: ['command 1', 'command 2', 'command 3']
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.users.users import UsersArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.users.users import Users
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=UsersArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Users(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py
new file mode 100644
index 00000000..cfd536c7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vlans.py
@@ -0,0 +1,241 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_vlans
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_vlans
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+author: Mohamed Javeed (@javeedf)
+short_description: Manage VLAN and its parameters
+description:
+ - This module provides configuration management of VLANs parameters
+ on devices running Enterprise SONiC Distribution by Dell Technologies.
+options:
+ config:
+ description: A dictionary of VLAN options.
+ type: list
+ elements: dict
+ suboptions:
+ vlan_id:
+ description:
+ - ID of the VLAN
+ - Range is 1 to 4094
+ type: int
+ required: true
+ description:
+ description:
+ - Description about the VLAN.
+ type: str
+ state:
+ description:
+ - The state that the configuration should be left in.
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using merged
+
+# Before state:
+# -------------
+#
+#sonic# show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#30 Inactive
+#
+#sonic#
+#
+
+
+- name: Merges given VLAN attributes with the device configuration
+ dellemc.enterprise_sonic.sonic_vlans:
+ config:
+ - vlan_id: 10
+ description: "Internal"
+ state: merged
+
+# After state:
+# ------------
+#
+#sonic# show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#30 Inactive
+#
+#sonic#
+#
+#sonic# show interface Vlan 10
+#Description: Internal
+#Vlan10 is up
+#Mode of IPV4 address assignment: not-set
+#Mode of IPV6 address assignment: not-set
+#IP MTU 6000 bytes
+#sonic#
+#
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+#sonic# show interface Vlan 70
+#Description: Internal
+#Vlan70 is up
+#Mode of IPV4 address assignment: not-set
+#Mode of IPV6 address assignment: not-set
+#IP MTU 6000 bytes
+
+- name: Deletes attributes of the given VLANs
+ dellemc.enterprise_sonic.sonic_vlans:
+ config:
+ - vlan_id: 70
+ description: "Internal"
+ state: deleted
+
+# After state:
+# ------------
+#
+#sonic# show interface Vlan 70
+#Vlan70 is up
+#Mode of IPV4 address assignment: not-set
+#Mode of IPV6 address assignment: not-set
+#IP MTU 6000 bytes
+
+# Before state:
+# -------------
+#
+#sonic# show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#20 Inactive
+#
+#sonic#
+
+- name: Deletes attributes of the given VLANs
+ dellemc.enterprise_sonic.sonic_vlans:
+ config:
+ - vlan_id: 20
+ state: deleted
+
+# After state:
+# ------------
+#
+#sonic# show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#
+#sonic#
+
+
+# Using deleted
+
+# Before state:
+# -------------
+#
+#sonic# show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#10 Inactive
+#20 Inactive
+#30 Inactive
+#
+#sonic#
+
+- name: Deletes all the VLANs on the switch
+ dellemc.enterprise_sonic.sonic_vlans:
+ config:
+ state: deleted
+
+# After state:
+# ------------
+#
+#sonic# show Vlan
+#Q: A - Access (Untagged), T - Tagged
+#NUM Status Q Ports
+#
+#sonic#
+
+
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration that is returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vlans.vlans import VlansArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.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/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py
new file mode 100644
index 00000000..4c881aee
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vrfs.py
@@ -0,0 +1,204 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_vrfs
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_vrfs
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage VRFs and associate VRFs to interfaces such as, Eth, LAG, VLAN, and loopback
+description: Manages VRF and VRF interface attributes in Enterprise SONiC Distribution by Dell Technologies.
+author: Abirami N (@abirami-n)
+options:
+ config:
+ description: A list of VRF configurations.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ type: str
+ description: The name of the VRF interface.
+ required: true
+ members:
+ type: dict
+ description: Holds a dictionary mapping of list of interfaces linked to a VRF interface.
+ suboptions:
+ interfaces:
+ type: list
+ elements: dict
+ description: List of interface names that are linked to a specific VRF interface.
+ suboptions:
+ name:
+ type: str
+ description: The name of the physical interface.
+ state:
+ description: "The state of the configuration after module completion."
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+#show ip vrf
+#VRF-NAME INTERFACES
+#----------------------------------------------------------------
+#Vrfcheck1
+#Vrfcheck2
+#Vrfcheck3 Eth1/3
+# Eth1/14
+# Eth1/16
+# Eth1/17
+#Vrfcheck4 Eth1/5
+# Eth1/6
+#
+- name: Configuring vrf deleted state
+ dellemc.enterprise_sonic.sonic_vrfs:
+ config:
+ - name: Vrfcheck4
+ members:
+ interfaces:
+ - name: Eth1/6
+ - name: Vrfcheck3
+ members:
+ interfaces:
+ - name: Eth1/3
+ - name: Eth1/14
+ state: deleted
+#
+# After state:
+# ------------
+#
+#show ip vrf
+#VRF-NAME INTERFACES
+#----------------------------------------------------------------
+#Vrfcheck1
+#Vrfcheck2
+#Vrfcheck3 Eth1/16
+# Eth1/17
+#Vrfcheck4 Eth1/5
+#
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+#show ip vrf
+#VRF-NAME INTERFACES
+#----------------------------------------------------------------
+#Vrfcheck1
+#Vrfcheck2
+#Vrfcheck3 Eth1/16
+# Eth1/17
+#Vrfcheck4
+#
+- name: Configuring vrf merged state
+ dellemc.enterprise_sonic.sonic_vrfs:
+ config:
+ - name: Vrfcheck4
+ members:
+ interfaces:
+ - name: Eth1/5
+ - name: Eth1/6
+ - name: Vrfcheck3
+ members:
+ interfaces:
+ - name: Eth1/3
+ - name: Eth1/14
+ state: merged
+#
+# After state:
+# ------------
+#
+#show ip vrf
+#VRF-NAME INTERFACES
+#----------------------------------------------------------------
+#Vrfcheck1
+#Vrfcheck2
+#Vrfcheck3 Eth1/3
+# Eth1/14
+# Eth1/16
+# Eth1/17
+#Vrfcheck4 Eth1/5
+# Eth1/6
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vrfs.vrfs import VrfsArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vrfs.vrfs import Vrfs
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=VrfsArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Vrfs(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py
new file mode 100644
index 00000000..e6613ba2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/modules/sonic_vxlans.py
@@ -0,0 +1,245 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# © Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved
+# 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 sonic_vxlans
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: sonic_vxlans
+version_added: 1.0.0
+notes:
+- Tested against Enterprise SONiC Distribution by Dell Technologies.
+- Supports C(check_mode).
+short_description: Manage VxLAN EVPN and its parameters
+description: 'Manages interface attributes of Enterprise SONiC interfaces.'
+author: Niraimadaiselvam M (@niraimadaiselvamm)
+options:
+ config:
+ description:
+ - A list of VxLAN configurations.
+ - source_ip and evpn_nvo are required together.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ type: str
+ description: 'The name of the VxLAN.'
+ required: true
+ evpn_nvo:
+ type: str
+ description: 'EVPN nvo name'
+ source_ip:
+ description: 'The source IP address of the VTEP.'
+ type: str
+ primary_ip:
+ description: 'The vtep mclag primary ip address for this node'
+ type: str
+ vlan_map:
+ description: 'The list of VNI map of VLAN.'
+ type: list
+ elements: dict
+ suboptions:
+ vni:
+ type: int
+ description: 'Specifies the VNI ID.'
+ required: true
+ vlan:
+ type: int
+ description: 'VLAN ID for VNI VLAN map.'
+ vrf_map:
+ description: 'list of VNI map of VRF.'
+ type: list
+ elements: dict
+ suboptions:
+ vni:
+ type: int
+ description: 'Specifies the VNI ID.'
+ required: true
+ vrf:
+ type: str
+ description: 'VRF name for VNI VRF map.'
+ state:
+ description: 'The state of the configuration after module completion.'
+ type: str
+ choices:
+ - merged
+ - deleted
+ default: merged
+"""
+EXAMPLES = """
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# do show running-configuration
+#
+#interface vxlan vteptest1
+# source-ip 1.1.1.1
+# primary-ip 2.2.2.2
+# map vni 101 vlan 11
+# map vni 102 vlan 12
+# map vni 101 vrf Vrfcheck1
+# map vni 102 vrf Vrfcheck2
+#!
+#
+- name: "Test vxlans deleted state 01"
+ dellemc.enterprise_sonic.sonic_vxlans:
+ config:
+ - name: vteptest1
+ source_ip: 1.1.1.1
+ vlan_map:
+ - vni: 101
+ vlan: 11
+ vrf_map:
+ - vni: 101
+ vrf: Vrfcheck1
+ state: deleted
+#
+# After state:
+# ------------
+#
+# do show running-configuration
+#
+#interface vxlan vteptest1
+# source-ip 1.1.1.1
+# map vni 102 vlan 12
+# map vni 102 vrf Vrfcheck2
+#!
+#
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# do show running-configuration
+#
+#interface vxlan vteptest1
+# source-ip 1.1.1.1
+# map vni 102 vlan 12
+# map vni 102 vrf Vrfcheck2
+#!
+#
+- name: "Test vxlans deleted state 02"
+ dellemc.enterprise_sonic.sonic_vxlans:
+ config:
+ state: deleted
+#
+# After state:
+# ------------
+#
+# do show running-configuration
+#
+#!
+#
+# Using merged
+#
+# Before state:
+# -------------
+#
+# do show running-configuration
+#
+#!
+#
+- name: "Test vxlans merged state 01"
+ dellemc.enterprise_sonic.sonic_vxlans:
+ config:
+ - name: vteptest1
+ source_ip: 1.1.1.1
+ primary_ip: 2.2.2.2
+ evpn_nvo_name: nvo1
+ vlan_map:
+ - vni: 101
+ vlan: 11
+ - vni: 102
+ vlan: 12
+ vrf_map:
+ - vni: 101
+ vrf: Vrfcheck1
+ - vni: 102
+ vrf: Vrfcheck2
+ state: merged
+#
+# After state:
+# ------------
+#
+# do show running-configuration
+#
+#interface vxlan vteptest1
+# source-ip 1.1.1.1
+# primary-ip 2.2.2.2
+# map vni 101 vlan 11
+# map vni 102 vlan 12
+# map vni 101 vrf Vrfcheck1
+# map vni 102 vrf Vrfcheck2
+#!
+# """
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ type: list
+ sample: >
+ The configuration returned is always in the same format
+ of the parameters above.
+commands:
+ description: The set of commands that are 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.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.vxlans.vxlans import VxlansArgs
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.vxlans.vxlans import Vxlans
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=VxlansArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Vxlans(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/terminal/__init__.py b/ansible_collections/dellemc/enterprise_sonic/plugins/terminal/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/terminal/__init__.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/plugins/terminal/sonic.py b/ansible_collections/dellemc/enterprise_sonic/plugins/terminal/sonic.py
new file mode 100644
index 00000000..20665736
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/plugins/terminal/sonic.py
@@ -0,0 +1,73 @@
+#
+# (c) 2020 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Copyright (c) 2020 Dell Inc.
+#
+# 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
+
+from ansible.errors import AnsibleConnectionFailure
+from ansible.plugins.terminal import TerminalBase
+
+DOCUMENTATION = """
+short_description: Terminal plugin module for sonic CLI modules
+version_added: 1.0.0
+"""
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_stdout_re = [
+ re.compile(br"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:#) ?$"),
+ re.compile(br"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$"),
+ re.compile(br"\$ ?$")
+ ]
+
+ terminal_stderr_re = [
+ re.compile(br"% ?Error"),
+ re.compile(br"% ?Bad secret"),
+ re.compile(br"Syntax error:"),
+ 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+"),
+ ]
+
+ def on_open_shell(self):
+ try:
+ if self._get_prompt().endswith(b'$ '):
+ self._exec_cli_command(b'sonic-cli')
+ self._exec_cli_command(b'terminal length 0')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to open sonic cli')
+
+ def on_become(self, passwd=None):
+ if self._get_prompt().endswith(b'#'):
+ return
+
+ 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 prompt.endswith(b'#'):
+ self._exec_cli_command(b'exit')
diff --git a/ansible_collections/dellemc/enterprise_sonic/rebuild.sh b/ansible_collections/dellemc/enterprise_sonic/rebuild.sh
new file mode 100755
index 00000000..0e17e52b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/rebuild.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+namespace=$(grep -w "namespace" galaxy.yml | awk '{print $2}')
+name=$(grep -w "name" galaxy.yml | awk '{print $2}')
+version=$(grep -w "version" galaxy.yml | awk '{print $2}')
+collection_file="$namespace-$name-$version.tar.gz"
+#echo "$collection_file"
+
+rm -f /root/ansible_log.log
+rm -rf /root/.ansible/collections/ansible_collections/dellemc/enterprise_sonic
+rm "$collection_file"
+ansible-galaxy collection build
+
+ansible-galaxy collection install "$collection_file" --force #-with-deps
+
+# ansible-playbook -i playbooks/common_examples/hosts playbooks/common_examples/sonic_l3_interfaces.yaml -vvvv
+# ansible-playbook -i playbooks/common_examples/hosts playbooks/common_examples/sonic_l3_interfaces_config.yaml -vvvv
+# ansible-playbook -i playbooks/common_examples/hosts playbooks/common_examples/sonic_l3_interfaces_test.yaml -vvvv
+# ansible-playbook -i playbooks/common_examples/hosts -vvvv playbooks/common_examples/sonic_bgp_extcommunities.yaml -vvvv
+# ansible-playbook -i playbooks/common_examples/hosts -vvvv playbooks/common_examples/sonic_bgp_extcommunities_config.yaml -vvv
+# ansible-playbook -i playbooks/common_examples/hosts -vvvv playbooks/common_examples/test.yml -vvvv
+# ansible-playbook -i playbooks/common_examples/hosts -vvvv playbooks/common_examples/sonic_bgp_as_paths_config.yaml -vvvv
diff --git a/ansible_collections/dellemc/enterprise_sonic/requirements.txt b/ansible_collections/dellemc/enterprise_sonic/requirements.txt
new file mode 100644
index 00000000..d7d0f2e2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/requirements.txt
@@ -0,0 +1,2 @@
+paramiko>=2.7
+jinja2>=2.8 \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts b/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts
new file mode 100644
index 00000000..b8ec3e04
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/hosts
@@ -0,0 +1,13 @@
+sonic1 ansible_host=100.94.81.17 ansible_user=admin ansible_password=admin
+sonic2 ansible_host=100.94.81.19 ansible_user=admin ansible_password=admin
+#sonic2 ansible_user=admin ansible_password=admin
+
+[datacenter]
+sonic1
+sonic2
+
+[datacenter:vars]
+ansible_network_os=dellemc.enterprise_sonic.sonic
+ansible_python_interpreter=/usr/bin/python3
+ansible_httpapi_use_ssl=true
+ansible_httpapi_validate_certs=false
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/image-upgrade.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/image-upgrade.yaml
new file mode 100644
index 00000000..0891c1e6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/image-upgrade.yaml
@@ -0,0 +1,31 @@
+---
+- name: "Test SONiC CLI"
+ hosts: datacenter
+ gather_facts: no
+ connection: ssh
+ vars:
+ build_number: 120
+ tasks:
+ - name: Download Image from build server
+ get_url:
+ #url: "http://10.14.1.208/work/sonic_archive1/dell_sonic_3.1.x_share/{{ build_number }}/sonic-broadcom.bin"
+ #url: http://devopsweb.force10networks.com/tftpboot/SONIC/dell_sonic_3.1.x_share/last_good/sonic-broadcom.bin
+ url: http://10.14.1.69/tftpboot/SONIC/dell_sonic_3.1.x_share/{{ build_number }}/sonic-broadcom.bin
+ dest: /tmp/sonic-broadcom-{{ build_number }}.bin
+ mode: '0777'
+ - name: Install the downloaded image
+ become: true
+ command: sonic_installer install /tmp/sonic-broadcom-{{ build_number }}.bin -y
+ register: output
+ - name: wait for 3 seconds
+ pause:
+ seconds: 3
+ - name: Unconditionally reboot the machine with all defaults
+ become: true
+ reboot:
+ - name: "Wait for system to get ready"
+ command: "show system status"
+ register: result
+ until: result.stdout.find("System is ready") >= 0
+ retries: 15
+ delay: 15
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml
new file mode 100644
index 00000000..93dd8544
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/defaults/main.yml
@@ -0,0 +1,66 @@
+---
+idempotnet_condition: "{{ 'Passed' if (idempotent_task_output.failed == false and
+ idempotent_task_output.commands == empty and
+ idempotent_task_output.changed == false)
+ else 'Failed'
+ }}"
+action_condition: "{{ 'Passed' if (action_task_output.failed == false and
+ action_task_output.commands != empty and
+ action_task_output.changed == true)
+ else 'Failed'
+ }}"
+cli_test_condition: "{{ 'Passed' if(cli_tests_output.failed == false and
+ cli_tests_output.changed == false
+ )
+ else 'Failed'
+ }}"
+
+single_run_condition: "{{ 'Passed' if(single_run_task_output.failed == false and
+ single_run_task_output.commands != empty and
+ single_run_task_output.changed == true
+ )
+ else 'Failed'
+ }}"
+
+single_run_idem_condition: "{{ 'Passed' if (single_run_task_output.failed == false and
+ single_run_task_output.commands == empty and
+ single_run_task_output.changed == false)
+ else 'Failed'
+ }}"
+
+REPORT_DIR: "/var/www/html/ansible/regression"
+empty: []
+
+module_name1: debug
+
+std_name: STANDARD
+native_name: NATIVE
+
+interface_mode: STANDARD
+
+default_interface_cli_std: default interface range Eth 1/5-1/10
+default_interface_cli_native: default interface range Ethernet20-40
+
+default_interface_cli:
+ - "{{ default_interface_cli_std if std_name in interface_mode else default_interface_cli_native }}"
+
+native_eth1: Ethernet20
+native_eth2: Ethernet24
+native_eth3: Ethernet28
+native_eth4: Ethernet32
+native_eth5: Ethernet36
+native_eth6: Ethernet40
+
+std_eth1: Eth1/5
+std_eth2: Eth1/6
+std_eth3: Eth1/7
+std_eth4: Eth1/8
+std_eth5: Eth1/9
+std_eth6: Eth1/10
+
+interface1: "{{ std_eth1 if std_name in interface_mode else native_eth1 }}"
+interface2: "{{ std_eth2 if std_name in interface_mode else native_eth2 }}"
+interface3: "{{ std_eth3 if std_name in interface_mode else native_eth3 }}"
+interface4: "{{ std_eth4 if std_name in interface_mode else native_eth4 }}"
+interface5: "{{ std_eth5 if std_name in interface_mode else native_eth5 }}"
+interface6: "{{ std_eth6 if std_name in interface_mode else native_eth6 }}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/meta/main.yaml
new file mode 100644
index 00000000..a84afac6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/meta/main.yaml
@@ -0,0 +1,3 @@
+---
+collections:
+ - dellemc.enterprise_sonic \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/action.facts.report.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/action.facts.report.yaml
new file mode 100644
index 00000000..8c8ee5e5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/action.facts.report.yaml
@@ -0,0 +1,10 @@
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.1': {
+ 'status': action_condition,
+ 'module_stderr': action_task_output.module_stderr | default(action_task_output.msg | default('No Error')),
+ 'before': action_task_output.before | default('Not defined'),
+ 'after': action_task_output.after | default('Not defined'),
+ 'commands': action_task_output.commands | default('Not defined'),
+ 'configs': item.input | default('Not defined'),
+ }}}, recursive=True) }}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.contains.test.facts.report.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.contains.test.facts.report.yaml
new file mode 100644
index 00000000..66e263e6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.contains.test.facts.report.yaml
@@ -0,0 +1,11 @@
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.3': {
+ 'status': cli_contains_condition,
+ 'module_stderr': cli_tests_output.module_stderr | default(cli_tests_output.msg | default('No Error')),
+ 'commands': cli_tests_output.commands | default('Not defined'),
+ 'configs': item.input | default('Not defined'),
+ 'msg': cli_tests_output.msg | default('Not defined'),
+ }}}, recursive=True) }}"
+ # no_log: true
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.test.facts.report.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.test.facts.report.yaml
new file mode 100644
index 00000000..b8165d1b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli.test.facts.report.yaml
@@ -0,0 +1,11 @@
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.3': {
+ 'status': cli_test_condition,
+ 'module_stderr': cli_tests_output.module_stderr | default(cli_tests_output.msg | default('No Error')),
+ 'commands': cli_tests_output.commands | default('Not defined'),
+ 'configs': item.input | default('Not defined'),
+ 'msg': cli_tests_output.msg | default('Not defined'),
+ }}}, recursive=True) }}"
+ # no_log: true
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli_tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli_tasks_template.yaml
new file mode 100644
index 00000000..4c5fa8e7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/cli_tasks_template.yaml
@@ -0,0 +1,14 @@
+- debug: msg="{{ base_cfg_path + item.name }}.cfg"
+
+- name: "Push CLI for validation"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ src: "{{ base_cfg_path + item.name }}.cfg"
+ register: cli_tests_output
+ ignore_errors: yes
+
+- debug: var=cli_tests_output
+- import_role:
+ name: common
+ tasks_from: cli.test.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/idempotent.facts.report.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/idempotent.facts.report.yaml
new file mode 100644
index 00000000..adeec696
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/idempotent.facts.report.yaml
@@ -0,0 +1,12 @@
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.2': {
+ 'status': idempotnet_condition,
+ 'module_stderr': idempotent_task_output.module_stderr | default(idempotent_task_output.msg | default('No Error')),
+ 'before': idempotent_task_output.before | default('Not defined'),
+ 'after': idempotent_task_output.after | default('Not defined'),
+ 'commands': idempotent_task_output.commands | default('Not defined'),
+ 'configs': item.input | default('Not defined'),
+ 'msg': idempotent_task_output.msg | default('Not defined'),
+ }}}, recursive=True) }}"
+ # no_log: true \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/main.yml
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/main.yml
@@ -0,0 +1 @@
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/single.run.facts.report.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/single.run.facts.report.yaml
new file mode 100644
index 00000000..e950b010
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/tasks/single.run.facts.report.yaml
@@ -0,0 +1,10 @@
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.1': {
+ 'status': single_run_task_status,
+ 'module_stderr': single_run_task_output.module_stderr | default(single_run_task_output.msg | default('No Error')),
+ 'before': single_run_task_output.before | default('Not defined'),
+ 'after': single_run_task_output.after | default('Not defined'),
+ 'commands': single_run_task_output.commands | default('Not defined'),
+ 'configs': item.input | default('Not defined'),
+ }}}, recursive=True) }}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template.j2 b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template.j2
new file mode 100644
index 00000000..9ad1384b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template.j2
@@ -0,0 +1,14 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_{{module_name}}:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- debug: var=action_task_output
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.1': action_condition }}, recursive=True) }}"
+ #no_log: true
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template1.j2 b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template1.j2
new file mode 100644
index 00000000..1afb4859
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/common/templates/task_template1.j2
@@ -0,0 +1,14 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_{{module_name}}:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- debug: var=action_task_output
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.1': action_condition }}, recursive=True) }}"
+ #no_log: true
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml
new file mode 100644
index 00000000..291f615e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/defaults/main.yml
@@ -0,0 +1,54 @@
+---
+ansible_connection: httpapi
+module_name: aaa
+tests:
+ - name: test_case_01
+ description: aaa properties
+ state: merged
+ input:
+ authentication:
+ data:
+ fail_through: true
+ group: tacacs+
+ local: true
+
+ - name: test_case_02
+ description: Update created aaa properties
+ state: merged
+ input:
+ authentication:
+ data:
+ fail_through: false
+
+ - name: test_case_03
+ description: Update aaa properties - change group
+ state: merged
+ input:
+ authentication:
+ data:
+ fail_through: true
+ group: radius
+ local: true
+
+ - name: test_case_04
+ description: Delete aaa properties
+ state: deleted
+ input:
+ authentication:
+ data:
+ group: radius
+
+ - name: test_case_05
+ description: aaa properties
+ state: merged
+ input:
+ authentication:
+ data:
+ fail_through: true
+ group: radius
+ local: true
+
+test_delete_all:
+ - name: del_all_test_case_01
+ description: Delete aaa properties
+ state: deleted
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/meta/main.yaml
new file mode 100644
index 00000000..d0ceaf6f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/main.yml
new file mode 100644
index 00000000..fdcaa9a4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/main.yml
@@ -0,0 +1,17 @@
+- debug: msg="sonic_aaa Test started ..."
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "test_delete_all {{ module_name }} stated ..."
+ include_tasks: tasks_template_del.yaml
+ loop: "{{ test_delete_all }}"
+ when: test_delete_all is defined
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..e8a964f3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old radius server configurations
+ sonic_aaa:
+ config: {}
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template.yaml
new file mode 100644
index 00000000..3b9f6b98
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_aaa:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_aaa:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml
new file mode 100644
index 00000000..b50cc26c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_aaa:
+ state: "{{ item.state }}"
+ config:
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_aaa:
+ state: "{{ item.state }}"
+ config:
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/defaults/main.yml
new file mode 100644
index 00000000..2e553a03
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+ansible_connection: httpapi
+
+preparations_tests:
+ init_prefix:
+ - "no ip prefix-list p1" \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/invalid.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/invalid.yaml
new file mode 100644
index 00000000..8e7f1513
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/invalid.yaml
@@ -0,0 +1,28 @@
+---
+- name: "Test sonic_api with invalid payload"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet64/config/description
+ method: "PATCH"
+ status_code: 204
+ body: {"openconfig-interfaces:descriptio": "hi "}
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == true"
+ - "result.msg is defined"
+
+- name: "Test sonic_api with invalid url"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet64/config/
+ method: "PATCH"
+ status_code: 204
+ body: {"openconfig-interfaces:description": "hi "}
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - "result.failed == true"
+ - "result.msg is defined"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/main.yaml
new file mode 100644
index 00000000..8e9893d8
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/main.yaml
@@ -0,0 +1,10 @@
+---
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- include_tasks: test_get.yaml
+- include_tasks: test_patch.yaml
+- include_tasks: test_post.yaml
+- include_tasks: test_put.yaml
+- include_tasks: test_delete.yaml
+- include_tasks: invalid.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/patch.txt b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/patch.txt
new file mode 100644
index 00000000..cacbc086
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/patch.txt
@@ -0,0 +1,8 @@
+{"openconfig-if-ip:ipv4": {
+ "addresses": {
+ "address": [
+ {
+ "ip": "1.1.1.1",
+ "config": {"ip": "1.1.1.1", "prefix-length": 24}
+ }]}}
+ }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..a9be2b6d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/preparation_tests.yaml
@@ -0,0 +1,6 @@
+- name: "remove prefix"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_prefix }}"
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_delete.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_delete.yaml
new file mode 100644
index 00000000..6b646cd5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_delete.yaml
@@ -0,0 +1,12 @@
+---
+ - name: "Test delete_api and check whether it returns code 204"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet64/subinterfaces/subinterface=0/openconfig-if-ip:ipv4/addresses/address=1.1.1.1/config/prefix-length
+ method: "DELETE"
+ status_code: 204
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == true"
+ - "204 in result.response[0]"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_get.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_get.yaml
new file mode 100644
index 00000000..b705840b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_get.yaml
@@ -0,0 +1,11 @@
+---
+- name: "check whether get_api returns code 200"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet8
+ method: "GET"
+ status_code: 200
+ register: result
+- assert:
+ that:
+ - "result.changed == false"
+ - "200 in result.response[0]"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_patch.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_patch.yaml
new file mode 100644
index 00000000..0e779612
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_patch.yaml
@@ -0,0 +1,13 @@
+---
+ - name: "Test patch_api and check whether code 204 is returned"
+ sonic_api:
+ url: data/openconfig-interfaces:interfaces/interface=Ethernet64/config/description
+ method: "PATCH"
+ status_code: 204
+ body: {"openconfig-interfaces:description": "hi "}
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == true"
+ - "204 in result.response[0]"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_post.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_post.yaml
new file mode 100644
index 00000000..50383888
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_post.yaml
@@ -0,0 +1,27 @@
+---
+ - name: "Test post_api and check whether code 201 is returned"
+ sonic_api:
+ url: data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets/prefix-set=p1
+ method: "POST"
+ body: {"openconfig-routing-policy:config": {"name": "p1", "mode": "IPV4" }}
+ status_code: 201
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == true"
+ - "201 in result.response[0]"
+
+ - name: "Test post_api to create same prefix-set and check whether play is failed"
+ sonic_api:
+ url: data/openconfig-routing-policy:routing-policy/defined-sets/prefix-sets/prefix-set=p1
+ method: "POST"
+ body: {"openconfig-routing-policy:config": {"name": "p1", "mode": "IPV4" }}
+ status_code: 201
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - "result.failed == true"
+ - "result.msg is defined"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_put.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_put.yaml
new file mode 100644
index 00000000..4b0a7abe
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_api/tasks/test_put.yaml
@@ -0,0 +1,13 @@
+---
+ - name: "Test put_api and check whether code 204 is returned"
+ sonic_api:
+ url: data/openconfig-network-instance:network-instances/network-instance=Vlan100
+ method: "PUT"
+ body: {"openconfig-network-instance:network-instance": [{"name": "Vlan100", "config": {"name": "Vlan100"}}]}
+ status_code: 204
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == true"
+ - "204 in result.response[0]"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml
new file mode 100644
index 00000000..0eb7a6cb
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/defaults/main.yml
@@ -0,0 +1,250 @@
+---
+ansible_connection: httpapi
+module_name: bgp
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+bgp_as_1: 51
+bgp_as_2: 52
+bgp_as_3: 53
+
+preparations_tests:
+ init_vrf:
+ - "ip vrf {{vrf_1}}"
+ - "ip vrf {{vrf_2}}"
+
+tests_cli:
+ - name: cli_test_case_01
+ description: creates bestpath BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.4
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ max_med:
+ on_startup:
+ timer: 667
+ med_val: 7878
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.5
+ vrf_name: "{{vrf_1}}"
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ max_med:
+ on_startup:
+ timer: 889
+ med_val: 8854
+
+tests:
+ - name: test_case_01
+ description: creates BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.4
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.5
+ vrf_name: "{{vrf_1}}"
+ - name: test_case_02
+ description: Updates BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.31
+ vrf_name: "{{vrf_1}}"
+ log_neighbor_changes: True
+ - name: test_case_03
+ description: Deletes BGP properties
+ state: deleted
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.31
+ vrf_name: "{{vrf_1}}"
+ log_neighbor_changes: True
+ - name: test_case_04
+ description: creates bestpath BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.4
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ max_med:
+ on_startup:
+ timer: 889
+ med_val: 8854
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.5
+ vrf_name: "{{vrf_1}}"
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ confed: True
+ missing_as_worst: True
+ max_med:
+ on_startup:
+ timer: 556
+ med_val: 5567
+ - name: test_case_05
+ description: Update bestpath BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.51
+ bestpath:
+ as_path:
+ confed: False
+ ignore: False
+ compare_routerid: False
+ med:
+ confed: False
+ max_med:
+ on_startup:
+ timer: 776
+ med_val: 7768
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.52
+ vrf_name: "{{vrf_1}}"
+ bestpath:
+ as_path:
+ multipath_relax: False
+ multipath_relax_as_set: False
+ compare_routerid: False
+ med:
+ missing_as_worst: False
+ max_med:
+ on_startup:
+ timer: 445
+ med_val: 4458
+ - name: test_case_06
+ description: Update1 bestpath BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.51
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ compare_routerid: True
+ med:
+ confed: True
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.52
+ vrf_name: "{{ vrf_1 }}"
+ bestpath:
+ as_path:
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ missing_as_worst: True
+ - name: test_case_07
+ description: Deletes BGP properties
+ state: deleted
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ bestpath:
+ as_path:
+ confed: False
+ ignore: False
+ compare_routerid: False
+ med:
+ confed: False
+ max_med:
+ on_startup:
+ timer: 889
+ med_val: 8854
+ - bgp_as: "{{ bgp_as_2 }}"
+ vrf_name: "{{vrf_1}}"
+ bestpath:
+ as_path:
+ multipath_relax: True
+ multipath_relax_as_set: False
+ compare_routerid: True
+ med:
+ missing_as_worst: False
+ max_med:
+ on_startup:
+ timer: 889
+ med_val: 8854
+ - name: test_case_08
+ description: Update1 bestpath BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ router_id: 110.2.2.51
+ bestpath:
+ as_path:
+ confed: True
+ ignore: True
+ compare_routerid: True
+ med:
+ confed: True
+ - bgp_as: "{{ bgp_as_2 }}"
+ router_id: 110.2.2.52
+ vrf_name: "{{ vrf_1 }}"
+ bestpath:
+ as_path:
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ missing_as_worst: True
+ - bgp_as: "{{ bgp_as_3 }}"
+ router_id: 120.2.2.52
+ vrf_name: "{{ vrf_2 }}"
+ bestpath:
+ as_path:
+ multipath_relax: True
+ multipath_relax_as_set: True
+ compare_routerid: True
+ med:
+ missing_as_worst: True
+ - name: test_case_09
+ description: Deletes BGP properties
+ state: deleted
+ input:
+ - bgp_as: "{{ bgp_as_2 }}"
+ vrf_name: "{{vrf_1}}"
+ - name: test_case_10
+ description: Deletes all BGP properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..8a52f127
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/cleanup_tests.yaml
@@ -0,0 +1,6 @@
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml
new file mode 100644
index 00000000..29d5392d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/main.yml
@@ -0,0 +1,28 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} CLI validation started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests_cli }}"
+
+- name: "Test CLI validation started ..."
+ include_role:
+ name: common
+ tasks_from: cli_tasks_template.yaml
+ loop: "{{ tests_cli }}"
+
+- name: Clean up test
+ include_tasks: cleanup_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..ec5f139d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/preparation_tests.yaml
@@ -0,0 +1,11 @@
+- name: "initialize VRFs"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_vrf }}"
+
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/tasks_template.yaml
new file mode 100644
index 00000000..7a7394e2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..72000656
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp/templates/cli_test_case_01.cfg
@@ -0,0 +1,18 @@
+router bgp 52 vrf VrfReg1
+ router-id 110.2.2.5
+ log-neighbor-changes
+ bestpath as-path multipath-relax as-set
+ bestpath as-path ignore
+ bestpath as-path confed
+ bestpath med missing-as-worst confed
+ bestpath compare-routerid
+ timers 60 180
+router bgp 51
+ router-id 110.2.2.4
+ log-neighbor-changes
+ bestpath as-path multipath-relax as-set
+ bestpath as-path ignore
+ bestpath as-path confed
+ bestpath med missing-as-worst confed
+ bestpath compare-routerid
+ timers 60 180
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml
new file mode 100644
index 00000000..ba23b3f5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/defaults/main.yml
@@ -0,0 +1,324 @@
+---
+ansible_connection: httpapi
+module_name: bgp_af
+
+
+bgp_as_1: 51
+bgp_as_2: 52
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+preparations_tests:
+ init_route_map:
+ - route-map rmap_reg1 permit 11
+ - route-map rmap_reg2 permit 11
+ - route-map rmap_reg3 permit 11
+ init_vrf:
+ - "ip vrf {{vrf_1}}"
+ - "ip vrf {{vrf_2}}"
+ init_bgp:
+ - bgp_as: "{{bgp_as_1}}"
+ router_id: 111.2.2.41
+ log_neighbor_changes: False
+ - bgp_as: "{{bgp_as_2}}"
+ router_id: 111.2.2.42
+ log_neighbor_changes: True
+ vrf_name: VrfReg1
+tests:
+ - name: test_case_01
+ description: BGP AF properties
+ state: merged
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ - afi: ipv6
+ safi: unicast
+ - afi: l2vpn
+ safi: evpn
+ - bgp_as: "{{ bgp_as_2 }}"
+ vrf_name: "{{vrf_1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ - afi: ipv6
+ safi: unicast
+ - afi: l2vpn
+ safi: evpn
+ - name: test_case_02
+ description: Update created BGP AF properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 2
+ ibgp: 3
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "26"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "28"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ advertise_pip: True
+ advertise_pip_ip: "1.1.1.1"
+ advertise_pip_peer_ip: "2.2.2.2"
+ advertise_svi_ip: True
+ advertise_all_vni: True
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+ - bgp_as: "{{bgp_as_2}}"
+ vrf_name: "{{vrf_1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 1
+ ibgp: 2
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "26"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 2
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "28"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+ - name: test_case_03
+ description: Update2 created BGP AF properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: default
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 2
+ ibgp: 3
+ redistribute:
+ - metric: "30"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "36"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "35"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "31"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "37"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "38"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ advertise_pip: False
+ advertise_pip_ip: "3.3.3.3"
+ advertise_pip_peer_ip: "4.4.4.4"
+ advertise_svi_ip: False
+ advertise_all_vni: False
+ route_advertise_list:
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
+ - bgp_as: "{{bgp_as_2}}"
+ vrf_name: "{{vrf_1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 4
+ ibgp: 5
+ redistribute:
+ - metric: "40"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "41"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "42"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 9
+ ibgp: 8
+ redistribute:
+ - metric: "43"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "44"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "45"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
+ - name: test_case_04
+ description: Delete BGP AF properties
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: default
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 4
+ ibgp: 3
+ redistribute:
+ - metric: "30"
+ protocol: connected
+ route_map: rmap_reg1
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "37"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "38"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ advertise_pip: False
+ advertise_pip_ip: "3.3.3.3"
+ advertise_pip_peer_ip: "4.4.4.4"
+ advertise_svi_ip: False
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+ - bgp_as: "{{bgp_as_2}}"
+ vrf_name: "{{vrf_1}}"
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 4
+ ibgp: 3
+ redistribute:
+ - metric: "41"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "42"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 4
+ ibgp: 6
+ redistribute:
+ - metric: "43"
+ protocol: connected
+ route_map: rmap_reg3
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - advertise_afi: ipv4
+ - name: test_case_05
+ description: Delete1 BGP AF properties
+ state: deleted
+ input:
+ - bgp_as: "{{ bgp_as_1 }}"
+ vrf_name: default
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ redistribute:
+ - afi: ipv6
+ safi: unicast
+ redistribute:
+ - bgp_as: "{{bgp_as_2}}"
+ vrf_name: "{{vrf_1}}"
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - name: test_case_06
+ description: Delete2 BGP AF properties
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: default
+ address_family:
+ afis:
+ - name: test_case_07
+ description: Delete3 BGP AF properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..002f8ab7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/cleanup_tests.yaml
@@ -0,0 +1,10 @@
+- name: Deletes old bgp_af
+ sonic_bgp_af:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/main.yml
new file mode 100644
index 00000000..287404c2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/main.yml
@@ -0,0 +1,15 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+
+- name: "cleanup {{ module_name }} started ..."
+ include_tasks: cleanup_tests.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..82a2f308
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/preparation_tests.yaml
@@ -0,0 +1,20 @@
+- name: "initialize route maps"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_route_map }}"
+- name: "initialize VRFs"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_vrf }}"
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Create bgp
+ sonic_bgp:
+ config: "{{ preparations_tests.init_bgp }}"
+ state: merged
+ ignore_errors: yes \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/tasks_template.yaml
new file mode 100644
index 00000000..9fe7149d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_af/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp_af:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_af:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml
new file mode 100644
index 00000000..f2e31e4a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/defaults/main.yml
@@ -0,0 +1,78 @@
+---
+ansible_connection: httpapi
+module_name: bgp_as_paths
+
+bgp_as_1: 51
+bgp_as_2: 52
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+tests:
+ - name: test_case_01
+ description: BGP properties
+ state: merged
+ input:
+ - name: test
+ members:
+ - "11"
+ permit: True
+ - name: test_1
+ members:
+ - "101.101"
+ permit: False
+ - name: test_case_02
+ description: Update created BGP properties
+ state: merged
+ input:
+ - name: test
+ members:
+ - "11"
+ - "22"
+ - "33"
+ - 44
+ permit: True
+ - name: test_1
+ members:
+ - "101.101"
+ - "201.201"
+ - "301.301"
+ permit: False
+ - name: test_2
+ members:
+ - '111\\:'
+ - '11\\d+'
+ - '113\\*'
+ - '114\\'
+ permit: True
+ - name: test_case_03
+ description: Delete BGP properties
+ state: deleted
+ input:
+ - name: test
+ members:
+ - "33"
+ - name: test_1
+ members:
+ - "101.101"
+ - "201.201"
+ - "301.301"
+ permit: False
+ - name: test_2
+ members:
+ - '111\\:'
+ - '11\\d+'
+ - '113\\*'
+ - '114\\'
+ permit: True
+ - name: test_case_04
+ description: Delete BGP properties
+ state: deleted
+ input:
+ - name: test
+ members:
+ permit:
+ - name: test_case_05
+ description: Delete BGP properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/main.yml
new file mode 100644
index 00000000..51c65668
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/main.yml
@@ -0,0 +1,13 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..f524c06f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old bgp as paths
+ sonic_bgp_as_paths:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/tasks_template.yaml
new file mode 100644
index 00000000..70dbcdff
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_as_paths/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp_as_paths:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: true
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_as_paths:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: true
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml
new file mode 100644
index 00000000..eb32d275
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/defaults/main.yml
@@ -0,0 +1,101 @@
+---
+ansible_connection: httpapi
+module_name: bgp_communities
+
+tests:
+ - name: test_case_01
+ description: BGP Communities properties
+ state: merged
+ input:
+ - name: test
+ type: expanded
+ permit: false
+ match: ANY
+ members:
+ regex:
+ - "11"
+ - "12"
+ - name: test2
+ type: standard
+ permit: true
+ match: ALL
+ members:
+ regex:
+ - "21"
+ - "22"
+ - name: test_case_02
+ description: Update created BGP properties
+ state: merged
+ input:
+ - name: test
+ type: expanded
+ permit: false
+ match: ANY
+ members:
+ regex:
+ - "12"
+ - "13"
+ - 14
+ - name: test2
+ type: standard
+ permit: true
+ match: ALL
+ members:
+ regex:
+ - "23"
+ - "24"
+ - 25
+ - name: test_case_03
+ description: Update1 created BGP properties
+ state: merged
+ input:
+ - name: test
+ type: expanded
+ permit: true
+ match: ANY
+ members:
+ regex:
+ - "11"
+ - "12"
+ - name: test2
+ type: standard
+ permit: false
+ match: ALL
+ members:
+ regex:
+ - "21"
+ - "22"
+ - name: test_case_04
+ description: Delete BGP properties
+ state: deleted
+ input:
+ - name: test
+ type: expanded
+ members:
+ regex:
+ - "12"
+ - "13"
+ - name: test2
+ type: standard
+ match: ALL
+ members:
+ regex:
+ - "23"
+ - "24"
+ - name: test_case_05
+ description: Delete1 BGP properties
+ state: deleted
+ input:
+ - name: test
+ type: expanded
+ members:
+ regex:
+ - name: test_case_06
+ description: Delete2 BGP properties
+ state: deleted
+ input:
+ - name: test
+ - name: test_case_07
+ description: Delete2 BGP properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/main.yml
new file mode 100644
index 00000000..94d190b3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/main.yml
@@ -0,0 +1,13 @@
+- debug: msg="{{ module_name }} Test started ..."
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..d204af4e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old bgp sonic_bgp_communities
+ sonic_bgp_communities:
+ config: []
+ state: deleted
+ ignore_errors: yes \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/tasks_template.yaml
new file mode 100644
index 00000000..e875d051
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_communities/tasks/tasks_template.yaml
@@ -0,0 +1,23 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp_communities:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: true
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_communities:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: true
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
+
+- debug: var=action_task_output \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml
new file mode 100644
index 00000000..be6e96a8
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml
@@ -0,0 +1,321 @@
+---
+ansible_connection: httpapi
+module_name: sonic_bgp_ext_communities
+
+tests: "{{ merged_tests + deleted_tests }}"
+
+merged_tests:
+ - name: test_case_01
+ description: BGP Communities properties
+ state: merged
+ input:
+ - name: test_ext
+ type: expanded
+ permit: false
+ match: any
+ members:
+ regex:
+ - "11"
+ - "12"
+ - name: test_std
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "101:101"
+ - "201:201"
+ route_origin:
+ - "301:301"
+ - "401:401"
+ - name: test_case_02
+ description: Update permit BGP Communities properties
+ state: merged
+ input:
+ - name: test_ext
+ type: expanded
+ permit: false
+ match: any
+ members:
+ regex:
+ - "13"
+ - "14"
+ - name: test_std
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "2201:101"
+ - "3301:201"
+ - "11.1.1.1:223"
+ - "11.1.1.2:224"
+ route_origin:
+ - "4401:301"
+ - "5501:401"
+ - name: test_case_03
+ description: Update permit BGP Communities properties
+ state: merged
+ input:
+ - name: test_ext
+ type: expanded
+ permit: false
+ match: any
+ members:
+ regex:
+ - "15"
+ - "16"
+ - name: test_std
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "2202:101"
+ - "3302:201"
+ - "11.1.1.1:225"
+ - "11.1.1.2:226"
+ - "11.1.1.3:225"
+ - "11.1.1.4:226"
+ - name: test_case_04
+ description: Update match BGP Communities properties
+ state: merged
+ input:
+ - name: test_ext
+ type: expanded
+ permit: false
+ match: any
+ members:
+ regex:
+ - "15"
+ - "16"
+ - name: test_std
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_origin:
+ - "4403.301"
+ - "5503.401"
+ - name: test_case_05
+ description: Create empty Communities properties
+ state: merged
+ input:
+ - name: test_ext1
+ type: expanded
+ permit: true
+ match: any
+ - name: test_std1
+ type: standard
+ permit: false
+ match: any
+ - name: test_case_06
+ description: test BGP Communities properties
+ state: merged
+ input:
+ - name: test_comm112
+ type: expanded
+ permit: true
+ match: any
+ members:
+ regex:
+ - "15"
+ - "16"
+ - name: test_comm
+ type: standard
+ permit: false
+ match: any
+ members:
+ route_origin:
+ - "4403.301"
+ - "5503.401"
+
+deleted_tests:
+ # Ethernet testcases started...
+ - name: del_test_case_0111
+ description: BGP Communities properties
+ state: merged
+ input:
+ - name: test_ext
+ type: expanded
+ permit: false
+ match: any
+ members:
+ regex:
+ - "11"
+ - "12"
+ - "13"
+ - "14"
+ - "15"
+ - "16"
+ - name: test_std
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "101.101"
+ - "201.201"
+ - "102.101"
+ - "202.201"
+ - "1.1.1.1.101"
+ - "1.1.1.2.201"
+ route_origin:
+ - "301.301"
+ - "401.401"
+ - "302.301"
+ - "402.401"
+ - "303.301"
+ - "403.401"
+ - name: test_std11
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "101.101"
+ - "201.201"
+ - "102.101"
+ - "202.201"
+ - "103.101"
+ - "203.201"
+ - "1.1.1.1.101"
+ - "1.1.1.2.201"
+ - "1.1.1.1.102"
+ - "1.1.1.2.203"
+ route_origin:
+ - "301.301"
+ - "401.401"
+ - "302.301"
+ - "402.401"
+ - "303.301"
+ - "403.401"
+ - name: test_std12
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "101.101"
+ - "201.201"
+ - "102.101"
+ - "202.201"
+ - "103.101"
+ - "203.201"
+ - "1.1.1.1.101"
+ - "1.1.1.2.201"
+ - "1.1.1.1.102"
+ - "1.1.1.2.203"
+ route_origin:
+ - "301.301"
+ - "401.401"
+ - "302.301"
+ - "402.401"
+ - "303.301"
+ - "403.401"
+ - name: test_std12
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "101.101"
+ - "201.201"
+ - "102.101"
+ - "202.201"
+ - "103.101"
+ - "203.201"
+ - "1.1.1.1.101"
+ - "1.1.1.2.201"
+ - "1.1.1.1.102"
+ - "1.1.1.2.203"
+ route_origin:
+ - "301.301"
+ - "401.401"
+ - "302.301"
+ - "402.401"
+ - "303.301"
+ - "403.401"
+ - name: test_std12
+ type: standard
+ permit: true
+ match: all
+ members:
+ route_target:
+ - "103.101"
+ - "203.201"
+ - "1.1.1.1:102"
+ - "1.1.1.2.203"
+ route_origin:
+ - "301.301"
+ - "401.401"
+ - name: del_test_case_01
+ description: BGP Communities properties
+ state: deleted
+ input:
+ - name: test_ext
+ type: expanded
+ members:
+ regex:
+ - "11"
+ - "12"
+ - name: test_std
+ type: standard
+ members:
+ route_target:
+ - "101:101"
+ - "201:201"
+ route_origin:
+ - "301:301"
+ - "401:401"
+ - name: test_std1
+ type: standard
+ members:
+ route_target:
+ - "101:101"
+ - "201:201"
+ route_origin:
+ - "301:301"
+ - "401:401"
+ - name: test_case_02
+ description: BGP Communities properties
+ state: deleted
+ input:
+ - name: test_ext
+ type: expanded
+ members:
+ regex:
+ - name: test_std
+ type: standard
+ members:
+ route_target:
+ - "1.1.1.1.101"
+ - name: test_std11
+ type: standard
+ members:
+ route_origin:
+ - "301.301"
+ - "401.401"
+ - "1.1.1.1.101"
+ - name: del_test_case_03
+ description: Update created BGP properties
+ state: deleted
+ input:
+ - name: test_ext
+ - name: test_std
+ type: standard
+ members:
+ route_target:
+ - name: test_std11
+ type: standard
+ members:
+ route_origin:
+ - name: test_std12
+ type: standard
+ members:
+ route_origin:
+ route_target:
+ - name: del_test_case_04
+ description: Update created BGP properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/main.yml
new file mode 100644
index 00000000..d2ae6b41
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/main.yml
@@ -0,0 +1,13 @@
+- debug: msg="{{ module_name }} Test started ..."
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+# - name: Display all variables/facts known for a host
+# debug:
+# var: ansible_facts.test_reports
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..e4a98edb
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old bgp sonic_bgp_communities
+ sonic_bgp_ext_communities:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/tasks_template.yaml
new file mode 100644
index 00000000..380ef22f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_ext_communities/tasks/tasks_template.yaml
@@ -0,0 +1,23 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp_ext_communities:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: true
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_ext_communities:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: true
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
+
+- debug: var=action_task_output \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml
new file mode 100644
index 00000000..35386125
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main copy.yml
@@ -0,0 +1,316 @@
+---
+ansible_connection: httpapi
+module_name: sonic_bgp_neighbors
+
+bgp_as_1: 51
+bgp_as_2: 52
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+preparations_tests:
+ init_route_map:
+ - route-map rmap_reg1 permit 11
+ - route-map rmap_reg2 permit 11
+ - route-map rmap_reg3 permit 11
+ - route-map rmap_reg4 permit 11
+ - route-map rmap_reg5 permit 11
+ init_vrf:
+ - "ip vrf {{vrf_1}}"
+ - "ip vrf {{vrf_2}}"
+ init_bgp:
+ - bgp_as: "{{bgp_as_1}}"
+ router_id: 111.2.2.41
+ log_neighbor_changes: False
+ - bgp_as: "{{bgp_as_2}}"
+ router_id: 111.2.2.42
+ log_neighbor_changes: True
+ vrf_name: VrfReg1
+
+negative_tests:
+ - name: negative_test_case_01
+ description: allowas_in beyond value
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: Ethernet12
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 11
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - name: negative_test_case_02
+ description: BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: Ethernet12
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 11
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg2
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+
+tests: "{{ merged_tests }}"
+
+deleted_tests:
+ - name: test_case_del_01
+ description: Delete peer group BGP properties
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peergroup:
+ - name: SPINE
+ - name: SPINE1
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peergroup:
+ - name: SPINE
+ - name: SPINE1
+
+
+
+merged_tests:
+ - name: test_case_01
+ description: BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: Ethernet12
+ remote_as: 5
+ peer_group: SPINE
+ advertisement_interval: 10
+ timers:
+ keepalive: 40
+ holdtime: 50
+ bfd: true
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ - neighbor: 192.168.1.4
+ - neighbor: 2::2
+ - neighbor: Ethernet8
+ - neighbor: 192.168.1.5
+ remote_as: 6
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - neighbor: 3::3
+ remote_as: 7
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: Ethernet24
+ remote_as: 11
+ peer_group: SPINE
+ advertisement_interval: 10
+ timers:
+ keepalive: 40
+ holdtime: 50
+ bfd: true
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ - neighbor: 192.168.2.2
+ - neighbor: Ethernet28
+ remote_as: 12
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - name: test_case_02
+ description: Update BGP properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peergroup:
+ - name: SPINE
+ - name: SPINE1
+ neighbors:
+ - neighbor: Ethernet12
+ remote_as: 111
+ peer_group: SPINE
+ advertisement_interval: 11
+ timers:
+ keepalive: 41
+ holdtime: 51
+ bfd: false
+ capability:
+ dynamic: false
+ extended_nexthop: false
+ - neighbor: 192.168.1.4
+ - neighbor: Ethernet8
+ - neighbor: 192.168.1.5
+ remote_as: 112
+ peer_group: SPINE1
+ advertisement_interval: 21
+ timers:
+ keepalive: 22
+ holdtime: 23
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peergroup:
+ - name: SPINE
+ - name: SPINE1
+ neighbors:
+ - neighbor: Ethernet24
+ remote_as: 213
+ peer_group: SPINE1
+ advertisement_interval: 44
+ timers:
+ keepalive: 55
+ holdtime: 44
+ bfd: false
+ capability:
+ dynamic: false
+ extended_nexthop: false
+ - neighbor: 192.168.2.2
+ - neighbor: Ethernet28
+ remote_as: 214
+ peer_group: SPINE
+ advertisement_interval: 45
+ timers:
+ keepalive: 33
+ holdtime: 34
+ capability:
+ dynamic: false
+ - neighbor: 3::3
+ remote_as: 215
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - name: test_case_03
+ description: BGP ipv6 properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: 2::2
+ - neighbor: 11::11
+ remote_as: external
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - neighbor: 3::3
+ remote_as: 7
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: 192.168.2.2
+ - neighbor: Ethernet28
+ remote_as: 12
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ capability:
+ dynamic: true
+ - name: test_case_04
+ description: BGP remote-as properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: Ethernet8
+ remote_as: internal
+ - neighbor: 11::11
+ remote_as: external
+ - neighbor: 67.1.1.1
+ remote_as: 7
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: Ethernet8
+ remote_as: 1345
+ - neighbor: 11::11
+ remote_as: 2345
+ - neighbor: 67.1.1.1
+ remote_as: external
+ - name: test_case_05
+ description: BGP remote-as properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: Ethernet8
+ remote_as: external
+ - neighbor: 11::11
+ remote_as: internal
+ - neighbor: 67.1.1.1
+ remote_as: internal
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peergroup:
+ - name: SPINE
+ neighbors:
+ - neighbor: Ethernet8
+ remote_as: internal
+ - neighbor: 11::11
+ remote_as: external
+ - neighbor: 67.1.1.1
+ remote_as: 1123 \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml
new file mode 100644
index 00000000..140eeeae
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/defaults/main.yml
@@ -0,0 +1,880 @@
+---
+ansible_connection: httpapi
+module_name: sonic_bgp_neighbors
+
+bgp_as_1: 51
+bgp_as_2: 52
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+preparations_tests:
+ init_route_map:
+ - route-map rmap_reg1 permit 11
+ - route-map rmap_reg2 permit 11
+ - route-map rmap_reg3 permit 11
+ - route-map rmap_reg4 permit 11
+ - route-map rmap_reg5 permit 11
+ init_prefix_list:
+ - ip prefix-list p1 seq 1 permit 1.1.1.1/1
+ - ip prefix-list p2 seq 2 permit 2.2.2.2/2
+ init_vrf:
+ - "ip vrf {{vrf_1}}"
+ - "ip vrf {{vrf_2}}"
+ init_bgp:
+ - bgp_as: "{{bgp_as_1}}"
+ router_id: 111.2.2.41
+ log_neighbor_changes: False
+ - bgp_as: "{{bgp_as_1}}"
+ router_id: 111.2.2.41
+ log_neighbor_changes: False
+ vrf_name: VrfReg1
+ - bgp_as: "{{bgp_as_2}}"
+ router_id: 111.2.2.52
+ log_neighbor_changes: True
+ vrf_name: VrfReg2
+
+
+tests: "{{ merged_tests + deleted_tests }}"
+
+action_tests:
+ - name: test_case_action_01
+ description: Delete peer group BGP NEIGHBORS NEIGHBORS properties
+ state: merged
+ input: []
+
+deleted_tests:
+ - name: test_case_del_01
+ description: Delete BGP NEIGHBORS additional attributes
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ auth_pwd:
+ pwd: "U2FsdGVkX19eY7P3qRyyjaFsQgjoSQE71IX6IeBRios="
+ encrypted: true
+ dont_negotiate_capability: false
+ ebgp_multihop:
+ enabled: false
+ multihop_ttl: 2
+ enforce_first_as: false
+ enforce_multihop: false
+ local_address: '1::1'
+ local_as:
+ nbr_description: "description 2"
+ override_capability: false
+ passive: false
+ port: 4
+ solo: false
+ - neighbor: 192.168.1.5
+ disable_connected_check: false
+ shutdown_msg: "msg2"
+ ttl_security: 8
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ auth_pwd:
+ pwd: "U2FsdGVkX19eY7P3qRyyjaFsQgjoSQE71IX6IeBRios="
+ encrypted: true
+ nbr_description: 'description 3'
+ strict_capability_match: false
+ v6only: false
+
+ - name: test_case_del_02
+ description: Delete BGP peer-group prefix-list attributes
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE3
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 2
+ prevent_teardown: false
+ warning_threshold: 88
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 3
+ warning_threshold: 77
+ restart_timer: 10
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - afi: l2vpn
+ safi: evpn
+ prefix_limit:
+ max_prefixes: 4
+ warning_threshold: 66
+ restart_timer: 15
+ prefix_list_in: p2
+ prefix_list_out: p1
+
+ - name: test_case_del_03
+ description: BGP NEIGHBORS remote-as properties
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_type: internal
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ origin: false
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ remote_as:
+ peer_type: internal
+ - neighbor: 11::11
+ remote_as:
+ peer_type: external
+ - neighbor: 67.1.1.1
+ remote_as:
+ peer_as: 7
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_as: 1232
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ remote_as:
+ peer_as: 1234
+ - neighbor: 11::11
+ remote_as:
+ peer_as: 4332
+ - neighbor: 67.1.1.1
+ remote_as:
+ peer_type: external
+
+ - name: test_case_del_04
+ description: BGP NEIGHBORS remote-as properties
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ - neighbor: 11::11
+ - neighbor: 67.1.1.1
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ - neighbor: 11::11
+ - neighbor: 67.1.1.1
+
+ - name: test_case_del_05
+ description: BGP NEIGHBORS delete neighbor peergroup, bfd, and timers
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 2"
+ timers:
+ keepalive: 40
+ holdtime: 50
+ connect_retry: 60
+ neighbors:
+ - neighbor: "{{ interface2 }}"
+ peer_group: SPINE
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 3"
+ timers:
+ keepalive: 41
+ holdtime: 51
+ connect_retry: 61
+ - neighbor: 3::3
+ peer_group: SPINE
+ - neighbor: 192.168.1.5
+ peer_group: SPINE1
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 2"
+ timers:
+ keepalive: 40
+ holdtime: 50
+ connect_retry: 60
+ neighbors:
+ - neighbor: "{{ interface4 }}"
+ peer_group: SPINE
+ - neighbor: "{{ interface3 }}"
+ peer_group: SPINE1
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 4"
+ timers:
+ keepalive: 55
+ holdtime: 44
+ connect_retry: 33
+ - neighbor: 3::3
+ peer_group: SPINE
+
+ - name: test_case_del_06
+ description: Delete peer group additional attributes
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ auth_pwd:
+ pwd: "U2FsdGVkX19eY7P3qRyyjaFsQgjoSQE71IX6IeBRios="
+ encrypted: true
+ dont_negotiate_capability: false
+ ebgp_multihop:
+ enabled: false
+ multihop_ttl: 2
+ enforce_first_as: false
+ enforce_multihop: false
+ local_address: '1.1.1.1'
+ local_as:
+ as: 3
+ no_prepend: false
+ replace_as: false
+ pg_description: "description 2"
+ override_capability: false
+ passive: false
+ solo: false
+ - name: SPINE1
+ disable_connected_check: false
+ shutdown_msg: "msg2"
+ strict_capability_match: false
+ ttl_security: 8
+
+ - name: test_case_del_07
+ description: Delete peer group BGP NEIGHBORS properties
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE1
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ - name: SPINE1
+
+ - name: test_case_del_08
+ description: BGP NEIGHBORS remote-as properties
+ state: deleted
+ input: []
+
+merged_tests:
+ - name: test_case_01
+ description: BGP NEIGHBORS properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_as: 12
+ bfd:
+ enabled: true
+ check_failure: true
+ profile: "profile 1"
+ advertisement_interval: 10
+ timers:
+ keepalive: 40
+ holdtime: 50
+ connect_retry: 60
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 2
+ neighbors:
+ - neighbor: "{{ interface2 }}"
+ remote_as:
+ peer_as: 12
+ peer_group: SPINE
+ advertisement_interval: 10
+ timers:
+ keepalive: 40
+ holdtime: 50
+ connect_retry: 60
+ bfd:
+ enabled: true
+ check_failure: true
+ profile: "profile 1"
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ - neighbor: 192.168.1.4
+ - neighbor: 2::2
+ - neighbor: "{{ interface1 }}"
+ - neighbor: 192.168.1.5
+ remote_as:
+ peer_as: 6
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - neighbor: 3::3
+ remote_as:
+ peer_as: 7
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_type: internal
+ bfd:
+ enabled: true
+ check_failure: true
+ profile: "profile 1"
+ advertisement_interval: 15
+ timers:
+ keepalive: 50
+ holdtime: 40
+ connect_retry: 60
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 3
+ neighbors:
+ - neighbor: "{{ interface3 }}"
+ remote_as:
+ peer_as: 11
+ peer_group: SPINE
+ advertisement_interval: 10
+ timers:
+ keepalive: 40
+ holdtime: 50
+ connect_retry: 60
+ bfd:
+ enabled: true
+ check_failure: true
+ profile: "profile 2"
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ - neighbor: 192.168.2.2
+ - neighbor: "{{ interface4 }}"
+ remote_as:
+ peer_as: 12
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - name: test_case_02
+ description: Update BGP NEIGHBORS properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 2"
+ - name: SPINE1
+ remote_as:
+ peer_type: external
+ bfd:
+ enabled: true
+ check_failure: true
+ profile: "profile 2"
+ advertisement_interval: 15
+ timers:
+ keepalive: 30
+ holdtime: 60
+ connect_retry: 90
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ origin: true
+ neighbors:
+ - neighbor: "{{ interface2 }}"
+ remote_as:
+ peer_as: 111
+ peer_group: SPINE
+ advertisement_interval: 11
+ timers:
+ keepalive: 41
+ holdtime: 51
+ connect_retry: 61
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 3"
+ capability:
+ dynamic: false
+ extended_nexthop: false
+ - neighbor: 192.168.1.4
+ - neighbor: "{{ interface1 }}"
+ - neighbor: 192.168.1.5
+ remote_as:
+ peer_as: 112
+ peer_group: SPINE1
+ advertisement_interval: 21
+ timers:
+ keepalive: 22
+ holdtime: 23
+ connect_retry: 24
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 2"
+ - name: SPINE1
+ remote_as:
+ peer_type: internal
+ bfd:
+ enabled: true
+ check_failure: true
+ profile: "profile 2"
+ advertisement_interval: 30
+ timers:
+ keepalive: 10
+ holdtime: 20
+ connect_retry: 30
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ address_family:
+ afis:
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ origin: true
+ neighbors:
+ - neighbor: "{{ interface3 }}"
+ remote_as:
+ peer_as: 212
+ peer_group: SPINE1
+ advertisement_interval: 44
+ timers:
+ keepalive: 55
+ holdtime: 44
+ connect_retry: 33
+ bfd:
+ enabled: false
+ check_failure: false
+ profile: "profile 4"
+ capability:
+ dynamic: false
+ extended_nexthop: false
+ - neighbor: 192.168.2.2
+ - neighbor: "{{ interface4 }}"
+ remote_as:
+ peer_as: 214
+ peer_group: SPINE
+ advertisement_interval: 45
+ timers:
+ keepalive: 33
+ holdtime: 34
+ connect_retry: 35
+ capability:
+ dynamic: false
+ - neighbor: 3::3
+ remote_as:
+ peer_as: 215
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - name: test_case_03
+ description: BGP NEIGHBORS ipv6 properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_as: 1123
+ neighbors:
+ - neighbor: 2::2
+ - neighbor: 11::11
+ remote_as:
+ peer_type: external
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - neighbor: 3::3
+ remote_as:
+ peer_as: 556
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_type: external
+ address_family:
+ afis:
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 4
+ neighbors:
+ - neighbor: 192.168.2.2
+ - neighbor: "{{ interface4 }}"
+ remote_as:
+ peer_as: 557
+ peer_group: SPINE
+ advertisement_interval: 20
+ timers:
+ keepalive: 30
+ holdtime: 20
+ connect_retry: 10
+ capability:
+ dynamic: true
+ - name: test_case_04
+ description: BGP NEIGHBORS remote-as properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_type: internal
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ remote_as:
+ peer_type: internal
+ - neighbor: 11::11
+ remote_as:
+ peer_type: external
+ - neighbor: 67.1.1.1
+ remote_as:
+ peer_as: 7
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ remote_as:
+ peer_as: 1321
+ address_family:
+ afis:
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ value: 4
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ remote_as:
+ peer_as: 1234
+ - neighbor: 11::11
+ remote_as:
+ peer_as: 4332
+ - neighbor: 67.1.1.1
+ remote_as:
+ peer_type: external
+ - name: test_case_05
+ description: BGP NEIGHBORS remote-as properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ remote_as:
+ peer_type: external
+ - neighbor: 11::11
+ remote_as:
+ peer_type: internal
+ - neighbor: 67.1.1.1
+ remote_as:
+ peer_type: internal
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ peer_group:
+ - name: SPINE
+ capability:
+ dynamic: true
+ extended_nexthop: true
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ remote_as:
+ peer_type: internal
+ - neighbor: 11::11
+ remote_as:
+ peer_type: external
+ - neighbor: 67.1.1.1
+ remote_as:
+ peer_as: 1123
+ - name: test_case_06
+ description: BGP NEIGHBORS configure additional attributes
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ auth_pwd:
+ pwd: "U2FsdGVkX19eY7P3qRyyjaFsQgjoSQE71IX6IeBRios="
+ encrypted: true
+ dont_negotiate_capability: true
+ ebgp_multihop:
+ enabled: true
+ multihop_ttl: 1
+ enforce_first_as: true
+ enforce_multihop: true
+ local_address: "{{ interface5 }}"
+ local_as:
+ as: 2
+ no_prepend: true
+ replace_as: true
+ pg_description: "description 1"
+ override_capability: true
+ passive: true
+ solo: true
+ - name: SPINE1
+ disable_connected_check: true
+ shutdown_msg: "msg1"
+ strict_capability_match: true
+ ttl_security: 5
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ auth_pwd:
+ pwd: "U2FsdGVkX19eY7P3qRyyjaFsQgjoSQE71IX6IeBRios="
+ encrypted: true
+ dont_negotiate_capability: true
+ ebgp_multihop:
+ enabled: true
+ multihop_ttl: 1
+ enforce_first_as: true
+ enforce_multihop: true
+ local_address: "{{ interface5 }}"
+ local_as:
+ as: 2
+ no_prepend: true
+ replace_as: true
+ nbr_description: "description 1"
+ override_capability: true
+ passive: true
+ port: 3
+ solo: true
+ - neighbor: 192.168.1.5
+ disable_connected_check: true
+ shutdown_msg: "msg1"
+ ttl_security: 5
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ auth_pwd:
+ pwd: "U2FsdGVkX19eY7P3qRyyjaFsQgjoSQE71IX6IeBRios="
+ encrypted: true
+ nbr_description: 'description 2'
+ strict_capability_match: true
+ v6only: true
+ - name: test_case_07
+ description: BGP NEIGHBORS modify additional attributes
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE
+ dont_negotiate_capability: false
+ ebgp_multihop:
+ enabled: false
+ multihop_ttl: 2
+ enforce_first_as: false
+ enforce_multihop: false
+ local_address: '1.1.1.1'
+ local_as:
+ as: 3
+ no_prepend: false
+ replace_as: false
+ pg_description: "description 2"
+ override_capability: false
+ passive: false
+ solo: false
+ - name: SPINE1
+ disable_connected_check: false
+ shutdown_msg: "msg2"
+ strict_capability_match: false
+ ttl_security: 8
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ dont_negotiate_capability: false
+ ebgp_multihop:
+ enabled: false
+ multihop_ttl: 2
+ enforce_first_as: false
+ enforce_multihop: false
+ local_address: '1::1'
+ local_as:
+ as: 3
+ no_prepend: false
+ replace_as: false
+ nbr_description: "description 2"
+ override_capability: false
+ passive: false
+ port: 4
+ solo: false
+ - neighbor: 192.168.1.5
+ disable_connected_check: false
+ shutdown_msg: "msg2"
+ ttl_security: 8
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ neighbors:
+ - neighbor: "{{ interface1 }}"
+ nbr_description: 'description 3'
+ strict_capability_match: false
+ v6only: false
+ - name: test_case_08
+ description: Configure BGP peer-group prefix-list attributes
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE3
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 1
+ prevent_teardown: true
+ warning_threshold: 80
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 2
+ warning_threshold: 70
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - afi: l2vpn
+ safi: evpn
+ prefix_limit:
+ max_prefixes: 3
+ warning_threshold: 60
+ restart_timer: 8
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - name: test_case_09
+ description: Modify BGP peer-group prefix-list attributes
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ peer_group:
+ - name: SPINE3
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 2
+ prevent_teardown: false
+ warning_threshold: 88
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 3
+ warning_threshold: 77
+ restart_timer: 10
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - afi: l2vpn
+ safi: evpn
+ prefix_limit:
+ max_prefixes: 4
+ warning_threshold: 66
+ restart_timer: 15
+ prefix_list_in: p2
+ prefix_list_out: p1
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/action_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/action_template.yaml
new file mode 100644
index 00000000..98dee21e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/action_template.yaml
@@ -0,0 +1,10 @@
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_neighbors:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..d8f20a11
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/cleanup_tests.yaml
@@ -0,0 +1,10 @@
+- name: Deletes old bgp_neighbors
+ sonic_bgp_neighbors:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/main.yml
new file mode 100644
index 00000000..c622e0b3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/main.yml
@@ -0,0 +1,19 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "Test action {{ module_name }} started ..."
+ include_tasks: action_template.yaml
+ loop: "{{ action_tests }}"
+
+- name: "Cleanup {{ module_name }} started ..."
+ include_tasks: cleanup_tests.yaml
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..c5917c78
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/preparation_tests.yaml
@@ -0,0 +1,38 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize route maps"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_route_map }}"
+ ignore_errors: yes
+- name: "initialize prefix_lists"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_prefix_list }}"
+ ignore_errors: yes
+- name: "initialize VRFs"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_vrf }}"
+ ignore_errors: yes
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Create bgp
+ sonic_bgp:
+ config: "{{ preparations_tests.init_bgp }}"
+ state: merged
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/tasks_template.yaml
new file mode 100644
index 00000000..d170be5d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp_neighbors:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_neighbors:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml
new file mode 100644
index 00000000..dcb7b46e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/defaults/main.yml
@@ -0,0 +1,468 @@
+---
+ansible_connection: httpapi
+module_name: sonic_bgp_neighbors_af
+
+bgp_as_1: 51
+bgp_as_2: 52
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+preparations_tests:
+ init_route_map:
+ - route-map rmap_reg1 permit 11
+ - route-map rmap_reg2 permit 11
+ - route-map rmap_reg3 permit 11
+ - route-map rmap_reg4 permit 11
+ - route-map rmap_reg5 permit 11
+ init_prefix_list:
+ - ip prefix-list p1 seq 1 permit 1.1.1.1/1
+ - ip prefix-list p2 seq 2 permit 2.2.2.2/2
+ init_vrf:
+ - "ip vrf {{vrf_1}}"
+ - "ip vrf {{vrf_2}}"
+ init_bgp:
+ - bgp_as: "{{bgp_as_1}}"
+ router_id: 111.2.2.41
+ log_neighbor_changes: False
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ router_id: 111.2.2.42
+ log_neighbor_changes: True
+ init_bgp_neighbors:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 12.1.1.1
+ - neighbor: 13.1.1.1
+ - neighbor: 14.1.1.1
+ - neighbor: 15.1.1.1
+ - neighbor: "{{ interface3 }}"
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{vrf_1}}"
+ neighbors:
+ - neighbor: 21.1.1.1
+ - neighbor: 22.1.1.1
+ - neighbor: 23.1.1.1
+ - neighbor: 24.1.1.1
+ - neighbor: "{{ interface4 }}"
+
+negative_tests:
+ - name: negative_test_case_01
+ description: allowas_in beyond value
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: "{{ interface3 }}"
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 11
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - name: negative_test_case_02
+ description: BGP NEIGHBORS AF properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: "{{ interface3 }}"
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 11
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg2
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+tests1:
+ - name: test_case_02
+ description: Update BGP NEIGHBORS AF properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 12.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ origin: true
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ route_reflector_client: false
+ route_server_client: true
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 3
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: false
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ value: 4
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{ vrf_1 }}"
+ neighbors:
+ - neighbor: "{{ interface4 }}"
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 4
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 7
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: false
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ origin: true
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+
+
+tests:
+ - name: test_case_01
+ description: BGP NEIGHBORS AF properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 12.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 7
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ route_reflector_client: false
+ route_server_client: true
+ activate: true
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 7
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: false
+ activate: false
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ origin: true
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ activate: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{ vrf_1 }}"
+ neighbors:
+ - neighbor: "{{ interface4 }}"
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 4
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ activate: false
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 5
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: false
+ activate: true
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ origin: true
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ activate: false
+ - name: test_case_02
+ description: Update BGP NEIGHBORS AF properties
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 12.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ origin: true
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ route_reflector_client: false
+ route_server_client: true
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 3
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: false
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ value: 4
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - bgp_as: "{{bgp_as_1}}"
+ vrf_name: "{{ vrf_1 }}"
+ neighbors:
+ - neighbor: "{{ interface4 }}"
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 4
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - afi: ipv6
+ safi: unicast
+ allowas_in:
+ value: 7
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: false
+ - afi: l2vpn
+ safi: evpn
+ allowas_in:
+ origin: true
+ route_map:
+ - name: rmap_reg1
+ direction: in
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: true
+ route_server_client: true
+ - name: test_case_03
+ description: BGP NEIGHBORS AF
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 12.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 7
+ route_map:
+ - name: rmap_reg1
+ direction: out
+ route_reflector_client: false
+ route_server_client: true
+ - name: test_case_04
+ description: Change route map
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 12.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ allowas_in:
+ value: 7
+ route_map:
+ - name: rmap_reg2
+ direction: out
+ route_reflector_client: false
+ route_server_client: true
+ - name: test_case_05
+ description: Configure BGP neighbor prefix-list attributes
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 15.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 1
+ prevent_teardown: true
+ warning_threshold: 80
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: true
+ prefix_limit:
+ max_prefixes: 2
+ warning_threshold: 70
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - afi: l2vpn
+ safi: evpn
+ prefix_limit:
+ max_prefixes: 3
+ warning_threshold: 60
+ restart_timer: 8
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - name: test_case_06
+ description: Modify BGP neighbor prefix-list attributes
+ state: merged
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 15.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 2
+ prevent_teardown: false
+ warning_threshold: 88
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 3
+ warning_threshold: 77
+ restart_timer: 10
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - afi: l2vpn
+ safi: evpn
+ prefix_limit:
+ max_prefixes: 4
+ warning_threshold: 66
+ restart_timer: 15
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - name: test_case_07
+ description: Delete BGP neighbor prefix-list attributes
+ state: deleted
+ input:
+ - bgp_as: "{{bgp_as_1}}"
+ neighbors:
+ - neighbor: 15.1.1.1
+ address_family:
+ - afi: ipv4
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg2
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 2
+ prevent_teardown: false
+ warning_threshold: 88
+ restart_timer: 5
+ prefix_list_in: p2
+ prefix_list_out: p1
+ - afi: ipv6
+ safi: unicast
+ ip_afi:
+ default_policy_name: rmap_reg1
+ send_default_route: false
+ prefix_limit:
+ max_prefixes: 3
+ warning_threshold: 77
+ restart_timer: 10
+ prefix_list_in: p1
+ prefix_list_out: p2
+ - afi: l2vpn
+ safi: evpn
+ prefix_limit:
+ max_prefixes: 4
+ warning_threshold: 66
+ restart_timer: 15
+ prefix_list_in: p2
+ prefix_list_out: p1
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..453ed64e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/cleanup_tests.yaml
@@ -0,0 +1,15 @@
+- name: Deletes old bgp_neighbors_af
+ sonic_bgp_neighbors_af:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old bgp_neighbors
+ sonic_bgp_neighbors:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/main.yml
new file mode 100644
index 00000000..012bf4ae
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/main.yml
@@ -0,0 +1,21 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- debug: var=interface1
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "Cleanup {{ module_name }} started ..."
+ include_tasks: cleanup_tests.yaml
+
+# - name: "Test nagative {{ module_name }} started ..."
+# include_tasks: negative_tasks_template.yaml
+# loop: "{{ negative_tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..93f74215
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/preparation_tests.yaml
@@ -0,0 +1,43 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize route maps"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_route_map }}"
+ ignore_errors: yes
+- name: "initialize prefix lists"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_prefix_list }}"
+ ignore_errors: yes
+- name: "initialize VRFs"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_vrf }}"
+ ignore_errors: yes
+- name: Deletes old bgp
+ sonic_bgp:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Create bgp
+ sonic_bgp:
+ config: "{{ preparations_tests.init_bgp }}"
+ state: merged
+ ignore_errors: yes
+- name: Create bgp neighbors
+ sonic_bgp_neighbors:
+ config: "{{ preparations_tests.init_bgp_neighbors }}"
+ state: merged
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/tasks_template.yaml
new file mode 100644
index 00000000..1ac758be
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_bgp_neighbors_af/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_bgp_neighbors_af:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_bgp_neighbors_af:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/defaults/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/defaults/main.yaml
new file mode 100644
index 00000000..ae1e7fb6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/defaults/main.yaml
@@ -0,0 +1 @@
+ansible_connection: network_cli \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/bad_operator.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/bad_operator.yaml
new file mode 100644
index 00000000..79150a75
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/bad_operator.yaml
@@ -0,0 +1,33 @@
+---
+
+ - name: Test sonic commands with wait_for negative case
+ sonic_command:
+ commands:
+ - 'show platform'
+ - 'show system'
+ wait_for:
+ - result[0] contains Fel
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - "result.failed == True"
+ - "result.failed_conditions is defined"
+ - "result.msg is defined"
+
+ - name: Test sonic commands with wait_for and match=any
+ sonic_command:
+ commands:
+ - 'show platform'
+ - 'show system'
+ wait_for:
+ - result[0] contains Fel
+ - result[1] contains sonic
+ match: any
+ register: result
+
+ - assert:
+ that:
+ - "result.failed == False"
+ - "result.changed == False"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/cli_command.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/cli_command.yaml
new file mode 100644
index 00000000..f6294f30
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/cli_command.yaml
@@ -0,0 +1,26 @@
+---
+
+ - block:
+
+ - name: get output for single command
+ register: result
+ cli_command:
+ command: show system
+
+ - assert:
+ that:
+ - result.changed == false
+ - result.stdout is defined
+
+ - name: send invalid command
+ register: result
+ ignore_errors: true
+ cli_command:
+ command: show foo
+
+ - assert:
+ that:
+ - result.failed == true
+ - result.msg is defined
+
+ when: ansible_connection == 'network_cli'
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/contains.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/contains.yaml
new file mode 100644
index 00000000..40a7f310
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/contains.yaml
@@ -0,0 +1,29 @@
+---
+
+
+ - name: Test sonic single command with wait_for
+ sonic_command:
+ commands: 'show platform '
+ wait_for:
+ - result[0] contains Del
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == false"
+ - "result.stdout is defined"
+
+ - name: Test sonic multiple command with wait_for
+ sonic_command:
+ commands:
+ - 'show platform'
+ - 'show system'
+ wait_for:
+ - result[0] contains Dell
+ - result[1] contains sonic
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == false"
+ - "result.stdout is defined"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/invalid.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/invalid.yaml
new file mode 100644
index 00000000..0f5278bb
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/invalid.yaml
@@ -0,0 +1,25 @@
+---
+- name: run invalid command
+ register: result
+ ignore_errors: true
+ sonic_command:
+ commands:
+ - show foo
+
+- assert:
+ that:
+ - result.failed == true
+ - result.msg is defined
+
+- name: run commands that include invalid command
+ register: result
+ ignore_errors: true
+ sonic_command:
+ commands:
+ - show system
+ - show foo
+
+- assert:
+ that:
+ - result.failed == true
+ - result.msg is defined
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/main.yaml
new file mode 100644
index 00000000..8dd58fbe
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/main.yaml
@@ -0,0 +1,12 @@
+---
+
+- include_tasks: output.yaml
+- include_tasks: contains.yaml
+- include_tasks: bad_operator.yaml
+- include_tasks: invalid.yaml
+- include_tasks: cli_command.yaml
+- include_tasks: test_local.yaml
+ vars:
+ ansible_connection: local
+- include_tasks: timeout.yaml
+- include_tasks: prompt.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/output.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/output.yaml
new file mode 100644
index 00000000..5f6840cf
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/output.yaml
@@ -0,0 +1,24 @@
+---
+
+ - name: Test sonic single command
+ sonic_command:
+ commands: 'show interface status'
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == false"
+ - "result.stdout is defined"
+
+ - name: Test sonic multiple commands
+ sonic_command:
+ commands:
+ - 'show platform'
+ - 'show system'
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == false"
+ - "result.stdout is defined"
+ - "result.stdout | length == 2"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/prompt.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/prompt.yaml
new file mode 100644
index 00000000..0bf3fc03
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/prompt.yaml
@@ -0,0 +1,15 @@
+---
+
+
+ - name: Test sonic command with prompt handling
+ sonic_command:
+ commands:
+ - command: 'image remove all'
+ prompt: '\[y/N\]:$'
+ answer: 'N'
+ register: result
+
+ - assert:
+ that:
+ - "result.changed == false"
+ - "result.stdout is defined"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/test_local.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/test_local.yaml
new file mode 100644
index 00000000..e765756b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/test_local.yaml
@@ -0,0 +1,16 @@
+---
+
+ - block:
+
+ - name: test failure for local connection
+ register: result
+ cli_command:
+ command: show platform
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result.failed == true
+ - "'Connection type local is not valid for this module' in result.msg"
+ when:
+ ansible_connection == 'local'
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/timeout.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/timeout.yaml
new file mode 100644
index 00000000..1f1f2ffd
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_command/tasks/timeout.yaml
@@ -0,0 +1,30 @@
+---
+
+- name: test bad condition
+ register: result
+ ignore_errors: true
+ sonic_command:
+ commands:
+ - show system
+ wait_for:
+ - result[0] contains bad_value_string
+
+- assert:
+ that:
+ - result.failed == true
+ - result.msg is defined
+
+- name: test bad condition with less retries
+ register: result
+ ignore_errors: true
+ sonic_command:
+ commands:
+ - show system
+ retries: 5
+ wait_for:
+ - result[0] contains bad_value_string
+
+- assert:
+ that:
+ - result.failed == true
+ - result.msg is defined
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/defaults/main.yml
new file mode 100644
index 00000000..38be9bc7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/defaults/main.yml
@@ -0,0 +1,83 @@
+---
+module_name: sonic_config
+ansible_connection: network_cli
+idempotent_condition: "{{ 'Passed' if ( idempotent_task_output.failed == false and
+ idempotent_task_output.commands is not defined and
+ idempotent_task_output.changed == false)
+ else 'Failed'
+ }}"
+action_condition: "{{ 'Passed' if ( action_task_output.failed == false and
+ action_task_output.commands is defined and
+ action_task_output.changed == true)
+ else 'Failed'
+ }}"
+
+prompt_tc_condition: "{{ 'Passed' if ( prompt_tc.commands is defined )
+ else 'Failed'
+ }}"
+
+backup_condition: "{{ 'Passed' if (backup_tc.stat.exists == true)
+ else 'Failed'
+ }}"
+
+replace_or_exact_condition: "{{ 'Passed' if ( replace_tc.commands is defined and replace_tc.commands |length>2) else 'Failed' }}"
+
+empty: []
+
+bgp_as_1: 51
+bgp_as_2: 52
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+snmp_location: snmp_chennai
+snmp_contact: snmp_devops
+
+preparations_tests:
+ clean_cfg_lines:
+ - no interface PortChannel 11
+ - no interface PortChannel 1
+ - no interface PortChannel 2
+ - no snmp-server community abcd
+ - no snmp-server community efgh
+ - no snmp-server community ijkl
+ - no snmp-server community mnop
+ - no snmp-server community qrst
+ - no snmp-server community uvwx
+ - no snmp-server location
+ - no snmp-server contact
+ - interface Vlan 11
+ clean_interfaces:
+ - parent: interface Ethernet8
+ lines:
+ - no description
+
+tests:
+ # Ethernet testcases started...
+ - name: test_case_01
+ description: Configure ip access-list using 'before' and 'after' option on SONIC device
+ input:
+ lines:
+ - mtu 4444
+ parents: ['interface PortChannel 11']
+ before: ['snmp-server community abcd']
+ after: ['snmp-server community efgh']
+ - name: test_case_02
+ description: Test sonic config module with single CLI
+ input:
+ before: 'snmp-server community ijkl'
+ commands: 'snmp-server community mnop'
+ - name: test_case_03
+ description: Test sonic config module with multiple CLI
+ input:
+ commands: ['snmp-server community qrst', 'snmp-server community uvwx']
+ - name: test_case_04
+ description: Configure interface description using parents option on SONIC device
+ input:
+ lines:
+ - description 'hi'
+ parents: ['interface Ethernet8']
+ - name: test_case_05
+ description: Configure cli using source file
+ input:
+ src: snmp.j2
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/backup.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/backup.yaml
new file mode 100644
index 00000000..c6bb3542
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/backup.yaml
@@ -0,0 +1,16 @@
+- name: create configurable backup path
+ sonic_config:
+ backup: yes
+ backup_options:
+ filename: backup.cfg
+ dir_path: /tmp/
+ register: backup_file
+
+- name: Verify file is created or not
+ stat: path={{backup_file.backup_path}}
+ register: backup_tc
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'backup_test_case.1': backup_condition }}, recursive=True) }}"
+ no_log: true
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/main.yml
new file mode 100644
index 00000000..933bb71f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/main.yml
@@ -0,0 +1,24 @@
+- debug: msg="sonic_config Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "Test Prompt validation started ..."
+ include_tasks: prompt.yaml
+
+- name: "Test sublevel command using replace in {{ module_name }} started ..."
+ include_tasks: replace_tasks_template.yaml
+
+- name: "Test sublevel command using match in {{ module_name }} started ..."
+ include_tasks: match_template.yaml
+
+- name: "Test backup option in {{ module_name }} started ..."
+ include_tasks: backup.yaml
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/match_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/match_template.yaml
new file mode 100644
index 00000000..a92a809c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/match_template.yaml
@@ -0,0 +1,64 @@
+- name: Configure sub level command using default match on SONIC device
+ sonic_config:
+ lines: ['switchport access Vlan 11', 'mtu 1500', 'no shutdown']
+ parents: ['interface PortChannel 2']
+ register: action_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'match_test_case.1': action_condition }}, recursive=True) }}"
+ no_log: true
+
+- name: Configure sub level command using default match on SONIC device idempotent
+ sonic_config:
+ lines: ['switchport access Vlan 11', 'mtu 1500', 'no shutdown']
+ parents: ['interface PortChannel 2']
+ register: idempotent_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'match_test_case.2': idempotent_condition }}, recursive=True) }}"
+ when: item.idemponent is not defined or item.idemponent != false
+ no_log: true
+
+- name: Rerun same configure task interchanging positions of sublevel commands using "match=strict" on SONIC device
+ sonic_config:
+ lines: ['mtu 1500', 'switchport access Vlan 11', 'no shutdown']
+ parents: ['interface PortChannel 2']
+ match: strict
+ register: action_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'match_test_case.3': action_condition }}, recursive=True) }}"
+ no_log: true
+
+- name: Configure sublevel command using "match=exact" on SONIC device
+ sonic_config:
+ lines: ['switchport access Vlan 11', 'mtu 1500', 'no shutdown', 'graceful-shutdown']
+ parents: ['interface PortChannel 2']
+ match: exact
+ register: replace_tc
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'match_test_case.4': replace_or_exact_condition }}, recursive=True) }}"
+ no_log: true
+
+- name: Rerun sublevel command using "match=exact" on SONIC device idempotent
+ sonic_config:
+ lines: ['switchport access Vlan 11', 'mtu 1500', 'no shutdown', 'graceful-shutdown']
+ parents: ['interface PortChannel 2']
+ match: exact
+ register: idempotent_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'match_test_case.5': idempotent_condition }}, recursive=True) }}"
+ when: item.idemponent is not defined or item.idemponent != false
+ no_log: true
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..5187a4fa
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/preparation_tests.yaml
@@ -0,0 +1,11 @@
+- name: "clean_cfg_lines"
+ sonic_config:
+ commands: "{{ preparations_tests.clean_cfg_lines }}"
+ register: prep_tasks
+
+- name: "parent based clean cfg"
+ sonic_config:
+ lines: "{{ item.lines }}"
+ parents: "{{ item.parent }}"
+ register: prep_tasks
+ with_items: "{{preparations_tests.clean_interfaces}}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/prompt.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/prompt.yaml
new file mode 100644
index 00000000..ac5f7057
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/prompt.yaml
@@ -0,0 +1,12 @@
+- name: Test sonic config with prompt handling
+ sonic_config:
+ commands:
+ - command: 'do image remove all'
+ prompt: '\[y/N\]:$'
+ answer: 'N'
+ register: prompt_tc
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'prompt_test_case.1': prompt_tc_condition }}, recursive=True) }}"
+ no_log: true
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/replace_tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/replace_tasks_template.yaml
new file mode 100644
index 00000000..9ab50b36
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/replace_tasks_template.yaml
@@ -0,0 +1,38 @@
+- name: Configure sub level command using "default" option on SONIC device
+ sonic_config:
+ lines: ['mtu 1312', 'no shutdown', 'no graceful-shutdown']
+ parents: ['interface PortChannel 1']
+ register: action_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'replace_test_case.1': action_condition }}, recursive=True) }}"
+ no_log: true
+
+- name: Configure sub level command using replace "block" option on SONIC device
+ sonic_config:
+ lines: ['mtu 1312', 'no shutdown', 'graceful-shutdown']
+ parents: ['interface PortChannel 1']
+ replace: block
+ register: replace_tc
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'replace_test_case.2': replace_or_exact_condition }}, recursive=True) }}"
+ no_log: true
+
+- name: Configure sub level command using replace "block" option on SONIC device idempotent
+ sonic_config:
+ lines: ['mtu 1312', 'no shutdown', 'graceful-shutdown']
+ parents: ['interface PortChannel 1']
+ replace: block
+ register: idempotent_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {'replace_test_case.3': idempotent_condition }}, recursive=True) }}"
+ when: item.idemponent is not defined or item.idemponent != false
+ no_log: true
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/tasks_template.yaml
new file mode 100644
index 00000000..fa2e7ace
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_config: "{{ item.input }}"
+ register: action_task_output
+ ignore_errors: true
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.1': action_condition }}, recursive=True) }}"
+ no_log: true
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_config: "{{ item.input }}"
+ register: idempotent_task_output
+ ignore_errors: true
+ when: item.idemponent is not defined or item.idemponent != false
+
+- set_fact:
+ ansible_facts:
+ test_reports: "{{ ansible_facts['test_reports']| default({})| combine({module_name: {item.name+'.2': idempotent_condition }}, recursive=True) }}"
+ when: item.idemponent is not defined or item.idemponent != false
+ no_log: true
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/snmp.j2 b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/snmp.j2
new file mode 100644
index 00000000..cd93c9b5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/snmp.j2
@@ -0,0 +1,6 @@
+{% if snmp_location is defined and snmp_location %}
+snmp-server location "{{ snmp_location }}"
+{% endif %}
+{% if snmp_contact is defined and snmp_contact %}
+snmp-server contact "{{ snmp_contact }}"
+{% endif %}
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/src.txt b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/src.txt
new file mode 100644
index 00000000..fbfeac92
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_config/templates/src.txt
@@ -0,0 +1,3 @@
+interface ethernet 40
+description hello
+mtu 1800
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml
new file mode 100644
index 00000000..d391e690
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/defaults/main.yml
@@ -0,0 +1,153 @@
+---
+ansible_connection: httpapi
+module_name: interfaces
+
+po1: PortChannel50
+po2: PortChannel51
+
+lo1: Loopback 100
+lo2: Loopback 101
+
+vlan1: 500
+vlan2: 501
+
+preparations_tests:
+ add_vlans_input:
+ - vlan_id: "{{ vlan1 }}"
+ - vlan_id: "{{ vlan2 }}"
+ add_lag_interfaces:
+ - name: "{{ po1 }}"
+ - name: "{{ po2 }}"
+ delete_port_configurations:
+ - "no interface {{ lo1 }}"
+ - "no interface {{ lo2 }}"
+
+tests:
+
+ # Ethernet testcases started...
+ - name: test_case_01
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ description: ansible Ethernet4 descr
+ mtu: 6445
+ enabled: false
+ - name: test_case_02
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ description: ansible Ethernet4 descr
+ mtu: 6444
+ enabled: true
+ - name: "{{ interface3 }}"
+ description: ansible Ethernet12 descr
+ mtu: 6000
+ enabled: true
+ - name: "{{ interface2 }}"
+ description: ansible Ethernet8 descr
+ mtu: 5666
+ enabled: false
+ - name: "{{ interface4 }}"
+ description: ansible Ethernet16 descr
+ mtu: 5222
+ enabled: true
+ - name: test_case_03
+ description: Update interface parameters
+ state: deleted
+ input:
+ - name: "{{ interface1 }}"
+ description:
+ - name: "{{ interface3 }}"
+ mtu:
+ - name: "{{ interface2 }}"
+ enabled:
+ - name: "{{ interface4 }}"
+ - name: test_case_04
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ description: ansible Ethernet4 descr
+ mtu: 6444
+ enabled: true
+ - name: "{{ interface3 }}"
+ description: ansible Ethernet12 descr
+ - name: "{{ interface4 }}"
+ description: ansible eth56 descr
+# Loopback test cases started
+ - name: test_case_05
+ description: Loopback interface parameters
+ state: merged
+ input:
+ - name: "{{ lo1 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os
+ - name: "{{ lo2 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os
+ - name: test_case_06
+ description: Loopback delete interface parameters
+ state: deleted
+ input:
+ - name: "{{ lo1 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os
+ - name: test_case_07
+ description: Loopback delete interface parameters
+ state: deleted
+ input:
+ - name: "{{ lo1 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os
+ - name: "{{ lo2 }}" # Loopback: mtu, desc, enabled not configurable in sonic-os
+# Vlan test cases started
+ - name: test_case_08
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "Vlan{{ vlan1 }}" # Vlan: desc, enabled not configurable in sonic-os
+ mtu: 5000
+ - name: "Vlan{{ vlan2 }}"
+ mtu: 5001
+ - name: test_case_09
+ description: Update interface parameters
+ state: deleted
+ input:
+ - name: "Vlan{{ vlan1 }}"
+ mtu:
+ - name: "Vlan{{ vlan2 }}"
+ - name: test_case_10
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "Vlan{{ vlan1 }}"
+ mtu: 6676
+ - name: "Vlan{{ vlan2 }}"
+ mtu: 5113
+# Portchannel testcase started
+ - name: test_case_12
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ po1 }}"
+ mtu: 3434
+ enabled: true
+ - name: "{{ po2 }}"
+ description: ansible PortChannel51 descr
+ mtu: 5454
+ enabled: true
+ - name: test_case_13
+ description: Update interface parameters
+ state: deleted
+ input:
+ - name: "{{ po1 }}"
+ - name: "{{ po2 }}"
+ - name: test_case_14
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ po1 }}"
+ mtu: 3434
+ enabled: true
+ - name: "{{ po2 }}"
+ description: ansible PortChannel51 descr
+ mtu: 5454
+ enabled: true
+ - name: test_case_15
+ description: Update interface parameters
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/main.yml
new file mode 100644
index 00000000..51c65668
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/main.yml
@@ -0,0 +1,13 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..ce448251
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/preparation_tests.yaml
@@ -0,0 +1,44 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize default interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ default_interface_cli }}"
+ register: output
+ ignore_errors: yes
+- name: "delete loopback interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.delete_port_configurations }}"
+ register: output
+ ignore_errors: yes
+- name: delete VLANs
+ sonic_vlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: delete existing portchannels
+ sonic_lag_interfaces:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: create VLANs
+ sonic_vlans:
+ config: "{{ preparations_tests.add_vlans_input }}"
+ state: merged
+ ignore_errors: yes
+- name: create sonic_lag_interfaces "merged" state
+ sonic_lag_interfaces:
+ config: "{{ preparations_tests.add_lag_interfaces }}"
+ state: merged
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml
new file mode 100644
index 00000000..debf1a6c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_interfaces/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml
new file mode 100644
index 00000000..8117a89c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/defaults/main.yml
@@ -0,0 +1,101 @@
+---
+ansible_connection: httpapi
+module_name: l2_interfaces
+
+preparations_tests:
+ add_vlans_input:
+ - vlan_id: 500
+ - vlan_id: 501
+ - vlan_id: 502
+ - vlan_id: 503
+ - vlan_id: 504
+ - vlan_id: 505
+ - vlan_id: 400
+ - vlan_id: 401
+ - vlan_id: 402
+ delete_port_configurations:
+ - name: "{{ interface1 }}"
+ - name: "{{ interface2 }}"
+ - name: "{{ interface3 }}"
+ add_lag_interfaces:
+ - name: PortChannel100
+ - name: PortChannel101
+
+tests:
+ # merge test cases started
+ - name: test_case_01
+ description: Add access and trunk VLANs
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ access:
+ vlan: 400
+ - name: "{{ interface2 }}"
+ trunk:
+ allowed_vlans:
+ - vlan: 501
+ - vlan: 502
+ - name: "{{ interface4 }}"
+ access:
+ vlan: 401
+ trunk:
+ allowed_vlans:
+ - vlan: 504
+ - vlan: 505
+ - name: PortChannel100
+ access:
+ vlan: 400
+ - name: "{{ interface3 }}"
+ trunk:
+ allowed_vlans:
+ - vlan: 501
+ - vlan: 502
+ - name: test_case_02
+ description: Update trunk VLANs
+ state: merged
+ input:
+ - name: "{{ interface3 }}"
+ trunk:
+ allowed_vlans:
+ - vlan: 502
+ - vlan: 503
+ access:
+ vlan: 402
+ # delete test cases started
+ - name: test_case_03
+ description: Delete Access VLAN
+ state: deleted
+ input:
+ - name: "{{ interface1 }}"
+ access:
+ - name: test_case_04
+ description: Delete specific trunk VLANs
+ state: deleted
+ input:
+ - name: "{{ interface3 }}"
+ trunk:
+ allowed_vlans:
+ - vlan: 502
+ - name: test_case_05
+ description: Delete access VLANs from both associations
+ state: deleted
+ input:
+ - name: "{{ interface3 }}"
+ access:
+ vlan:
+ - name: test_case_06
+ description: Delete all trunk VLANs
+ state: deleted
+ input:
+ - name: "{{ interface3 }}"
+ trunk:
+ allowed_vlans:
+ - name: test_case_07
+ description: Delete all associations in specific interface
+ state: deleted
+ input:
+ - name: "{{ interface2 }}"
+ - name: test_case_08
+ description: Delete All associations in all interfaces
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/main.yml
new file mode 100644
index 00000000..be018fea
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/main.yml
@@ -0,0 +1,12 @@
+- debug: msg="sonic_l2_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..12561232
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/preparation_tests.yaml
@@ -0,0 +1,35 @@
+---
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize default interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ default_interface_cli }}"
+ register: output
+ ignore_errors: yes
+- name: create sonic_lag_interfaces "merged" state
+ sonic_lag_interfaces:
+ config: "{{ preparations_tests.add_lag_interfaces }}"
+ state: merged
+ ignore_errors: yes
+- name: Delete VLANs Inputs
+ sonic_vlans:
+ config: "{{ preparations_tests.add_vlans_input }}"
+ state: deleted
+ register: merge_vlans_output
+ ignore_errors: yes
+- name: create VLANs
+ sonic_vlans:
+ config: "{{ preparations_tests.add_vlans_input }}"
+ state: merged
+ register: merge_vlans_output
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/tasks_template.yaml
new file mode 100644
index 00000000..8c883e5d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l2_interfaces/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_l2_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_l2_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml
new file mode 100644
index 00000000..de632204
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/defaults/main.yml
@@ -0,0 +1,244 @@
+---
+ansible_connection: httpapi
+module_name: l3_interfaces
+
+preparations_tests:
+ delete_port_configurations:
+ - name: "{{ interface1 }}"
+ - name: "{{ interface2 }}"
+ - name: "{{ interface3 }}"
+ - name: "{{ interface4 }}"
+ init_loopback:
+ - "interface Loopback 100"
+ - "interface Loopback 101"
+ - "interface Portchannel 100"
+ - "interface Portchannel 101"
+ - "interface Vlan 100"
+ - "interface Vlan 101"
+ - "interface Vlan 102"
+ - "interface Vlan 501"
+
+tests:
+ # Vlan testcases started...
+ - name: test_case_01
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: Loopback100
+ ipv4:
+ addresses:
+ - address: 101.1.1.1/32
+ - address: 102.1.1.1/32
+ secondary: True
+ - name: vlan 100
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 150::1/32
+ - name: po 100
+ ipv4:
+ addresses:
+ - address: 180.1.1.1/16
+ - name: vlan 102
+ ipv4:
+ anycast_addresses:
+ - 1.1.1.1/16
+ - name: test_case_02
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: Loopback100
+ ipv4:
+ addresses:
+ - address: 99.1.1.1/32
+ - address: 103.1.1.1/32
+ secondary: True
+ ipv6:
+ enabled: false
+ addresses:
+ - address: 101::1/128
+ - name: vlan 100
+ ipv4:
+ addresses:
+ - address: 150.1.1.1/16
+ - name: po 100
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 180::1/16
+ - name: vlan 102
+ ipv4:
+ anycast_addresses:
+ - 11.12.13.14/12
+ - name: test_case_03
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: loopback 100
+ ipv4:
+ addresses:
+ - address: 102.1.1.1/32
+ secondary: True
+ ipv6:
+ addresses:
+ - address: 102::1/128
+ - name: vlan 100
+ ipv4:
+ addresses:
+ - address: 152.1.1.1/16
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 152::1/16
+ - name: po 100
+ ipv4:
+ addresses:
+ - address: 182.1.1.1/16
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 182::1/16
+ - name: test_case_04
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ ipv4:
+ addresses:
+ - address: 80.1.1.1/16
+ - name: "{{ interface2 }}"
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 90::1/16
+ - name: test_case_05
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ ipv4:
+ addresses:
+ - address: 81.1.1.1/16
+ - name: "{{ interface2 }}"
+ ipv6:
+ enabled: false
+ addresses:
+ - address: 90::1/16
+ - address: 91::1/16
+ - name: test_case_06
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: "{{ interface1 }}"
+ ipv4:
+ addresses:
+ - address: 83.1.1.1/16
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 83::1/16
+ - address: 84::1/16
+ - name: "{{ interface2 }}"
+ ipv4:
+ addresses:
+ - address: 91.1.1.1/16
+ ipv6:
+ addresses:
+ - address: 90::1/16
+ - address: 91::1/16
+ - address: 92::1/16
+ - address: 93::1/16
+ - name: test_case_07
+ description: Update interface parameters
+ state: deleted
+ input:
+ - name: "{{ interface1 }}"
+ ipv4:
+ addresses:
+ - address: 82.1.1.1/16
+ - name: "{{ interface2 }}"
+ ipv6:
+ enabled: false
+ addresses:
+ - address: 91::1/16
+ - name: vlan 102
+ ipv4:
+ anycast_addresses:
+ - 1.1.1.1/16
+ - name: test_case_08
+ description: Update interface parameters
+ state: deleted
+ input:
+ - name: "{{ interface1 }}"
+ - name: "{{ interface2 }}"
+ - name: Vlan100
+ - name: test_case_09
+ description: Logical interfaces config
+ state: merged
+ input:
+ - name: Vlan101
+ ipv4:
+ addresses:
+ - address: 104.1.1.1/16
+ ipv6:
+ addresses:
+ - address: 1041::1/16
+ - address: 1042::1/16
+ - name: Loopback101
+ ipv4:
+ addresses:
+ - address: 204.1.1.1/32
+ ipv6:
+ addresses:
+ - address: 2041::1/128
+ - address: 2042::1/128
+ - name: PortChannel101
+ ipv4:
+ addresses:
+ - address: 214.1.1.1/16
+ ipv6:
+ addresses:
+ - address: 3041::1/16
+ - address: 3042::1/16
+ - name: test_case_10
+ description: Naming tests
+ state: merged
+ input:
+ - name: vlan 501
+ ipv4:
+ addresses:
+ - address: 105.1.1.1/16
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 1051::1/16
+ - address: 1052::1/16
+ - name: lo101
+ ipv4:
+ addresses:
+ - address: 204.1.1.7/32
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 2041::1/128
+ - address: 2042::1/128
+ - name: portchannel 100 # po100 or portchannel100
+ ipv4:
+ addresses:
+ - address: 64.1.1.1/16
+ ipv6:
+ enabled: true
+ addresses:
+ - address: 3051::1/16
+ - address: 3052::1/16
+ - name: test_case_11
+ description: Naming tests
+ state: deleted
+ input:
+ - name: vlan 501
+ - name: lo101
+ - name: portchannel 100 # po100 or portchannel100
+ - name: test_case_12
+ description: Update interface parameters
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/main.yml
new file mode 100644
index 00000000..51c65668
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/main.yml
@@ -0,0 +1,13 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..66700d53
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/preparation_tests.yaml
@@ -0,0 +1,28 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old l3 interfaces
+ sonic_l3_interfaces:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize default interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ default_interface_cli }}"
+ register: output
+ ignore_errors: yes
+- name: "initialize init_loopback"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_loopback }}"
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/tasks_template.yaml
new file mode 100644
index 00000000..c2d30198
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_l3_interfaces/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_l3_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_l3_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml
new file mode 100644
index 00000000..3f77caba
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/defaults/main.yml
@@ -0,0 +1,89 @@
+---
+ansible_connection: httpapi
+module_name: lag_interfaces
+
+preparations_tests:
+ delete_port_configurations:
+ - name: "{{ interface1 }}"
+ - name: "{{ interface2 }}"
+ - name: "{{ interface3 }}"
+ - name: "{{ interface4 }}"
+
+tests:
+ # Ethernet testcases started...
+ - name: test_case_01
+ description: Update Ethernet interface parameters
+ state: merged
+ input:
+ - name: PortChannel43
+ mode: static
+ - name: PortChannel44
+ mode: lacp
+ - name: test_case_02
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: PortChannel40
+ mode: static
+ members:
+ interfaces:
+ - member: "{{ interface1 }}"
+ - name: PortChannel41
+ mode: lacp
+ members:
+ interfaces:
+ - member: "{{ interface3 }}"
+ - name: PortChannel43
+ mode: static
+ - name: test_case_03
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: PortChannel40
+ mode: static
+ members:
+ interfaces:
+ - member: "{{ interface1 }}"
+ - member: "{{ interface2 }}"
+ - name: PortChannel41
+ mode: lacp
+ members:
+ interfaces:
+ - member: "{{ interface3 }}"
+ - member: "{{ interface4 }}"
+ - name: PortChannel42
+ - name: test_case_04
+ description: Delete interface parameters
+ state: deleted
+ input:
+ - name: PortChannel40
+ members:
+ interfaces:
+ - member: "{{ interface1 }}"
+ - name: PortChannel41
+ members:
+ interfaces:
+ - name: PortChannel42
+ - name: test_case_05
+ description: Update interface parameters
+ state: merged
+ input:
+ - name: portchannel 40
+ members:
+ interfaces:
+ - member: "{{ interface1 }}"
+ - name: po41
+ members:
+ interfaces:
+ - member: "{{ interface3 }}"
+ - name: test_case_06
+ description: Create standalone portchannels
+ state: merged
+ input:
+ - name: portchannel42
+ - name: portchannel 12
+ - name: po10
+ - name: test_case_07
+ description: Update interface parameters
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/meta/main.yaml
new file mode 100644
index 00000000..78f79f8c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/main.yml
new file mode 100644
index 00000000..1338f5f8
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/main.yml
@@ -0,0 +1,13 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- name: Preparation test, default lag-interface configs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..55b64d5f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/preparation_tests.yaml
@@ -0,0 +1,22 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize default interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ default_interface_cli }}"
+ register: output
+ ignore_errors: yes
+- name: Delete all port channels
+ sonic_lag_interfaces:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/tasks_template.yaml
new file mode 100644
index 00000000..92767743
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_lag_interfaces/tasks/tasks_template.yaml
@@ -0,0 +1,22 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_lag_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_lag_interfaces:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml
new file mode 100644
index 00000000..a2df2d36
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/defaults/main.yml
@@ -0,0 +1,107 @@
+---
+ansible_connection: httpapi
+module_name: mclag
+preparations_tests:
+ add_vlans_input:
+ - vlan_id: 4
+ - vlan_id: 5
+ - vlan_id: 6
+ - vlan_id: 2
+ add_lag_interfaces:
+ - name: Po10
+ - name: Po11
+ - name: Po12
+ - name: Po13
+
+merged_tests:
+ - name: test_case_01
+ description: MCLAG properties
+ state: merged
+ input:
+ domain_id: 1
+ source_address: 3.3.3.3
+ peer_address: 1.1.1.1
+ peer_link: "{{ interface1 }}"
+ keepalive: 3
+ session_timeout: 300
+ system_mac: 00:00:00:01:01:01
+
+ - name: test_case_02
+ description: Update created MCLAG properties
+ state: merged
+ input:
+ domain_id: 1
+ source_address: 3.3.3.4
+ peer_address: 1.1.1.2
+ peer_link: "{{ interface2 }}"
+ keepalive: 2
+ session_timeout: 350
+ system_mac: 00:00:00:11:11:11
+
+ - name: test_case_03
+ description: Update MCLAG properties - associate vlans and portchannels
+ state: merged
+ input:
+ domain_id: 1
+ unique_ip:
+ vlans:
+ - vlan: vlan4
+ - vlan: vlan5
+ members:
+ portchannels:
+ - lag: Po10
+ - lag: Po11
+
+delete_all:
+ - name: del_all_test_case_01
+ description: Delete MCLAG properties
+ state: deleted
+
+updated_tests:
+ - name: test_case_05
+ description: Create new MCLAG with all properties including vlans and portchannels
+ state: merged
+ input:
+ domain_id: 2
+ source_address: 3.3.3.5
+ peer_address: 1.1.1.3
+ peer_link: "{{ interface3 }}"
+ keepalive: 3
+ session_timeout: 300
+ system_mac: 00:00:00:01:01:01
+ unique_ip:
+ vlans:
+ - vlan: vlan2
+ - vlan: vlan6
+ members:
+ portchannels:
+ - lag: Po13
+ - lag: Po12
+
+ - name: del_test_case_01
+ description: Delete MCLAG properties
+ state: deleted
+ input:
+ domain_id: 2
+ source_address: 3.3.3.3
+ peer_address: 1.1.1.1
+ peer_link: "{{ interface3 }}"
+ keepalive: 3
+ session_timeout: 300
+ system_mac: 00:00:00:01:01:01
+ unique_ip:
+ vlans:
+ - vlan: vlan2
+ members:
+ portchannels:
+ - lag: Po13
+
+ - name: del_test_case_02
+ description: Delete MCLAG associated vlans and portchannels
+ state: deleted
+ input:
+ domain_id: 2
+ unique_ip:
+ vlans:
+ members:
+ portchannels:
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/meta/main.yaml
new file mode 100644
index 00000000..d0ceaf6f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml
new file mode 100644
index 00000000..071ef949
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/main.yml
@@ -0,0 +1,22 @@
+- debug: msg="sonic_mclag Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ merged_tests }}"
+
+- name: "delete_all {{ module_name }} stated ..."
+ include_tasks: tasks_template_del.yaml
+ loop: "{{ delete_all }}"
+ when: delete_all is defined
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ updated_tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..843a1e83
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/preparation_tests.yaml
@@ -0,0 +1,21 @@
+---
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: create sonic_lag_interfaces "merged" state
+ sonic_lag_interfaces:
+ config: "{{ preparations_tests.add_lag_interfaces }}"
+ state: merged
+ ignore_errors: yes
+- name: create VLANs Inputs
+ sonic_vlans:
+ config: "{{ preparations_tests.add_vlans_input }}"
+ state: merged
+ ignore_errors: yes
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template.yaml
new file mode 100644
index 00000000..add970c4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_mclag:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_mclag:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template_del.yaml
new file mode 100644
index 00000000..9001d50a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_mclag/tasks/tasks_template_del.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_mclag:
+ state: "{{ item.state }}"
+ config:
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_mclag:
+ state: "{{ item.state }}"
+ config:
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml
new file mode 100644
index 00000000..860297d6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/defaults/main.yml
@@ -0,0 +1,209 @@
+---
+ansible_connection: httpapi
+module_name: ntp
+
+po1: Portchannel 100
+vlan1: Vlan 100
+lo1: Loopback 100
+
+mgmt_vrf: mgmt
+
+ntp_ip_server_1: 10.11.0.1
+ntp_ip_server_2: 10.11.0.2
+ntp_ip_server_3: 10.11.0.3
+ntp_host_server: pool.ntp.org
+
+preparations_tests:
+ delete_interfaces:
+ - "no interface {{ po1 }}"
+ - "no interface {{ vlan1 }}"
+ - "no interface {{ lo1 }}"
+ init_interfaces:
+ - "interface {{ po1 }}"
+ - "interface {{ vlan1 }}"
+ - "interface {{ lo1 }}"
+ delete_mgmt_vrf:
+ - "no ip vrf mgmt"
+ create_mgmt_vrf:
+ - "ip vrf mgmt"
+
+tests:
+ - name: test_case_01
+ description: Configure a NTP source interface
+ state: merged
+ input:
+ source_interfaces:
+ - "{{ interface1 }}"
+
+ - name: test_case_02
+ description: Create a NTP server
+ state: merged
+ input:
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+
+ - name: test_case_03
+ description: Configure NTP source interfaces
+ state: merged
+ input:
+ source_interfaces:
+ - "{{ interface1 }}"
+ - "{{ po1 }}"
+ - "{{ vlan1 }}"
+ - "{{ lo1 }}"
+
+ - name: test_case_04
+ description: Create NTP servers
+ state: merged
+ input:
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+ minpoll: 6
+ maxpoll: 9
+ - address: "{{ ntp_ip_server_2 }}"
+ minpoll: 5
+ maxpoll: 8
+ - address: "{{ ntp_host_server }}"
+ minpoll: 8
+ maxpoll: 10
+
+ - name: test_case_05
+ description: Configure NTP source interfaces and servers
+ state: merged
+ input:
+ source_interfaces:
+ - "{{ interface2 }}"
+ - "{{ po1 }}"
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+ minpoll: 6
+ maxpoll: 9
+ - address: "{{ ntp_ip_server_3 }}"
+ minpoll: 7
+ maxpoll: 10
+
+ - name: test_case_06
+ description: Delete a NTP source interface
+ state: deleted
+ input:
+ source_interfaces:
+ - "{{ interface1 }}"
+
+ - name: test_case_07
+ description: Delete a NTP server
+ state: deleted
+ input:
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+
+ - name: test_case_08
+ description: Delete several NTP source interfaces
+ state: deleted
+ input:
+ source_interfaces:
+ - "{{ interface2 }}"
+ - "{{ po1 }}"
+
+ - name: test_case_09
+ description: Delete several NTP servers
+ state: deleted
+ input:
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+ - address: "{{ ntp_ip_server_3 }}"
+
+ - name: test_case_10
+ description: Delete NTP source interfaces and servers
+ state: deleted
+ input:
+ source_interfaces:
+ - "{{ interface2 }}"
+ - "{{ vlan1 }}"
+ - "{{ lo1 }}"
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+ - address: "{{ ntp_host_server }}"
+
+ - name: test_case_11
+ description: Configure NTP VRF
+ state: merged
+ input:
+ vrf: "{{ mgmt_vrf }}"
+
+ - name: test_case_12
+ description: Delete NTP VRF
+ state: deleted
+ input:
+ vrf: "{{ mgmt_vrf }}"
+
+ - name: test_case_13
+ description: Enable NTP authentication
+ state: merged
+ input:
+ enable_ntp_auth: true
+
+ - name: test_case_14
+ description: Create NTP authentication keys
+ state: merged
+ input:
+ ntp_keys:
+ - key_id: 2
+ key_type: NTP_AUTH_SHA1
+ key_value: U2FsdGVkX197E9oiCGzwZlZxZpF5f/ZI8v+SGJdQvmA=
+ encrypted: true
+ - key_id: 6
+ key_type: NTP_AUTH_MD5
+ key_value: U2FsdGVkX1/wWVxmcp59mJQO6uzhFEHIxScdCbIqJh4=
+ encrypted: true
+
+ - name: test_case_15
+ description: Configure NTP trusted keys
+ state: merged
+ input:
+ trusted_keys:
+ - 2
+ - 6
+
+ - name: test_case_16
+ description: Create NTP servers with key
+ state: merged
+ input:
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+ key_id: 2
+ minpoll: 6
+ maxpoll: 9
+
+ - name: test_case_17
+ description: Delete NTP trusted keys
+ state: deleted
+ input:
+ trusted_keys:
+ - 2
+ - 6
+
+ - name: test_case_18
+ description: Delete NTP server
+ state: deleted
+ input:
+ servers:
+ - address: "{{ ntp_ip_server_1 }}"
+
+ - name: test_case_19
+ description: Delete NTP authentication keys
+ state: deleted
+ input:
+ ntp_keys:
+ - key_id: 2
+ - key_id: 6
+
+ - name: test_case_20
+ description: Delete NTP authentication
+ state: deleted
+ input:
+ enable_ntp_auth: true
+
+ - name: test_case_21
+ description: Delete all NTP configurations
+ state: deleted
+ input: {}
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..bb31455e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/cleanup_tests.yaml
@@ -0,0 +1,15 @@
+- name: Delete interfaces
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.delete_interfaces }}"
+ register: output
+ ignore_errors: yes
+
+- name: Delete MGMT VRF
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.delete_mgmt_vrf }}"
+ register: output
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/main.yml
new file mode 100644
index 00000000..ba1574e3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/main.yml
@@ -0,0 +1,16 @@
+- debug: msg="sonic_ntp Test started ..."
+
+- name: Preparations tests
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "Cleanup test {{ module_name }} started"
+ include_tasks: cleanup_tests.yaml
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..f74a2019
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/preparation_tests.yaml
@@ -0,0 +1,21 @@
+- name: Delete existing NTP configurations
+ sonic_ntp:
+ config: {}
+ state: deleted
+ ignore_errors: yes
+
+- name: Initialize interfaces
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_interfaces }}"
+ register: output
+ ignore_errors: yes
+
+- name: Create MGMT VRF
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.create_mgmt_vrf }}"
+ register: output
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/tasks_template.yaml
new file mode 100644
index 00000000..c580db84
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_ntp/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_ntp:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_ntp:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml
new file mode 100644
index 00000000..402088be
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/defaults/main.yml
@@ -0,0 +1,57 @@
+---
+ansible_connection: httpapi
+module_name: port_breakout
+
+preparations_tests:
+ delete_port_breakouts:
+ - "no interface breakout port 1/97"
+ - "no interface breakout port 1/98"
+ - "no interface breakout port 1/99"
+ - "no interface breakout port 1/100"
+ - "no interface breakout port 1/101"
+ - "no interface breakout port 1/102"
+
+tests_cli:
+ - name: cli_test_case_01
+ description: Configure breakout mode for ports
+ state: merged
+ input:
+ - name: 1/97
+ mode: 4x25G
+ - name: 1/98
+ mode: 1x40G
+
+tests:
+ - name: test_case_01
+ description: Configure breakout mode for ports
+ state: merged
+ input:
+ - name: 1/97
+ mode: 4x25G
+ - name: 1/98
+ mode: 1x40G
+ - name: 1/99
+ mode: 4x25G
+ - name: 1/100
+ mode: 4x10G
+ - name: 1/101
+ mode: 1x40G
+ - name: 1/102
+ mode: 4x25G
+ - name: test_case_02
+ description: Update breakout mode for ports
+ state: merged
+ input:
+ - name: 1/97
+ mode: 1x40G
+ - name: 1/98
+ mode: 4x10G
+ - name: test_case_03
+ description: delete specific port breakout mode
+ state: deleted
+ input:
+ - name: 1/98
+ - name: test_case_04
+ description: deleting all the port breakout modes
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/meta/main.yaml
new file mode 100644
index 00000000..0b356217
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..fc7e76dd
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/cleanup_tests.yaml
@@ -0,0 +1,6 @@
+- name: Deletes old bgp
+ sonic_port_breakout:
+ config: []
+ state: deleted
+ ignore_errors: yes
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml
new file mode 100644
index 00000000..2dea6531
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/main.yml
@@ -0,0 +1,31 @@
+- debug: msg="sonic_port_breakout Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} CLI validation started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests_cli }}"
+
+- name: "Test CLI validation started ..."
+ include_role:
+ name: common
+ tasks_from: cli_tasks_template.yaml
+ loop: "{{ tests_cli }}"
+
+# - name: Preparations test
+# include_tasks: preparation_tests.yaml
+
+# - name: "Test {{ module_name }} started ..."
+# include_tasks: tasks_template.yaml
+# loop: "{{ tests }}"
+
+# - name: Cleanup tests
+# include_tasks: cleanup_tests.yaml
+
+# - name: Display all variables/facts known for a host
+# debug:
+# var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..14ceb14c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old port breakouts
+ sonic_port_breakout:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml
new file mode 100644
index 00000000..04ab1b45
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_port_breakout:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_port_breakout:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..c08c5950
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_port_breakout/templates/cli_test_case_01.cfg
@@ -0,0 +1,2 @@
+interface breakout port 1/97 mode 4x25G
+interface breakout port 1/98 mode 1x40G
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml
new file mode 100644
index 00000000..be519991
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/defaults/main.yml
@@ -0,0 +1,122 @@
+---
+ansible_connection: httpapi
+module_name: sonic_prefix_lists
+
+tests:
+ - name: test_case_01
+ description: Add initial prefix list configuration
+ state: merged
+ input:
+ - afi: ipv4
+ name: pf1
+ prefixes:
+ - action: deny
+ sequence: 10
+ prefix: 1.2.3.0/24
+ ge: 25
+ le: 27
+ - afi: ipv4
+ name: pf2
+ prefixes:
+ - action: deny
+ prefix: 10.20.30.0/24
+ sequence: 20
+ ge: 26
+ - action: permit
+ prefix: 10.20.30.128/25
+ sequence: 50
+ ge: 27
+ le: 29
+ - afi: ipv4
+ name: pf3
+ prefixes:
+ - action: deny
+ prefix: 1.2.3.128/25
+ sequence: 30
+ le: 27
+ - afi: ipv6
+ name: pf4
+ prefixes:
+ - action: permit
+ sequence: 40
+ prefix: 50:60::/64
+
+ - name: test_case_02
+ description: Add another prefix set to the existing configuration
+ state: merged
+ input:
+ - afi: ipv4
+ name: pf5
+ prefixes:
+ - action: permit
+ prefix: 15.25.35.0/24
+ sequence: 15
+
+ - name: test_case_03
+ description: Add a prefix to an existing prefix set
+ state: merged
+ input:
+ - afi: ipv4
+ name: pf3
+ prefixes:
+ - action: permit
+ prefix: 1.2.3.192/26
+ sequence: 40
+ ge: 28
+ le: 30
+
+ - name: test_case_04
+ description: Modify "action" attributes for an existing prefix
+ state: merged
+ input:
+ - afi: ipv4
+ name: pf2
+ prefixes:
+ - action: permit
+ prefix: 10.20.30.0/24
+ sequence: 20
+ ge: 26
+ - action: deny
+ prefix: 10.20.30.128/25
+ sequence: 50
+ ge: 27
+ le: 29
+
+ - name: test_case_05
+ description: Delete prefixes from existing prefix sets
+ state: deleted
+ input:
+ - afi: ipv4
+ name: pf2
+ prefixes:
+ - action: deny
+ prefix: 10.20.30.0/24
+ sequence: 20
+ ge: 26
+ - afi: ipv4
+ name: pf3
+ prefixes:
+ - action: permit
+ prefix: 1.2.3.192/26
+ sequence: 40
+ ge: 28
+ le: 30
+ - afi: ipv4
+ name: pf5
+ prefixes:
+ - action: permit
+ prefix: 15.25.35.0/24
+ sequence: 15
+
+ - name: test_case_06
+ description: Delete prefix sets from the existing configuration
+ state: deleted
+ input:
+ - name: pf1
+ - name: pf4
+ afi: ipv6
+
+ - name: test_case_07
+ description: Delete all prefix set configuration
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/meta/main.yaml
new file mode 100644
index 00000000..0b356217
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..1a25238c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/cleanup_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes all prefix lists
+ sonic_prefix_lists:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/main.yml
new file mode 100644
index 00000000..6414ec4e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/main.yml
@@ -0,0 +1,15 @@
+- debug: msg="sonic_prefix_lists Test started ..."
+
+- name: Preparation test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "Cleanup {{ module_name }} started ..."
+ include_tasks: cleanup_tests.yaml
+
+- name: Display Test Report Output
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..6db3bac9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/preparation_tests.yaml
@@ -0,0 +1,7 @@
+- name: "Delete old prefix lists"
+ vars:
+ ansible_connection: httpapi
+ sonic_prefix_lists:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/tasks_template.yaml
new file mode 100644
index 00000000..4e5b0f4e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_prefix_lists/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_prefix_lists:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_prefix_lists:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml
new file mode 100644
index 00000000..6a79dc88
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/defaults/main.yml
@@ -0,0 +1,114 @@
+---
+ansible_connection: httpapi
+module_name: radius_server
+
+preparations_tests:
+ delete_radius_server:
+ - name: delete_all
+ description: Configure radius server
+ state: deleted
+ input:
+tests:
+ - name: test_case_01
+ description: Add new host configuration
+ state: merged
+ input:
+ auth_type: chap
+ timeout: 12
+ nas_ip: 10.11.12.13
+ retransmit: 5
+ statistics: true
+ servers:
+ host:
+ - name: my_host1
+ auth_type: chap
+ priority: 3
+ vrf: mgmt
+ timeout: 12
+ port: 55
+ source_interface: "{{ interface1 }}"
+ retransmit: 7
+ - name: my_host2
+ auth_type: pap
+ priority: 4
+ vrf: mgmt
+ timeout: 15
+ port: 56
+ source_interface: "{{ interface2 }}"
+ retransmit: 8
+ - name: my_host3
+ auth_type: mschapv2
+ priority: 6
+ vrf: mgmt
+ timeout: 20
+ port: 57
+ source_interface: "{{ interface3 }}"
+ retransmit: 9
+ - name: test_case_02
+ description: Update specific params of radius server
+ state: merged
+ input:
+ auth_type: mschapv2
+ timeout: 24
+ servers:
+ host:
+ - name: my_host
+ auth_type: mschapv2
+ port: 45
+ timeout: 9
+ vrf: mgmt
+ - name: test_case_03
+ description: Delete specific params of radius server
+ state: deleted
+ input:
+ key: login
+ timeout: 24
+ servers:
+ host:
+ - name: my_host
+ - name: test_case_04
+ description: Delete all hosts configurations
+ state: deleted
+ input:
+ servers:
+ host:
+ - name: test_case_05
+ description: Add new host configuration
+ state: merged
+ input:
+ auth_type: chap
+ timeout: 12
+ nas_ip: 10.11.12.13
+ retransmit: 5
+ statistics: true
+ servers:
+ host:
+ - name: my_host1
+ auth_type: chap
+ priority: 3
+ vrf: mgmt
+ timeout: 12
+ port: 55
+ source_interface: "{{ interface1 }}"
+ retransmit: 7
+ - name: my_host2
+ auth_type: pap
+ priority: 4
+ vrf: mgmt
+ timeout: 15
+ port: 56
+ source_interface: "{{ interface2 }}"
+ retransmit: 8
+ - name: my_host3
+ auth_type: mschapv2
+ priority: 6
+ vrf: mgmt
+ timeout: 20
+ port: 57
+ source_interface: "{{ interface3 }}"
+ retransmit: 9
+
+test_delete_all:
+ - name: test_case_06
+ description: delete all the configurations of radius server
+ state: deleted
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/meta/main.yaml
new file mode 100644
index 00000000..0b356217
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..bbe93069
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/cleanup_tests.yaml
@@ -0,0 +1,6 @@
+- name: Deletes old bgp
+ sonic_radius_server:
+ config: {}
+ state: deleted
+ ignore_errors: yes
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/main.yml
new file mode 100644
index 00000000..18bebfba
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/main.yml
@@ -0,0 +1,16 @@
+- debug: msg="sonic_radius_server Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "test_delete_all {{ module_name }} stated ..."
+ include_tasks: tasks_template_del.yaml
+ loop: "{{ test_delete_all }}"
+ when: test_delete_all is defined
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..f1a64aa7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old radius server configurations
+ sonic_radius_server:
+ config: {}
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template.yaml
new file mode 100644
index 00000000..66281d52
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_radius_server:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_radius_server:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template_del.yaml
new file mode 100644
index 00000000..1cfb7c04
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/tasks/tasks_template_del.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_radius_server:
+ state: "{{ item.state }}"
+ config:
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_radius_server:
+ state: "{{ item.state }}"
+ config:
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_radius_server/templates/cli_test_case_01.cfg
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml
new file mode 100644
index 00000000..64d2485f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/defaults/main.yml
@@ -0,0 +1,172 @@
+---
+ansible_connection: httpapi
+module_name: static_routes
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+preparations_tests:
+ init_vrf:
+ - 'ip vrf {{vrf_1}}'
+ - 'ip vrf {{vrf_2}}'
+tests:
+ - name: test_case_01
+ description: Add new static routes configuration
+ state: merged
+ input:
+ - vrf_name: 'default'
+ static_list:
+ - prefix: '2.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface1 }}'
+ metric: 1
+ tag: 2
+ track: 3
+ - vrf_name: '{{vrf_1}}'
+ static_list:
+ - prefix: '3.0.0.0/8'
+ next_hops:
+ - index:
+ blackhole: True
+ interface: '{{ interface1 }}'
+ nexthop_vrf: '{{vrf_2}}'
+ next_hop: '5.0.0.0'
+ metric: 1
+ tag: 2
+ track: 3
+ - index:
+ interface: '{{ interface1 }}'
+ nexthop_vrf: '{{vrf_2}}'
+ next_hop: '4.0.0.0'
+ metric: 4
+ tag: 5
+ track: 6
+ - vrf_name: '{{vrf_2}}'
+ static_list:
+ - prefix: '1.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface2 }}'
+ nexthop_vrf: '{{vrf_1}}'
+ - index:
+ interface: '{{ interface3 }}'
+ next_hop: '2.0.0.0'
+ - prefix: '7.0.0.0/8'
+ next_hops:
+ - index:
+ nexthop_vrf: '{{vrf_1}}'
+ next_hop: '3.0.0.0'
+ - name: test_case_02
+ description: Modify static routes configuration
+ state: merged
+ input:
+ - vrf_name: 'default'
+ static_list:
+ - prefix: '2.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface1 }}'
+ metric: 10
+ tag: 20
+ track: 30
+ - index:
+ next_hop: '3.0.0.0'
+ metric: 8
+ tag: 10
+ track: 12
+ - vrf_name: '{{vrf_1}}'
+ static_list:
+ - prefix: '3.0.0.0/8'
+ next_hops:
+ - index:
+ blackhole: True
+ interface: '{{ interface1 }}'
+ nexthop_vrf: '{{vrf_2}}'
+ next_hop: '5.0.0.0'
+ metric: 11
+ tag: 22
+ track: 33
+ - vrf_name: '{{vrf_2}}'
+ static_list:
+ - prefix: '1.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface2 }}'
+ nexthop_vrf: '{{vrf_1}}'
+ metric: 6
+ tag: 7
+ track: 8
+ - prefix: '7.0.0.0/8'
+ next_hops:
+ - index:
+ nexthop_vrf: '{{vrf_1}}'
+ next_hop: '3.0.0.0'
+ metric: 9
+ tag: 10
+ track: 11
+ - name: test_case_03
+ description: Delete static routes metric, tag, and track configuration
+ state: deleted
+ input:
+ - vrf_name: 'default'
+ static_list:
+ - prefix: '2.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface1 }}'
+ metric: 10
+ tag: 20
+ track: 30
+ - index:
+ next_hop: '3.0.0.0'
+ metric: 8
+ tag: 10
+ track: 12
+ - vrf_name: '{{vrf_1}}'
+ static_list:
+ - prefix: '3.0.0.0/8'
+ next_hops:
+ - index:
+ blackhole: True
+ interface: '{{ interface1 }}'
+ nexthop_vrf: '{{vrf_2}}'
+ next_hop: '5.0.0.0'
+ metric: 11
+ tag: 22
+ track: 33
+ - index:
+ interface: '{{ interface1 }}'
+ nexthop_vrf: '{{vrf_2}}'
+ next_hop: '4.0.0.0'
+ - name: test_case_04
+ description: Delete static route index configuration
+ state: deleted
+ input:
+ - vrf_name: 'default'
+ static_list:
+ - prefix: '2.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface1 }}'
+ - index:
+ next_hop: '3.0.0.0'
+ - vrf_name: '{{vrf_2}}'
+ static_list:
+ - prefix: '1.0.0.0/8'
+ next_hops:
+ - index:
+ interface: '{{ interface3 }}'
+ next_hop: '2.0.0.0'
+ - name: test_case_05
+ description: Delete static route prefix configuration
+ state: deleted
+ input:
+ - vrf_name: '{{vrf_1}}'
+ - vrf_name: '{{vrf_2}}'
+ static_list:
+ - prefix: '7.0.0.0/8'
+ - name: test_case_06
+ description: Delete all static routes configuration
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/meta/main.yaml
new file mode 100644
index 00000000..0b356217
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml
new file mode 100644
index 00000000..c87965de
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/main.yml
@@ -0,0 +1,11 @@
+- debug: msg="sonic_static_routes Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..d504873a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/preparation_tests.yaml
@@ -0,0 +1,11 @@
+- name: "initialize VRFs"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_vrf }}"
+
+- name: Deletes old static routes configurations
+ sonic_static_routes:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template.yaml
new file mode 100644
index 00000000..9695897c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_static_routes:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_static_routes:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml
new file mode 100644
index 00000000..bbf2331d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_static_routes/tasks/tasks_template_del.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_static_routes:
+ state: "{{ item.state }}"
+ config:
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_static_routes:
+ state: "{{ item.state }}"
+ config:
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml
new file mode 100644
index 00000000..c5146db9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/defaults/main.yml
@@ -0,0 +1,47 @@
+---
+ansible_connection: httpapi
+module_name: system
+tests:
+ - name: test_case_01
+ description: System properties
+ state: merged
+ input:
+ hostname: SONIC-test
+ interface_naming: standard
+ anycast_address:
+ ipv4: false
+ ipv6: false
+
+ - name: test_case_02
+ description: Update created System properties
+ state: merged
+ input:
+ hostname: SONIC-new
+
+ - name: test_case_03
+ description: Update System properties - associate mac address
+ state: merged
+ input:
+ anycast_address:
+ mac_address: 00:09:5B:EC:EE:F2
+
+ - name: del_test_case_01
+ description: Delete System properties
+ state: deleted
+ input:
+ hostname: SONIC-new
+ interface_naming: standard
+ anycast_address:
+ ipv4: false
+
+ - name: del_test_case_02
+ description: Delete System associated anycast mac address
+ state: deleted
+ input:
+ anycast_address:
+ mac_address: 00:09:5B:EC:EE:F2
+
+test_delete_all:
+ - name: del_all_test_case_01
+ description: Delete System properties
+ state: deleted
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/meta/main.yaml
new file mode 100644
index 00000000..d0ceaf6f
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..816050f9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/cleanup_tests.yaml
@@ -0,0 +1,13 @@
+#standard_command: interface-naming standard
+#native_command: no interface-naming standard
+#command: "{{ statandard_command if std_name in interface_mode else native_command }}"
+#command: "{{ 'interface-naming standard' if std_name in interface_mode else 'no interface-naming standard' }}"
+- name: Change the interface mode to old
+ vars:
+ ansible_connection: network_cli
+ command: interface-naming standard
+ no_command: no interface-naming standard
+ when: interface_mode == native_mode
+ sonic_config:
+ commands: "{{ command if std_name in interface_mode else no_command }}"
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/main.yml
new file mode 100644
index 00000000..7f6652f1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/main.yml
@@ -0,0 +1,20 @@
+- debug: msg="sonic_system Test started ..."
+
+- name: Preparation Tests
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "test_delete_all {{ module_name }} stated ..."
+ include_tasks: tasks_template_del.yaml
+ loop: "{{ test_delete_all }}"
+ when: test_delete_all is defined
+
+- name: "Cleanup test {{ module_name }} started"
+ include_tasks: cleanup_tests.yaml
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..eaf12cdf
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes system configurations
+ sonic_system:
+ config: {}
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template.yaml
new file mode 100644
index 00000000..fc1e58b3
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_system:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_system:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template_del.yaml
new file mode 100644
index 00000000..1ac0e555
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_system/tasks/tasks_template_del.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_system:
+ state: "{{ item.state }}"
+ config:
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_system:
+ state: "{{ item.state }}"
+ config:
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml
new file mode 100644
index 00000000..0f9b3e3c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/defaults/main.yml
@@ -0,0 +1,91 @@
+---
+ansible_connection: httpapi
+module_name: tacacs_server
+
+preparations_tests:
+ delete_tacacs_server:
+ - name: delete_all
+ description: Configure tacacs server
+ state: deleted
+ input:
+tests:
+ - name: test_case_01
+ description: Add new host configuration
+ state: merged
+ input:
+ auth_type: chap
+ source_interface: "{{ interface1 }}"
+ timeout: 12
+ servers:
+ host:
+ - name: my_host
+ auth_type: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ - name: my_host1
+ auth_type: login
+ port: 60
+ timeout: 14
+ priority: 4
+ - name: my_host2
+ auth_type: login
+ port: 60
+ timeout: 14
+ priority: 4
+ - name: test_case_02
+ description: Update specific params of tacacs server
+ state: merged
+ input:
+ auth_type: login
+ source_interface: "{{ interface2 }}"
+ timeout: 24
+ servers:
+ host:
+ - name: my_host
+ auth_type: mschap
+ port: 45
+ timeout: 9
+ priority: 5
+ vrf: default
+ - name: test_case_03
+ description: Delete specific params of tacacs server
+ state: deleted
+ input:
+ key: login
+ timeout: 24
+ servers:
+ host:
+ - name: my_host
+ - name: test_case_04
+ description: Delete all hosts configurations
+ state: deleted
+ input:
+ servers:
+ host:
+ - name: test_case_05
+ description: merge parameter of tacacs servers
+ state: merged
+ input:
+ servers:
+ host:
+ - name: my_host
+ auth_type: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ - name: my_host1
+ auth_type: login
+ port: 60
+ timeout: 14
+ priority: 4
+ - name: my_host2
+ auth_type: login
+ port: 60
+ timeout: 14
+ priority: 4
+
+test_delete_all:
+ - name: test_case_06
+ description: delete all the configurations of tacacs server
+ state: deleted
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/meta/main.yaml
new file mode 100644
index 00000000..0b356217
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..fd49cbd2
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/cleanup_tests.yaml
@@ -0,0 +1,6 @@
+- name: Deletes old bgp
+ sonic_tacacs_server:
+ config: {}
+ state: deleted
+ ignore_errors: yes
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/main.yml
new file mode 100644
index 00000000..4020d077
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/main.yml
@@ -0,0 +1,16 @@
+- debug: msg="sonic_tacacs_server Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: "test_delete_all {{ module_name }} stated ..."
+ include_tasks: tasks_template_del.yaml
+ loop: "{{ test_delete_all }}"
+ when: test_delete_all is defined
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..7c009d68
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes old tacacs server configurations
+ sonic_tacacs_server:
+ config: {}
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template.yaml
new file mode 100644
index 00000000..a73dd9ba
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_tacacs_server:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_tacacs_server:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template_del.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template_del.yaml
new file mode 100644
index 00000000..cf15eee9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/tasks/tasks_template_del.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_tacacs_server:
+ state: "{{ item.state }}"
+ config:
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_tacacs_server:
+ state: "{{ item.state }}"
+ config:
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_tacacs_server/templates/cli_test_case_01.cfg
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml
new file mode 100644
index 00000000..342cb2d4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/defaults/main.yml
@@ -0,0 +1,96 @@
+---
+ansible_connection: httpapi
+module_name: users
+
+tests_cli:
+ - name: cli_test_case_01
+ description: Configure users
+ state: merged
+ expected_cli:
+ - username sysadmin password
+ - username operator1 password
+ input:
+ - name: sysadmin
+ role: admin
+ password: admin
+ update_password: always
+ - name: operator1
+ role: operator
+ password: admin
+ update_password: always
+
+tests_single_run:
+ - name: test_case_sr_01
+ description: Configure users
+ state: merged
+ task_condition_type: action
+ input:
+ - name: user1
+ role: admin
+ password: admin
+ update_password: always
+ - name: user2
+ role: operator
+ password: admin
+ update_password: always
+ - name: user2
+ role: operator
+ password: admin
+ update_password: on_create
+ - name: test_case_sr_02
+ description: Validate users creation
+ state: merged
+ task_condition_type: idempotent
+ input:
+ - name: user1
+ role: admin
+ password: admin
+ update_password: on_create
+ - name: user2
+ role: operator
+ password: admin
+ update_password: on_create
+ - name: user2
+ role: operator
+ password: admin
+ update_password: on_create
+
+
+tests:
+ - name: test_case_01
+ description: Configure users
+ state: merged
+ input:
+ - name: user1
+ role: admin
+ password: admin
+ update_password: on_create
+ - name: user2
+ role: operator
+ password: admin
+ update_password: on_create
+ - name: user3
+ role: admin
+ password: admin
+ update_password: on_create
+ - name: test_case_02
+ description: Configure users role
+ state: merged
+ input:
+ - name: user1
+ role: operator
+ password: admin
+ update_password: on_create
+ - name: user2
+ role: admin
+ password: admin
+ update_password: on_create
+ - name: test_case_03
+ description: Delete user
+ state: deleted
+ input:
+ - name: user1
+ - name: test_case_04
+ description: Update users role
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/meta/main.yaml
new file mode 100644
index 00000000..0b356217
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common }
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml
new file mode 100644
index 00000000..1e9bfc24
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/cli_tests.yaml
@@ -0,0 +1,14 @@
+- name: Test sonic multiple command with wait_for
+ vars:
+ ansible_connection: network_cli
+ sonic_command:
+ commands:
+ - show running-configuration
+ register: cli_contains_output
+
+- set_fact:
+ cli_contains_condition: "{{ 'username operator1 password' in cli_contains_output.stdout.0 }}"
+
+- import_role:
+ name: common
+ tasks_from: cli.contains.test.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml
new file mode 100644
index 00000000..987174b9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/main.yml
@@ -0,0 +1,39 @@
+- debug: msg="sonic_users Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+# - name: CLI test test
+# include_tasks: cli_tests.yaml
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} Single run validation started ..."
+ include_tasks: single_run_template.yaml
+ loop: "{{ tests_single_run }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+# - name: "Test CLI validation started ..."
+# include_role:
+# name: common
+# tasks_from: cli_tasks_template.yaml
+# loop: "{{ tests_cli }}"
+
+# - name: Preparations test
+# include_tasks: preparation_tests.yaml
+
+
+
+# - name: Preparations test
+# include_tasks: preparation_tests.yaml
+
+# - name: Display all variables/facts known for a host
+# debug:
+# var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..3fe82414
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/preparation_tests.yaml
@@ -0,0 +1,5 @@
+- name: Deletes all users except admin
+ sonic_users:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/single_run_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/single_run_template.yaml
new file mode 100644
index 00000000..b6f7eab5
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/single_run_template.yaml
@@ -0,0 +1,19 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_users:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: single_run_task_output
+ ignore_errors: yes
+
+- set_fact:
+ single_run_task_status: "{{ single_run_idem_condition }}"
+ when: item.task_condition_type == "idempotent"
+
+- set_fact:
+ single_run_task_status: "{{ single_run_condition }}"
+ when: item.task_condition_type != "idempotent"
+
+- import_role:
+ name: common
+ tasks_from: single.run.facts.report.yaml
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/tasks_template.yaml
new file mode 100644
index 00000000..9713e0a9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_users:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_users:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..0a2ceda7
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_users/templates/cli_test_case_01.cfg
@@ -0,0 +1,2 @@
+username operator1 password $6$E3pDmCbrbYNEw952$6y9rQNgNRVCU4Fgu7QnNa.AYHuDqpzwYiTuR8QG0TUwcAbR/VFK1gZX1EEHpeE0wjgBf6USnLXcgBj8TIS5mg. role operator
+username sysadmin password $6$UuxErB15tyodQ3SG$j02rdRERouNP3hed8w0Un5EO3tAhpoQqj6WlvJOU/.zuN3NzdoY/x1sahmgExMeOPeUtaSO.XdheJghewZG2y1 role admin
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml
new file mode 100644
index 00000000..b7deed02
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/defaults/main.yml
@@ -0,0 +1,42 @@
+---
+ansible_connection: httpapi
+module_name: vlans
+
+preparations_tests:
+ add_vlans_input:
+ - vlan_id: 500
+ - vlan_id: 501
+ - vlan_id: 502
+ - vlan_id: 503
+
+
+tests:
+ # merge test cases started
+ - name: test_case_01
+ description: Add access and trunk VLANs
+ state: merged
+ input:
+ - vlan_id: 500
+ description: "vlan500 description"
+ - vlan_id: 501
+ description: "vlan501 description"
+ - name: test_case_02
+ description: Update trunk VLANs
+ state: merged
+ input:
+ - vlan_id: 500
+ description: "modified vlan500 description"
+ - vlan_id: 502
+ - vlan_id: 503
+ # delete test cases started
+ - name: test_case_03
+ description: Delete Access VLAN
+ state: deleted
+ input:
+ - vlan_id: 500
+ description: "modified vlan500 description"
+ - vlan_id: 501
+ - name: test_case_04
+ description: Delete specific trunk VLANs
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/main.yml
new file mode 100644
index 00000000..be018fea
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/main.yml
@@ -0,0 +1,12 @@
+- debug: msg="sonic_l2_interfaces Test started ..."
+
+- name: Preparations test, creates VLANs
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Display all variables/facts known for a host
+ debug:
+ var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..464180f6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/preparation_tests.yaml
@@ -0,0 +1,23 @@
+---
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize default interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ default_interface_cli }}"
+ register: output
+ ignore_errors: yes
+- name: Delete VLANs Inputs
+ sonic_vlans:
+ config: "{{ preparations_tests.add_vlans_input }}"
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/tasks_template.yaml
new file mode 100644
index 00000000..2e0c4135
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vlans/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_vlans:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_vlans:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml
new file mode 100644
index 00000000..afbfd754
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/defaults/main.yml
@@ -0,0 +1,139 @@
+---
+ansible_connection: httpapi
+module_name: vrf
+
+vrf_1: VrfReg1
+vrf_2: VrfReg2
+
+po1: Portchannel 100
+vlan1: Vlan 100
+looopback1: Loopback 100
+
+po2: Portchannel 101
+vlan2: Vlan 101
+looopback2: Loopback 101
+
+preparations_tests:
+ delete_interfaces:
+ - "no interface {{ po1 }}"
+ - "no interface {{ vlan1 }}"
+ - "no interface {{ looopback1 }}"
+ - "no interface {{ po2 }}"
+ - "no interface {{ vlan2 }}"
+ - "no interface {{ looopback2 }}"
+ init_interfaces:
+ - "interface {{ po1 }}"
+ - "interface {{ vlan1 }}"
+ - "interface {{ looopback1 }}"
+ - "interface {{ po2 }}"
+ - "interface {{ vlan2 }}"
+ - "interface {{ looopback2 }}"
+
+tests_cli:
+ - name: cli_test_case_01
+ description: creates VRF properties
+ state: merged
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+ - name: "{{ interface1 }}"
+ - name: "{{ po1 }}"
+ - name: "{{ vlan1 }}"
+ - name: "{{ looopback1 }}"
+ - name: "{{ vrf_2 }}"
+
+tests:
+ - name: test_case_01
+ description: creates VRF properties
+ state: merged
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+ - name: "{{ interface1 }}"
+ - name: "{{ po1 }}"
+ - name: "{{ vrf_2 }}"
+
+ - name: test_case_02
+ description: Update VRF properties
+ state: merged
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+ - name: "{{ vlan1 }}"
+ - name: "{{ vrf_2 }}"
+ members:
+ interfaces:
+ - name: "{{ interface2 }}"
+ - name: "{{ po2 }}"
+
+ - name: test_case_03
+ description: Update VRF properties
+ state: merged
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+ - name: "{{ interface1 }}"
+ - name: "{{ po1 }}"
+ - name: "{{ looopback1 }}"
+ - name: "{{ vrf_2 }}"
+ members:
+ interfaces:
+ - name: "{{ interface2 }}"
+ - name: "{{ po2 }}"
+ - name: "{{ vlan2 }}"
+ - name: "{{ looopback2 }}"
+
+ - name: del_test_case_04
+ description: Delete VRF properties
+ state: deleted
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+ - name: "{{ interface1 }}"
+ - name: "{{ vrf_2 }}"
+ members:
+ interfaces:
+ - name: "{{ interface2 }}"
+ - name: "{{ po2 }}"
+
+ - name: del_test_case_05
+ description: Delete VRF properties
+ state: deleted
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+
+ - name: del_test_case_06
+ description: Delete VRF properties
+ state: deleted
+ input:
+ - name: "{{ vrf_2 }}"
+
+ - name: test_case_07
+ description: Merged VRF properties
+ state: merged
+ input:
+ - name: "{{ vrf_1 }}"
+ members:
+ interfaces:
+ - name: "{{ interface1 }}"
+ - name: "{{ po1 }}"
+ - name: "{{ looopback1 }}"
+ - name: "{{ vrf_2 }}"
+ members:
+ interfaces:
+ - name: "{{ interface2 }}"
+ - name: "{{ po2 }}"
+ - name: "{{ vlan2 }}"
+ - name: "{{ looopback2 }}"
+
+ - name: del_test_case_08
+ description: Delete VRF properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..c6a26a68
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/cleanup_tests.yaml
@@ -0,0 +1,6 @@
+- name: Deletes old vrf
+ sonic_vrfs:
+ config: []
+ state: deleted
+ ignore_errors: yes
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml
new file mode 100644
index 00000000..8e165bc6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/main.yml
@@ -0,0 +1,34 @@
+- debug: msg="sonic_vrfs Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} CLI validation started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests_cli }}"
+
+- name: "Test CLI validation started ..."
+ include_role:
+ name: common
+ tasks_from: cli_tasks_template.yaml
+ loop: "{{ tests_cli }}"
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: Cleanup tests
+ include_tasks: cleanup_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+- name: Cleanup tests
+ include_tasks: cleanup_tests.yaml
+
+# - name: Display all variables/facts known for a host
+# debug:
+# var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..c6a33af4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/preparation_tests.yaml
@@ -0,0 +1,34 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize default interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ default_interface_cli }}"
+ register: output
+ ignore_errors: yes
+- name: Deletes old VRFs
+ sonic_vrfs:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_interfaces }}"
+ register: output
+ ignore_errors: yes
+- name: Deletes old VRFs
+ sonic_vrfs:
+ config: []
+ state: deleted
+ ignore_errors: yes
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/tasks_template.yaml
new file mode 100644
index 00000000..85fdccc0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_vrfs:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_vrfs:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..35cd6ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vrfs/templates/cli_test_case_01.cfg
@@ -0,0 +1,10 @@
+ip vrf VrfReg1
+ip vrf VrfReg2
+interface Vlan100
+ ip vrf forwarding VrfReg1
+interface Loopback 100
+ ip vrf forwarding VrfReg1
+interface PortChannel 100
+ ip vrf forwarding VrfReg1
+interface {{ interface1 }}
+ ip vrf forwarding VrfReg1
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml
new file mode 100644
index 00000000..f2687a09
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/defaults/main.yml
@@ -0,0 +1,121 @@
+---
+ansible_connection: httpapi
+module_name: vxlan
+
+vrf1: VrfReg1
+vrf2: VrfReg2
+
+po1: Portchannel 100
+vlan1: 105
+looopback1: Loopback 100
+po2: Portchannel 101
+vlan2: 106
+looopback2: Loopback 101
+
+preparations_tests:
+ delete_interfaces:
+ - "no interface Vlan {{ vlan1 }}"
+ - "no interface Vlan {{ vlan2 }}"
+ init_interfaces:
+ - "interface Vlan {{ vlan1 }}"
+ - "interface Vlan {{ vlan2 }}"
+ - "exit"
+ - "ip vrf {{vrf1}}"
+ - "ip vrf {{vrf2}}"
+
+tests_cli:
+ - name: cli_test_case_01
+ description: creates VXLAN properties
+ state: merged
+ input:
+ - name: vtep1
+ source_ip: 1.1.1.1
+ primary_ip: 2.2.2.2
+ evpn_nvo: nvo5
+ vlan_map:
+ - vni: 101
+ vlan: "{{vlan1}}"
+ - vni: 102
+ vlan: "{{vlan2}}"
+ vrf_map:
+ - vni: 101
+ vrf: "{{vrf1}}"
+ - vni: 102
+ vrf: "{{vrf2}}"
+
+tests:
+ - name: test_case_01
+ description: creates Vxlan properties
+ state: merged
+ input:
+ - name: vtep1
+ - name: test_case_02
+ description: Update VRF properties
+ state: merged
+ input:
+ - name: vtep1
+ source_ip: 1.1.1.1
+ primary_ip: 2.2.2.2
+ evpn_nvo: nvo5
+ - name: test_case_03
+ description: Update VRF properties
+ state: merged
+ input:
+ - name: vtep1
+ vlan_map:
+ - vni: 101
+ vlan: "{{vlan1}}"
+ - name: test_case_04
+ description: Update VRF properties
+ state: merged
+ input:
+ - name: vtep1
+ vrf_map:
+ - vni: 101
+ vrf: "{{vrf1}}"
+ - name: test_case_05
+ description: Update VRF properties
+ state: merged
+ input:
+ - name: vtep1
+ source_ip: 1.1.1.1
+ primary_ip: 2.2.2.2
+ evpn_nvo: nvo5
+ vlan_map:
+ - vni: 102
+ vlan: "{{vlan2}}"
+ vrf_map:
+ - vni: 102
+ vrf: "{{vrf2}}"
+ - name: del_test_case_06
+ description: Delete VRF properties
+ state: deleted
+ input:
+ - name: vtep1
+ source_ip: 1.1.1.1
+ primary_ip: 2.2.2.2
+ evpn_nvo: nvo5
+ vlan_map:
+ - vni: 101
+ vlan: "{{vlan1}}"
+ - vni: 102
+ vlan: "{{vlan2}}"
+ vrf_map:
+ - vni: 101
+ vrf: "{{vrf1}}"
+ - vni: 102
+ vrf: "{{vrf2}}"
+ - name: del_test_case_07
+ description: Delete VRF properties
+ state: deleted
+ input:
+ - name: vtep1
+ - name: test_case_08
+ description: Recreate Vxlan
+ state: merged
+ input:
+ - name: vtep1
+ - name: del_test_case_09
+ description: Delete VRF properties
+ state: deleted
+ input: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/meta/main.yaml
new file mode 100644
index 00000000..611fd54d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/meta/main.yaml
@@ -0,0 +1,5 @@
+---
+collections:
+ - dellemc.enterprise_sonic
+dependencies:
+ - { role: common } \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/cleanup_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/cleanup_tests.yaml
new file mode 100644
index 00000000..42f3ebb9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/cleanup_tests.yaml
@@ -0,0 +1,6 @@
+- name: Deletes old bgp
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+ \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/main.yml
new file mode 100644
index 00000000..721097ca
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/main.yml
@@ -0,0 +1,34 @@
+- debug: msg="sonic_interfaces Test started ..."
+
+- set_fact:
+ base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}"
+
+# - name: Preparations test
+# include_tasks: preparation_tests.yaml
+
+# - name: "Test {{ module_name }} CLI validation started ..."
+# include_tasks: tasks_template.yaml
+# loop: "{{ tests_cli }}"
+
+# - name: "Test CLI validation started ..."
+# include_role:
+# name: common
+# tasks_from: cli_tasks_template.yaml
+# loop: "{{ tests_cli }}"
+
+- name: Cleanup tests
+ include_tasks: cleanup_tests.yaml
+
+- name: Preparations test
+ include_tasks: preparation_tests.yaml
+
+- name: "Test {{ module_name }} started ..."
+ include_tasks: tasks_template.yaml
+ loop: "{{ tests }}"
+
+# - name: Cleanup tests
+# include_tasks: cleanup_tests.yaml
+
+# - name: Display all variables/facts known for a host
+# debug:
+# var: hostvars[inventory_hostname].ansible_facts.test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/preparation_tests.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/preparation_tests.yaml
new file mode 100644
index 00000000..5946f3f4
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/preparation_tests.yaml
@@ -0,0 +1,29 @@
+- name: Delete existing mclag
+ sonic_mclag:
+ config:
+ state: deleted
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes
+- name: "initialize interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.delete_interfaces }}"
+ register: output
+ ignore_errors: yes
+- name: "initialize interfaces"
+ vars:
+ ansible_connection: network_cli
+ sonic_config:
+ commands: "{{ preparations_tests.init_interfaces }}"
+ register: output
+ ignore_errors: yes
+- name: Deletes old vxlans
+ sonic_vxlans:
+ config: []
+ state: deleted
+ ignore_errors: yes \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/tasks_template.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/tasks_template.yaml
new file mode 100644
index 00000000..73d8250d
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/tasks/tasks_template.yaml
@@ -0,0 +1,21 @@
+- name: "{{ item.name}} , {{ item.description}}"
+ sonic_vxlans:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: action_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: action.facts.report.yaml
+
+- name: "{{ item.name}} , {{ item.description}} Idempotent"
+ sonic_vxlans:
+ config: "{{ item.input }}"
+ state: "{{ item.state }}"
+ register: idempotent_task_output
+ ignore_errors: yes
+
+- import_role:
+ name: common
+ tasks_from: idempotent.facts.report.yaml \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/templates/cli_test_case_01.cfg b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/templates/cli_test_case_01.cfg
new file mode 100644
index 00000000..ebdcb7c0
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/sonic_vxlan/templates/cli_test_case_01.cfg
@@ -0,0 +1,8 @@
+interface vxlan vtep1
+ source-ip 1.1.1.1
+ map vni 101 vlan 105
+ map vni 102 vlan 106
+ map vni 101 vrf VrfReg1
+ map vni 102 vrf VrfReg2
+!
+
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/defaults/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/defaults/main.yml
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/defaults/main.yml
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/meta/main.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/meta/main.yaml
new file mode 100644
index 00000000..a84afac6
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/meta/main.yaml
@@ -0,0 +1,3 @@
+---
+collections:
+ - dellemc.enterprise_sonic \ No newline at end of file
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/tasks/main.yml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/tasks/main.yml
new file mode 100644
index 00000000..a116ce79
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/tasks/main.yml
@@ -0,0 +1,12 @@
+- set_fact:
+ time: "{{ lookup('pipe', 'date +%H-%M-%S') }}"
+ date: "{{ lookup('pipe', 'date +%Y-%m-%d') }}"
+ report_dir: "{{ lookup('env', 'ANSIBLE_SONIC_REPORT_DIR') | default(REPORT_DIR, true) }}"
+- debug: var=report_dir
+- blockinfile:
+ path: "{{ report_dir }}/regression-{{ file_suffix }}.html"
+ block: "{{ lookup('template', 'regression_html_report.j2')}}"
+ mode: 00777
+ create: true
+ marker: ""
+ delegate_to: localhost
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2 b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2
new file mode 100644
index 00000000..0695c86b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/roles/test_reports/templates/regression_html_report.j2
@@ -0,0 +1,328 @@
+#jinja2: trim_blocks: True, lstrip_blocks: True
+
+<style>
+.table {
+ width: 100%;
+ max-width: 100%;
+ margin-bottom: 1rem;
+}
+
+.table th,
+.table td {
+ padding: 0.75rem;
+ vertical-align: top;
+ border-top: 1px solid #eceeef;
+}
+
+.table thead th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #eceeef;
+}
+
+.table tbody + tbody {
+ border-top: 2px solid #eceeef;
+}
+
+.table .table {
+ background-color: #fff;
+}
+
+.table-sm th,
+.table-sm td {
+ padding: 0.3rem;
+}
+
+.table-bordered {
+ border: 1px solid #eceeef;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border: 1px solid #eceeef;
+}
+
+.table-bordered thead th,
+.table-bordered thead td {
+ border-bottom-width: 2px;
+}
+
+.table-striped tbody tr:nth-of-type(odd) {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.table-hover tbody tr:hover {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-active,
+.table-active > th,
+.table-active > td {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-hover .table-active:hover {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-hover .table-active:hover > td,
+.table-hover .table-active:hover > th {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-success,
+.table-success > th,
+.table-success > td {
+ background-color: #dff0d8;
+}
+
+.table-hover .table-success:hover {
+ background-color: #d0e9c6;
+}
+
+.table-hover .table-success:hover > td,
+.table-hover .table-success:hover > th {
+ background-color: #d0e9c6;
+}
+
+.table-info,
+.table-info > th,
+.table-info > td {
+ background-color: #d9edf7;
+}
+
+.table-hover .table-info:hover {
+ background-color: #c4e3f3;
+}
+
+.table-hover .table-info:hover > td,
+.table-hover .table-info:hover > th {
+ background-color: #c4e3f3;
+}
+
+.table-warning,
+.table-warning > th,
+.table-warning > td {
+ background-color: #fcf8e3;
+}
+
+.table-hover .table-warning:hover {
+ background-color: #faf2cc;
+}
+
+.table-hover .table-warning:hover > td,
+.table-hover .table-warning:hover > th {
+ background-color: #faf2cc;
+}
+
+.table-danger,
+.table-danger > th,
+.table-danger > td {
+ background-color: #f2dede;
+}
+
+.table-hover .table-danger:hover {
+ background-color: #ebcccc;
+}
+
+.table-hover .table-danger:hover > td,
+.table-hover .table-danger:hover > th {
+ background-color: #ebcccc;
+}
+
+.thead-inverse th {
+ color: #fff;
+ background-color: #292b2c;
+}
+
+.thead-default th {
+ color: #464a4c;
+ background-color: #eceeef;
+}
+
+.table-inverse {
+ color: #fff;
+ background-color: #292b2c;
+}
+
+.table-inverse th,
+.table-inverse td,
+.table-inverse thead th {
+ border-color: #fff;
+}
+
+.table-inverse.table-bordered {
+ border: 0;
+}
+
+.table-responsive {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+}
+
+.table-responsive.table-bordered {
+ border: 0;
+}
+
+.passed {
+color: green;
+}
+.failed {
+color: red;
+}
+
+
+</style>
+
+<h2 style="color:blue;">Regression report </h2>
+<h3 style="color:blue;"> Date: <i style="color:black;">{{ date}} </i> Time: <i style="color:black;">{{ time }}</i></h3>
+
+{% set complete_passed = [0] %}
+{% set complete_failed = [0] %}
+{% set complete_total = [0] %}
+<h3 style="color:blue;">Summary report </h3>
+<table class="table table-striped table-bordered">
+ <thead>
+ <tr>
+ <th>Module</th>
+ <th>Total testcases</th>
+ <th>Passed</th>
+ <th>Failed</th>
+ </tr>
+ </thead>
+<tbody>
+
+{% for module_name, test_data_list in ansible_facts.test_reports.items() %}
+{% set passed = [0] %}
+{% set failed = [0] %}
+{% set total = [0] %}
+{% for testcase_name, test_data in test_data_list.items() %}
+{% if total.append(total.pop() + 1) %}{% endif %}
+{% if complete_total.append(complete_total.pop() + 1) %}{% endif %}
+
+{% if 'Passed' in test_data.status %}
+{% if passed.append(passed.pop() + 1) %}{% endif %}
+{% if complete_passed.append(complete_passed.pop() + 1) %}{% endif %}
+{% endif %}
+
+{% if 'Failed' in test_data.status %}
+{% if failed.append(failed.pop() + 1) %}{% endif %}
+{% if complete_failed.append(complete_failed.pop() + 1) %}{% endif %}
+{% endif %}
+{% endfor %}
+<tr>
+<td>{{ module_name }}</td>
+<td>{{ total[0] }}</td>
+<td>{{ passed[0] }}</td>
+<td>{{ failed[0] }}</td>
+</tr>
+{% endfor %}
+</tbody>
+</table>
+
+<h2> Testcase Complete Summary: Total: {{ complete_total[0] }}, Passed: {{ complete_passed[0] }}, Failed: {{ complete_failed[0] }} </h2>
+
+
+</br>
+</br>
+</br>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% for module_name, test_data_list in ansible_facts.test_reports.items() %}
+{% set passed = [0] %}
+{% set failed = [0] %}
+{% set total = [0] %}
+
+{% for testcase_name, test_data in test_data_list.items() %}
+{% if total.append(total.pop() + 1) %}{% endif %}
+
+{% if 'Passed' in test_data.status %}
+{% if passed.append(passed.pop() + 1) %}{% endif %}
+{% endif %}
+
+{% if 'Failed' in test_data.status %}
+{% if failed.append(failed.pop() + 1) %}{% endif %}
+{% endif %}
+{% endfor %}
+<h3 style="color:blue;">Detailed report of <i style="color:black;"> {{module_name }} </i> </h3>
+<h2> Testcase summary: Total: {{ total[0] }}, Passed: {{ passed[0] }}, Failed: {{ failed[0] }} </h2>
+<table class="table table-striped table-bordered">
+ <thead>
+ <tr>
+ <th>Testcase name</th>
+ <th>Status</th>
+ <th>User Input</th>
+ <th>Commands</th>
+ <th>Before</th>
+ <th>After</th>
+ <th>Module exception</th>
+ </tr>
+ </thead>
+<tbody>
+{% for name, test_data in test_data_list.items() %}
+<tr>
+<td>{{ name}}</td>
+<td>{{ test_data.status | default('Template Error')}}</td>
+{% if 'Passed' in test_data.status %}
+<td></td>
+<td></td>
+<td></td>
+<td></td>
+<td></td>
+<td></td>
+{% else %}
+<td>Input: {{ test_data.configs | default('Template Error') | to_nice_json(indent=3) }}</td>
+<td>Commands: {{ test_data.commands | default('Template Error') | to_nice_json(indent=3) }}</td>
+<td>Before: {{ test_data.before | default('Template Error') | to_nice_json(indent=3) }}</td>
+<td>After: {{ test_data.after | default('Template Error') | to_nice_json(indent=3) }}</td>
+<td>Error: {{ test_data.module_stderr | default('Template Error') | to_nice_json(indent=3) }}</td>
+{% endif %}
+</tr>
+{% endfor %}
+</tbody>
+</table>
+{% endfor %}
+
+<h2> Testcase Complete Summary: Total: {{ complete_total[0] }}, Passed: {{ complete_passed[0] }}, Failed: {{ complete_failed[0] }} </h2>
+
+
+<script>
+window.onload = function() {
+var ele = document.getElementsByTagName('table');
+for(var tb = 0; tb < ele.length;tb++) {
+ tbody = ele[tb].getElementsByTagName('tbody')
+ for(var tbd = 0; tbd < tbody.length;tbd++) {
+ tr = tbody[tbd].getElementsByTagName('tr');
+ for (var trs = 0; trs < tr.length;trs++) {
+ td = tr[trs].getElementsByTagName('td')
+ if(td[1].innerHTML === 'Passed') {
+ td[1].className ='passed';
+ }
+ if(td[1].innerHTML === 'Failed') {
+ td[1].className ='failed';
+ }
+ }
+ }
+ }
+}
+</script>
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml
new file mode 100644
index 00000000..c34c286c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/regression/test.yaml
@@ -0,0 +1,39 @@
+---
+- name: "Test SONiC CLI"
+ hosts: sonic1
+ gather_facts: no
+ connection: httpapi
+ vars:
+ file_suffix: "{{ lookup('pipe','date +%Y-%m-%d-%H-%M-%S') }}"
+ collections:
+ - dellemc.enterprise_sonic
+ roles:
+ #- sonic_api
+ #- sonic_command
+ #- sonic_config
+
+ - sonic_interfaces
+ - sonic_l2_interfaces
+ - sonic_lag_interfaces
+ - sonic_mclag
+ - sonic_vlans
+ - sonic_l3_interfaces
+ - sonic_bgp_communities
+ - sonic_bgp_ext_communities
+ - sonic_bgp_as_paths
+ - sonic_bgp
+ - sonic_bgp_af
+ - sonic_bgp_neighbors
+ - sonic_bgp_neighbors_af
+ - sonic_vrfs
+ - sonic_vxlan
+ - sonic_port_breakout
+ - sonic_users
+ - sonic_aaa
+ - sonic_tacacs_server
+ - sonic_radius_server
+ - sonic_system
+ - sonic_prefix_lists
+ - sonic_static_routes
+ - sonic_ntp
+ - test_reports
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.10.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.10.txt
new file mode 100644
index 00000000..c2cf4ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.10.txt
@@ -0,0 +1 @@
+plugins/action/sonic.py action-plugin-docs #action plugin for base class
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.11.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.11.txt
new file mode 100644
index 00000000..c2cf4ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.11.txt
@@ -0,0 +1 @@
+plugins/action/sonic.py action-plugin-docs #action plugin for base class
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.12.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.12.txt
new file mode 100644
index 00000000..c2cf4ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.12.txt
@@ -0,0 +1 @@
+plugins/action/sonic.py action-plugin-docs #action plugin for base class
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.13.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.13.txt
new file mode 100644
index 00000000..c2cf4ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.13.txt
@@ -0,0 +1 @@
+plugins/action/sonic.py action-plugin-docs #action plugin for base class
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.14.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.14.txt
new file mode 100644
index 00000000..c2cf4ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.14.txt
@@ -0,0 +1 @@
+plugins/action/sonic.py action-plugin-docs #action plugin for base class
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.9.txt b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.9.txt
new file mode 100644
index 00000000..c2cf4ded
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/sanity/ignore-2.9.txt
@@ -0,0 +1 @@
+plugins/action/sonic.py action-plugin-docs #action plugin for base class
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/run_test_cases.sh b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/run_test_cases.sh
new file mode 100755
index 00000000..ac35117e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/run_test_cases.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+pytest -vvvv test_diff_util.py
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_01_dict_diff_with_key_name.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_01_dict_diff_with_key_name.yaml
new file mode 100644
index 00000000..ddab8994
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_01_dict_diff_with_key_name.yaml
@@ -0,0 +1,43 @@
+---
+want:
+ name: tacacs
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ name: radius
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+diff:
+ name: tacacs
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_02_dict_diff_with_key_other.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_02_dict_diff_with_key_other.yaml
new file mode 100644
index 00000000..f7bd8a52
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_02_dict_diff_with_key_other.yaml
@@ -0,0 +1,46 @@
+---
+test_keys:
+ - config:
+ - method
+want:
+ method: tacacs
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ method: radius
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+diff:
+ method: tacacs
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_03_dict_diff_without_key.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_03_dict_diff_without_key.yaml
new file mode 100644
index 00000000..e09fe74a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_03_dict_diff_without_key.yaml
@@ -0,0 +1,38 @@
+---
+want:
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ auth_type: pap
+ key: pap
+ source_interface: Eth 11
+ timeout: 12
+ host:
+ name: my_host1
+ auth_type: pap
+ key: pap
+ port: 55
+ timeout: 11
+ priority: 2
+ vrf: default
+diff:
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ timeout: 12
+ priority: 3
+ vrf: mgmt
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_04_dict_diff_with_similar_dict.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_04_dict_diff_with_similar_dict.yaml
new file mode 100644
index 00000000..0d754337
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_04_dict_diff_with_similar_dict.yaml
@@ -0,0 +1,28 @@
+---
+want:
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+diff: {}
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_05_dict_diff_left_only.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_05_dict_diff_left_only.yaml
new file mode 100644
index 00000000..8a3e2e4a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_05_dict_diff_left_only.yaml
@@ -0,0 +1,29 @@
+---
+want:
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ key: chap
+ host:
+ name: my_host
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+diff:
+ auth_type: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ auth_type: chap
+ key: chap
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_06_dict_diff_left_only_with_none.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_06_dict_diff_left_only_with_none.yaml
new file mode 100644
index 00000000..78c65d27
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_06_dict_diff_left_only_with_none.yaml
@@ -0,0 +1,18 @@
+---
+want:
+ auth_type: chap
+ key:
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ auth_type:
+ port:
+ timeout: 15
+have:
+ key: chap
+diff:
+ auth_type: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ timeout: 15
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_07_dict_diff_skeleton_only.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_07_dict_diff_skeleton_only.yaml
new file mode 100644
index 00000000..b2307a03
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_07_dict_diff_skeleton_only.yaml
@@ -0,0 +1,30 @@
+---
+skeleton: True
+want:
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ key: pap
+ host:
+ name: my_host1
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+diff:
+ auth_type: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ auth_type: chap
+ key: chap
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_08_list_diff_with_key_name.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_08_list_diff_with_key_name.yaml
new file mode 100644
index 00000000..b2317f77
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_08_list_diff_with_key_name.yaml
@@ -0,0 +1,34 @@
+---
+want:
+ - name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+have:
+ - name: vrf1
+ router_id: 100.2.2.30
+ log_neighbor_changes: False
+ - name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - name: vrf3
+ router_id: 102.2.2.30
+ log_neighbor_changes: True
+diff:
+ - name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_09_list_diff_with_multi_keys.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_09_list_diff_with_multi_keys.yaml
new file mode 100644
index 00000000..e3e9701c
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_09_list_diff_with_multi_keys.yaml
@@ -0,0 +1,50 @@
+---
+test_keys:
+ - config:
+ - vrf_name
+ - bgp_as
+want:
+ - bgp_as: 51
+ vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - bgp_as: 52
+ vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - bgp_as: 53
+ vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - bgp_as: 54
+ vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+have:
+ - bgp_as: 50
+ vrf_name: vrf1
+ - bgp_as: 51
+ vrf_name: vrf1
+ router_id: 100.2.2.30
+ log_neighbor_changes: False
+ - bgp_as: 52
+ vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - bgp_as: 53
+ vrf_name: vrf3
+ router_id: 102.2.2.30
+ log_neighbor_changes: True
+diff:
+ - bgp_as: 51
+ vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - bgp_as: 53
+ vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - bgp_as: 54
+ vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_10_list_diff_with_key_other.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_10_list_diff_with_key_other.yaml
new file mode 100644
index 00000000..3d4494b9
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_10_list_diff_with_key_other.yaml
@@ -0,0 +1,37 @@
+---
+test_keys:
+ - config:
+ - vrf_name
+want:
+ - vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+have:
+ - vrf_name: vrf1
+ router_id: 100.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf3
+ router_id: 102.2.2.30
+ log_neighbor_changes: True
+diff:
+ - vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_11_list_diff_with_similar_list.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_11_list_diff_with_similar_list.yaml
new file mode 100644
index 00000000..b5826a5a
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_11_list_diff_with_similar_list.yaml
@@ -0,0 +1,31 @@
+---
+test_keys:
+ - config:
+ - vrf_name
+want:
+ - vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+have:
+ - vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+diff: []
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_12_list_diff_with_left_only.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_12_list_diff_with_left_only.yaml
new file mode 100644
index 00000000..b491385e
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_12_list_diff_with_left_only.yaml
@@ -0,0 +1,40 @@
+---
+test_keys:
+ - config:
+ - vrf_name
+want:
+ - vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf5
+ router_id: 114.2.2.30
+ log_neighbor_changes: True
+have:
+ - vrf_name: default
+ router_id: 100.2.2.30
+ log_neighbor_changes: False
+diff:
+ - vrf_name: vrf1
+ router_id: 110.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf5
+ router_id: 114.2.2.30
+ log_neighbor_changes: True
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_13_list_diff_with_left_only_with_none.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_13_list_diff_with_left_only_with_none.yaml
new file mode 100644
index 00000000..b15fea91
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_13_list_diff_with_left_only_with_none.yaml
@@ -0,0 +1,48 @@
+---
+test_keys:
+ - config:
+ - vrf_name
+want:
+ - vrf_name: vrf1
+ router_id:
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ log_neighbor_changes:
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf5
+ router_id: 114.2.2.30
+ log_neighbor_changes: True
+ best_path:
+ as_path:
+ med:
+ value: 5
+ id: i1
+have:
+ - vrf_name: default
+ router_id: 100.2.2.30
+ log_neighbor_changes: False
+
+diff:
+ - vrf_name: vrf1
+ log_neighbor_changes: True
+ - vrf_name: vrf2
+ router_id: 111.2.2.30
+ - vrf_name: vrf3
+ router_id: 112.2.2.30
+ log_neighbor_changes: False
+ - vrf_name: vrf4
+ router_id: 113.2.2.30
+ log_neighbor_changes: True
+ - vrf_name: vrf5
+ router_id: 114.2.2.30
+ log_neighbor_changes: True
+ best_path:
+ as_path:
+ value: 5
+ id: i1
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_14_list_diff_skeleton_only.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_14_list_diff_skeleton_only.yaml
new file mode 100644
index 00000000..b2ead570
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_14_list_diff_skeleton_only.yaml
@@ -0,0 +1,61 @@
+---
+skeleton: True
+want:
+ - name: radius
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+ - name: tacacs
+ auth_type: chap
+ key: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ name: my_host
+ auth_type: chap
+ key: chap
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+have:
+ - name: radius
+ key: pap
+ host:
+ name: my_host1
+ port: 55
+ timeout: 12
+ priority: 3
+ vrf: mgmt
+ - name: tacacs
+ auth_type: pap
+ source_interface: Eth 11
+ timeout: 11
+ host:
+ name: my_host_03
+ auth_type: pap
+ timeout: 13
+ priority: 4
+diff:
+ - name: radius
+ auth_type: chap
+ source_interface: Eth 12
+ timeout: 12
+ host:
+ auth_type: chap
+ key: chap
+ - name: tacacs
+ key: chap
+ host:
+ key: chap
+ port: 55
+ vrf: mgmt
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_15_list_of_list_diff.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_15_list_of_list_diff.yaml
new file mode 100644
index 00000000..77565dff
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_15_list_of_list_diff.yaml
@@ -0,0 +1,163 @@
+---
+test_keys:
+ - marks:
+ - year
+ - subjects:
+ - code
+want:
+ - name: test1
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 55
+ - code: E
+ mark: 76
+ - code: S
+ mark: 57
+ - year: 2
+ subjects:
+ - code: M
+ mark: 85
+ - code: E
+ mark: 56
+ - code: S
+ mark: 87
+ - year: 3
+ subjects:
+ - code: M
+ mark: 55
+ - code: E
+ mark: 96
+ - code: S
+ mark: 57
+ - year: 4
+ subjects:
+ - code: M
+ mark: 65
+ - code: E
+ mark: 56
+ - code: S
+ mark: 67
+ - name: test2
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 55
+ - code: E
+ mark: 76
+ - code: S
+ mark: 57
+ - year: 2
+ subjects:
+ - code: M
+ mark: 85
+ - code: E
+ mark: 56
+ - code: S
+ mark: 87
+ - code: G
+ mark: 58
+ - name: test3
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 56
+have:
+ - name: test1
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 75
+ - code: E
+ mark: 76
+ - code: S
+ mark: 77
+ - year: 2
+ subjects:
+ - code: M
+ mark: 85
+ - code: E
+ mark: 86
+ - code: S
+ mark: 87
+ - year: 3
+ subjects:
+ - code: M
+ mark: 95
+ - code: E
+ mark: 96
+ - code: S
+ mark: 97
+ - year: 4
+ subjects:
+ - code: M
+ mark: 65
+ - code: E
+ mark: 66
+ - code: S
+ mark: 67
+ - name: test2
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 75
+ - code: E
+ mark: 76
+ - code: S
+ mark: 77
+ - year: 2
+ subjects:
+ - code: M
+ mark: 85
+ - code: E
+ mark: 86
+ - code: S
+ mark: 87
+diff:
+ - name: test1
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 55
+ - code: S
+ mark: 57
+ - year: 2
+ subjects:
+ - code: E
+ mark: 56
+ - year: 3
+ subjects:
+ - code: M
+ mark: 55
+ - code: S
+ mark: 57
+ - year: 4
+ subjects:
+ - code: E
+ mark: 56
+ - name: test2
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 55
+ - code: S
+ mark: 57
+ - year: 2
+ subjects:
+ - code: E
+ mark: 56
+ - code: G
+ mark: 58
+ - name: test3
+ marks:
+ - year: 1
+ subjects:
+ - code: M
+ mark: 56
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_16_complex_list_with_dict_diff.yaml b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_16_complex_list_with_dict_diff.yaml
new file mode 100644
index 00000000..a6d40b8b
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_16_complex_list_with_dict_diff.yaml
@@ -0,0 +1,252 @@
+---
+test_keys:
+ - config:
+ - vrf_name
+ - bgp_as
+ - afis:
+ - afi
+ - safi
+ - redistribute:
+ - protocol
+ - route_advertise_list:
+ - advertise_afi
+want:
+ - bgp_as: 51
+ vrf_name: vrf1
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 2
+ ibgp: 3
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "26"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "28"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ advertise_all_vni: True
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
+ - bgp_as: 52
+ vrf_name: vrf2
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 2
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg2.1
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3.1
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "22"
+ protocol: connected
+ route_map: rmap_rega
+ - metric: "28"
+ protocol: ospf
+ route_map: rmap_regb.1
+ - metric: "28"
+ protocol: static
+ route_map: rmap_regc.1
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
+have:
+ - bgp_as: 50
+ vrf_name: vrf1
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 2
+ ibgp: 3
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "26"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "28"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ advertise_all_vni: True
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
+ - bgp_as: 52
+ vrf_name: vrf2
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 1
+ ibgp: 2
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "26"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 2
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_rega
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_regb
+ - metric: "28"
+ protocol: static
+ route_map: rmap_regc
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+diff:
+ - bgp_as: 51
+ vrf_name: vrf1
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 2
+ ibgp: 3
+ redistribute:
+ - metric: "20"
+ protocol: connected
+ route_map: rmap_reg1
+ - metric: "26"
+ protocol: ospf
+ route_map: rmap_reg2
+ - metric: "25"
+ protocol: static
+ route_map: rmap_reg3
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ebgp: 3
+ ibgp: 4
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ route_map: rmap_reg3
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg1
+ - metric: "28"
+ protocol: static
+ route_map: rmap_reg2
+ - afi: l2vpn
+ safi: evpn
+ advertise_all_vni: True
+ route_advertise_list:
+ - advertise_afi: ipv4
+ route_map: rmap_reg1
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
+ - bgp_as: 52
+ vrf_name: vrf2
+ address_family:
+ afis:
+ - afi: ipv4
+ safi: unicast
+ max_path:
+ ebgp: 3
+ redistribute:
+ - metric: "21"
+ protocol: connected
+ - metric: "27"
+ protocol: ospf
+ route_map: rmap_reg2.1
+ - protocol: static
+ route_map: rmap_reg3.1
+ - afi: ipv6
+ safi: unicast
+ max_path:
+ ibgp: 4
+ redistribute:
+ - metric: "22"
+ protocol: connected
+ - metric: "28"
+ protocol: ospf
+ route_map: rmap_regb.1
+ - protocol: static
+ route_map: rmap_regc.1
+ - afi: l2vpn
+ safi: evpn
+ route_advertise_list:
+ - advertise_afi: ipv6
+ route_map: rmap_reg2
diff --git a/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_diff_util.py b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_diff_util.py
new file mode 100644
index 00000000..65f384a1
--- /dev/null
+++ b/ansible_collections/dellemc/enterprise_sonic/tests/unit/utils/test_diff_util.py
@@ -0,0 +1,87 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+import unittest
+import os
+import yaml
+
+import sys
+sys.path.append('/root/.ansible/collections')
+
+from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
+ get_diff,
+)
+
+
+class TestDiffUtils(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def read_and_compare(self, file_name):
+ file_name = os.path.join(os.path.dirname(__file__), file_name)
+ file_stream = open(file_name, "r")
+ data = yaml.full_load(file_stream)
+ file_stream.close()
+
+ want = data.get('want', [])
+ have = data.get('have', [])
+ diff_exp = data.get('diff', [])
+ test_keys = data.get('test_keys', None)
+ is_skeleton = data.get('skeleton', None)
+
+ diff_act = get_diff(want, have, test_keys, is_skeleton=is_skeleton)
+
+ self.assertEqual(diff_exp, diff_act)
+
+ def test_01_dict_diff_with_key_name(self):
+ self.read_and_compare("test_01_dict_diff_with_key_name.yaml")
+
+ def test_02_dict_diff_with_key_other(self):
+ self.read_and_compare("test_02_dict_diff_with_key_other.yaml")
+
+ def test_03_dict_diff_without_key(self):
+ self.read_and_compare("test_03_dict_diff_without_key.yaml")
+
+ def test_04_dict_diff_with_similar_dict(self):
+ self.read_and_compare("test_04_dict_diff_with_similar_dict.yaml")
+
+ def test_05_dict_diff_left_only(self):
+ self.read_and_compare("test_05_dict_diff_left_only.yaml")
+
+ def test_06_dict_diff_left_only_with_none(self):
+ self.read_and_compare("test_06_dict_diff_left_only_with_none.yaml")
+
+ def test_07_dict_diff_skeleton_only(self):
+ self.read_and_compare("test_07_dict_diff_skeleton_only.yaml")
+
+ def test_08_list_diff_with_key_name(self):
+ self.read_and_compare("test_08_list_diff_with_key_name.yaml")
+
+ def test_09_list_diff_with_multi_keys(self):
+ self.read_and_compare("test_09_list_diff_with_multi_keys.yaml")
+
+ def test_10_list_diff_with_key_other(self):
+ self.read_and_compare("test_10_list_diff_with_key_other.yaml")
+
+ def test_11_list_diff_with_similar_list(self):
+ self.read_and_compare("test_11_list_diff_with_similar_list.yaml")
+
+ def test_12_list_diff_with_left_only(self):
+ self.read_and_compare("test_12_list_diff_with_left_only.yaml")
+
+ def test_13_list_diff_with_left_only_with_none(self):
+ self.read_and_compare("test_13_list_diff_with_left_only_with_none.yaml")
+
+ def test_14_list_diff_skeleton_only(self):
+ self.read_and_compare("test_14_list_diff_skeleton_only.yaml")
+
+ def test_15_list_of_list_diff(self):
+ self.read_and_compare("test_15_list_of_list_diff.yaml")
+
+ def test_16_complex_list_with_dict_diff(self):
+ self.read_and_compare("test_16_complex_list_with_dict_diff.yaml")