From cc40d32116db7fe039f451eb3e1e60ae66566a5f Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:17:53 +0530 Subject: [PATCH 01/41] Create fabric_workflow_manager.yml Initial file to create fabric workflow --- playbooks/bgpevpn/fabric_workflow_manager.yml | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 playbooks/bgpevpn/fabric_workflow_manager.yml diff --git a/playbooks/bgpevpn/fabric_workflow_manager.yml b/playbooks/bgpevpn/fabric_workflow_manager.yml new file mode 100644 index 0000000000..9ddac5c3ed --- /dev/null +++ b/playbooks/bgpevpn/fabric_workflow_manager.yml @@ -0,0 +1,108 @@ +--- +- name: Deploy BGP EVPN Fabric Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/bgp_evpn_fabric_devices.yml + vars: + output_dir: ./rendered + dnac_credentials_file: ../credentials.yml + + tasks: + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ fabric_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Render configs for all devices + ansible.builtin.template: + src: "{{ (role_template_map[item.0.key] if (role_template_map is defined and item.0.key in role_template_map) else ('bgp_evpn_fabric_' ~ item.0.key ~ '_cli.j2')) }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + peer_policy_template: "{{ peer_policy[item.0.key] }}" + peer_session_template: "{{ peer_session[item.0.key] }}" + dhcp_source_interface: "{{ router_id_interface }}" + neighbors: "{{ item.1.neighbors | default([]) }}" + loopback_ip: "{{ item.1.loopback_ip }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_files + + - name: Include DNAC credentials (if available) + ansible.builtin.include_vars: + file: "{{ dnac_credentials_file }}" + ignore_errors: true + + - name: Ensure evpn_push_items is defined + ansible.builtin.set_fact: + evpn_push_items: [] + - name: Push configs via DNAC template API (create ephemeral template per device) + when: dnac_host is defined + block: + - name: Build push payload list + ansible.builtin.set_fact: + evpn_push_items: "{{ evpn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+\\.cfg$','')), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_files.results }}" + + - name: Deploy each config using evpn_cli_template_workflow_manager module + vars: + proj_name: BGP_EVPN_FABRIC_AUTOGEN + loop: "{{ evpn_push_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + state: merged + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-EVPN" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated BGP EVPN fabric config" + language: JINJA + device_types: + - product_family: Switches and Hubs + # deploy_template: + # project_name: "{{ proj_name }}" + # template_name: "{{ cfg.device_name }}-EVPN" + # force_push: true + # device_details: + # device_hostnames: + # - "{{ cfg.device_name }}" + register: dnac_push_results + + - name: Show DNAC push summary + ansible.builtin.debug: + msg: "Deployed {{ dnac_push_results.results | length }} templates via DNAC" From a40aa62df99c403ea54bb8fb5ea2ee1d0d752477 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:26:00 +0530 Subject: [PATCH 02/41] Create fabric_overlay_topology_devices.yml Initian device topology var file --- .../vars/fabric_overlay_topology_devices.yml | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml diff --git a/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml b/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml new file mode 100644 index 0000000000..29ecb5b400 --- /dev/null +++ b/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml @@ -0,0 +1,135 @@ +--- +# Global fabric BGP EVPN VxLAN parameters +asn: 1001 +peer_policy: + leaf: leaf_peer_policy_template + spine: spine_peer_policy_template + border: border_peer_policy_template +peer_session: + leaf: leaf_peer_session_template + spine: spine_peer_session_template + border: border_peer_session_template +router_id_interface: Loopback0 +update_source: Loopback0 +source_interface: Loopback0 +peer_groups: + # Each device refers to neighbors by name so we can resolve IP automatically + leaf: [] # leaves will peer with spines (populated dynamically if needed) + spine: [] # spines will peer with leaves, borders and other spines (populated dynamically if needed) + border: [] # borders will peer with spines (populated dynamically if needed) + +# Device inventory grouped by role +fabric_devices: + leaf: # multiple leaf groups allowed + - group_name: leaf-group1 + devices: + - device_name: leaf-device1 + mgmt_ip: 100.10.10.1 + username: netops + password: "{{ vault_leaf_password | default('ChangeMeLeaf') }}" + loopback_ip: 115.1.1.1 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + - device_name: leaf-device2 + mgmt_ip: 100.10.10.2 + username: netops + password: "{{ vault_leaf_password | default('ChangeMeLeaf') }}" + loopback_ip: 115.2.2.2 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + - group_name: leaf-group2 + devices: + - device_name: leaf-device3 + mgmt_ip: 100.10.10.3 + username: netops + password: "{{ vault_leaf_password | default('ChangeMeLeaf') }}" + loopback_ip: 115.3.3.3 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + - device_name: leaf-device4 + mgmt_ip: 100.10.10.4 + username: netops + password: "{{ vault_leaf_password | default('ChangeMeLeaf') }}" + loopback_ip: 115.4.4.4 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + spine: # only one spine group typically + - group_name: spine-group1 + devices: + - device_name: spine-device1 + mgmt_ip: 100.10.10.5 + username: netops + password: "{{ vault_spine_password | default('ChangeMeSpine') }}" + loopback_ip: 115.5.5.5 + neighbors: + - 115.1.1.1 + - 115.2.2.2 + - 115.3.3.3 + - 115.4.4.4 + - 115.6.6.6 + - 115.7.7.7 + - 115.8.8.8 + - 115.9.9.9 + - 115.10.10.10 + - device_name: spine-device2 + mgmt_ip: 100.10.10.6 + username: netops + password: "{{ vault_spine_password | default('ChangeMeSpine') }}" + loopback_ip: 115.6.6.6 + neighbors: + - 115.1.1.1 + - 115.2.2.2 + - 115.3.3.3 + - 115.4.4.4 + - 115.5.5.5 + - 115.7.7.7 + - 115.8.8.8 + - 115.9.9.9 + - 115.10.10.10 + border: # multiple border groups allowed + - group_name: border-group1 + devices: + - device_name: border-device1 + mgmt_ip: 100.10.10.7 + username: netops + password: "{{ vault_border_password | default('ChangeMeBorder') }}" + loopback_ip: 115.7.7.7 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + - device_name: border-device2 + mgmt_ip: 100.10.10.8 + username: netops + password: "{{ vault_border_password | default('ChangeMeBorder') }}" + loopback_ip: 115.8.8.8 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + - group_name: border-group2 + devices: + - device_name: border-device3 + mgmt_ip: 100.10.10.9 + username: netops + password: "{{ vault_border_password | default('ChangeMeBorder') }}" + loopback_ip: 115.9.9.9 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + - device_name: border-device4 + mgmt_ip: 100.10.10.10 + username: netops + password: "{{ vault_border_password | default('ChangeMeBorder') }}" + loopback_ip: 115.10.10.10 + neighbors: + - 115.5.5.5 + - 115.6.6.6 + +# Mapping role to template path +role_template_map: + leaf: ../jinja_templates/fabric_leaf_cli.j2 + spine: ../jinja_templates/fabric_spine_cli.j2 + border: ../jinja_templates/fabric_border_cli.j2 From a5c920cf5a4307b3d0d99a70d243c25124b9bbc5 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:26:43 +0530 Subject: [PATCH 03/41] Update fabric_workflow_manager.yml Changed correct var file name --- playbooks/bgpevpn/fabric_workflow_manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/bgpevpn/fabric_workflow_manager.yml b/playbooks/bgpevpn/fabric_workflow_manager.yml index 9ddac5c3ed..222c24e949 100644 --- a/playbooks/bgpevpn/fabric_workflow_manager.yml +++ b/playbooks/bgpevpn/fabric_workflow_manager.yml @@ -3,7 +3,7 @@ hosts: localhost gather_facts: false vars_files: - - ./vars/bgp_evpn_fabric_devices.yml + - ./vars/fabric_overlay_topology_devices.yml vars: output_dir: ./rendered dnac_credentials_file: ../credentials.yml From 398ad2423f63c156dab30242a1b275ae0d18b0f6 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:29:37 +0530 Subject: [PATCH 04/41] Create fabric_border_cli.j2 Create fabric_border_cli.j2 --- .../jinja_templates/fabric_border_cli.j2 | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 new file mode 100644 index 0000000000..4e31fab398 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 @@ -0,0 +1,68 @@ +interface nve1 + no ip address + source-interface {{ source_interface }} + host-reachability protocol bgp + group-based-policy +! + +ip prefix-list ip_route_map_towards_wan_handoff seq 5 permit 0.0.0.0/0 le 31 +ip prefix-list ip_route_map_towards_wan_handoff seq 10 permit 177.0.0.0/16 ge 17 +ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 5 permit ::/0 le 127 +ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 10 permit 2002:177:0:1::/64 ge 65 +route-map ip_route_map_towards_wan_handoff permit 10 + match ip address prefix-list ip_route_map_towards_wan_handoff +route-map ipv6_route_map_towards_wan_handoff permit 10 + match ipv6 address prefix-list ipv6_route_map_towards_wan_handoff +! +router bgp {{ asn }} + template peer-policy {{ peer_policy_template }} + send-community both + exit-peer-policy + ! + template peer-session {{ peer_session_template }} + remote-as {{ asn }} + log-neighbor-changes + update-source {{ update_source }} + exit-peer-session + ! + bgp router-id interface {{ router_id_interface }} + bgp log-neighbor-changes + bgp graceful-restart + no bgp default ipv4-unicast +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} +{% endfor %} + ! + address-family ipv4 + exit-address-family + ! + address-family ipv4 mvpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family ipv6 mvpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family l2vpn evpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family +! + +ip dhcp relay information option vpn +ip dhcp relay information option +ip dhcp compatibility suboption link-selection standard +ip dhcp compatibility suboption server-override standard +! From 69143fb1caef2c924600dc37cbbe96567d7ffa46 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:30:23 +0530 Subject: [PATCH 05/41] Create fabric_leaf_cli.j2 Create fabric_leaf_cli.j2 --- .../jinja_templates/fabric_leaf_cli.j2 | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 new file mode 100644 index 0000000000..02b739034d --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 @@ -0,0 +1,62 @@ +interface nve1 + no ip address + source-interface {{ source_interface }} + host-reachability protocol bgp + group-based-policy +! + +router bgp {{ asn }} + template peer-policy {{ peer_policy_template }} + send-community both + exit-peer-policy + ! + template peer-session {{ peer_session_template }} + remote-as {{ asn }} + log-neighbor-changes + update-source {{ update_source }} + exit-peer-session + ! + bgp router-id interface {{ router_id_interface }} + bgp log-neighbor-changes + bgp graceful-restart + no bgp default ipv4-unicast +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} +{% endfor %} + ! + address-family ipv4 + exit-address-family + ! + address-family ipv4 mvpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family ipv6 mvpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family l2vpn evpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + +! + +ip dhcp-relay source-interface {{ dhcp_source_interface }} +ip dhcp relay information option vpn +ip dhcp relay information option +ip dhcp compatibility suboption link-selection standard +ip dhcp compatibility suboption server-override standard +ipv6 dhcp-relay source-interface {{ dhcp_source_interface }} +! From 5506cded8e240c8fb9294be278727cb87241ecb5 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:31:01 +0530 Subject: [PATCH 06/41] Create fabric_spine_cli.j2 Create fabric_spine_cli.j2 --- .../jinja_templates/fabric_spine_cli.j2 | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 b/playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 new file mode 100644 index 0000000000..0d92abaaac --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 @@ -0,0 +1,44 @@ +router bgp {{ asn }} + template peer-policy {{ peer_policy_template }} + route-reflector-client + send-community both + exit-peer-policy + ! + template peer-session {{ peer_session_template }} + remote-as {{ asn }} + log-neighbor-changes + update-source {{ update_source }} + exit-peer-session + ! + bgp router-id interface {{ router_id_interface }} + bgp log-neighbor-changes + bgp graceful-restart + no bgp default ipv4-unicast +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} +{% endfor %} + ! + address-family ipv4 mvpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family ipv6 mvpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family l2vpn evpn +{% for neighbor_ip in neighbors %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family +! From 6de5bed48a629144c6aac82e7c1c793e2021ebe8 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:44:00 +0530 Subject: [PATCH 07/41] Create l3vn_unicast_leaf_cli.j2 Create l3vn_unicast_leaf_cli.j2 --- playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 @@ -0,0 +1 @@ + From 16af5eea20afad94ead161806ca5d8b753e34e42 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:44:24 +0530 Subject: [PATCH 08/41] Create l3vn_unicast_border_cli.j2 Create l3vn_unicast_border_cli.j2 --- playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 @@ -0,0 +1 @@ + From 77d53fdfc4addab6932ed0b6af0eba95068ebb61 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:28:45 +0530 Subject: [PATCH 09/41] Create l3vn_multicast_anycast_rp_leaf_cli.j2 Create l3vn_multicast_anycast_rp_leaf_cli.j2 --- .../jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 @@ -0,0 +1 @@ + From 726d8301b0c128853123324815edde35443afe47 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:29:10 +0530 Subject: [PATCH 10/41] Create l3vn_multicast_anycast_rp_border_cli.j2 Create l3vn_multicast_anycast_rp_border_cli.j2 --- .../jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 @@ -0,0 +1 @@ + From 12d22d34ee27f6c66146b562c474f39fac7cafc4 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:29:50 +0530 Subject: [PATCH 11/41] Create l3vn_multicast_internal_rp_border_cli.j2 Create l3vn_multicast_internal_rp_border_cli.j2 --- .../jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 @@ -0,0 +1 @@ + From e77e17485d9e8c2d49e05e368d9e1b2d53666730 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:30:07 +0530 Subject: [PATCH 12/41] Create l3vn_multicast_internal_rp_leaf_cli.j2 Create l3vn_multicast_internal_rp_leaf_cli.j2 --- .../jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 @@ -0,0 +1 @@ + From 684cc4dc8aadac8683e2f9aae4e3ef46905ddb97 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:30:51 +0530 Subject: [PATCH 13/41] Create l3vn_multicast_external_rp_border_cli.j2 Create l3vn_multicast_external_rp_border_cli.j2 --- .../jinja_templates/l3vn_multicast_external_rp_border_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 @@ -0,0 +1 @@ + From 90e5b6bef7c5feb10d217bccaf22712aca6b891b Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:31:08 +0530 Subject: [PATCH 14/41] Create l3vn_multicast_external_rp_leaf_cli.j2 Create l3vn_multicast_external_rp_leaf_cli.j2 --- .../jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 | 1 + 1 file changed, 1 insertion(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 @@ -0,0 +1 @@ + From cba9c16a352da4c46ac57bc5d4c543fbd17957f8 Mon Sep 17 00:00:00 2001 From: vivek Date: Wed, 22 Oct 2025 15:28:12 +0530 Subject: [PATCH 15/41] Ansible playbooks and jinja templates for EVPN L2VN configurations --- .../evpn_l2vn_anycast_delete_deploy.yml | 71 ++++++++++++++++ .../bgpevpn/evpn_l2vn_anycast_deploy.yml | 82 ++++++++++++++++++ .../evpn_l2vn_anycast_mcast_delete_deploy.yml | 72 ++++++++++++++++ .../evpn_l2vn_anycast_mcast_deploy.yml | 83 +++++++++++++++++++ .../evpn_l2vn_localized_delete_deploy.yml | 70 ++++++++++++++++ .../bgpevpn/evpn_l2vn_localized_deploy.yml | 81 ++++++++++++++++++ .../jinja_templates/l2vn_anycast_cli.j2 | 48 +++++++++++ .../l2vn_anycast_delete_cli.j2 | 11 +++ .../jinja_templates/l2vn_anycast_mcast_cli.j2 | 48 +++++++++++ .../l2vn_anycast_mcast_delete_cli.j2 | 11 +++ .../jinja_templates/l2vn_localized_cli.j2 | 35 ++++++++ .../l2vn_localized_delete_cli.j2 | 4 + .../vars/evpn_l2vn_anycast_delete_var.yml | 4 + .../evpn_l2vn_anycast_mcast_delete_var.yml | 5 ++ .../vars/evpn_l2vn_anycast_mcast_var.yml | 14 ++++ .../bgpevpn/vars/evpn_l2vn_anycast_var.yml | 13 +++ .../vars/evpn_l2vn_localized_delete_var.yml | 3 + .../bgpevpn/vars/evpn_l2vn_localized_var.yml | 11 +++ 18 files changed, 666 insertions(+) create mode 100644 playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml create mode 100644 playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml create mode 100644 playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml create mode 100644 playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml create mode 100644 playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml create mode 100644 playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml create mode 100644 playbooks/bgpevpn/jinja_templates/l2vn_anycast_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l2vn_anycast_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l2vn_localized_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l2vn_localized_delete_cli.j2 create mode 100644 playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml create mode 100644 playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml create mode 100644 playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml create mode 100644 playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml create mode 100644 playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml create mode 100644 playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml new file mode 100644 index 0000000000..23bd60c746 --- /dev/null +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml @@ -0,0 +1,71 @@ +--- +- name: Deploy BGP EVPN L2VN Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/evpn_l2vn_anycast_delete_var.yml + tasks: + - name: Validate top-level L2VN delete input + ansible.builtin.assert: + that: + - l2vns is defined + - l2vns is iterable + - l2vns | length > 0 + fail_msg: "Invalid top-level vars: need non-empty 'l2vns' list." + + - name: Validate each L2VN entry mandatory fields + ansible.builtin.assert: + that: + - item.l2vn_name is defined + - item.vlan_id is defined + - item.vni is defined + fail_msg: >- + L2VN validation failed for index {{ idx }} (name={{ item.l2vn_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ l2vns }}" + loop_control: + index_var: idx + label: "{{ item.l2vn_name | default('UNNAMED') }}" + + - name: Render l2vn_anycast_delete_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_l2vn_anycast_delete: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_delete_cli.j2') }}" + + - name: Print rendered_l2vn_anycast_delete + ansible.builtin.debug: + var: rendered_l2vn_anycast_delete + + - name: Create new l2vn anycast delete template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "evpn_l2vn_anycast_delete" + template_name: "evpn_l2vn_anycast_delete_template" + template_content: "{{ rendered_l2vn_anycast_delete }}" + version_description: "Auto-generated BGP EVPN L2VN anycast delete config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "evpn_l2vn_anycast_delete" + template_name: "evpn_l2vn_anycast_delete_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml new file mode 100644 index 0000000000..4d734b4aa1 --- /dev/null +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml @@ -0,0 +1,82 @@ +--- +- name: Deploy BGP EVPN L2VN Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/evpn_l2vn_anycast_var.yml + tasks: + - name: Validate top-level L2VN input + ansible.builtin.assert: + that: + - asn is defined + - mac_address is defined + - l2vns is defined + - l2vns is iterable + - l2vns | length > 0 + fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list." + + - name: Validate each L2VN entry mandatory fields + ansible.builtin.assert: + that: + - item.l2vn_name is defined + - item.vlan_id is defined + - item.vni is defined + - item.associated_l3vn is defined + - item.l2vn_pool is defined + - item.l2vn_pool.ipv4_address is defined + - item.l2vn_pool.ipv4_netmask is defined + - (item.l2vn_pool.ipv6_address is not defined) or (item.l2vn_pool.ipv6_netmask is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv4_relay is not defined) or (item.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv4_relay is not defined) or (item.dhcp_relay.dhcpv4_relay.dhcpv4_server_vrf is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv6_relay is not defined) or (item.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv6_relay is not defined) or (item.dhcp_relay.dhcpv6_relay.dhcpv6_server_vrf is defined) + fail_msg: >- + L2VN validation failed for index {{ idx }} (name={{ item.l2vn_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ l2vns }}" + loop_control: + index_var: idx + label: "{{ item.l2vn_name | default('UNNAMED') }}" + + - name: Render l2vn_anycast_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_l2vn_anycast: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_cli.j2') }}" + + - name: Print rendered_l2vn_anycast + ansible.builtin.debug: + var: rendered_l2vn_anycast + + - name: Create new l2vn anycast template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "evpn_l2vn_anycast" + template_name: "evpn_l2vn_anycast_template" + template_content: "{{ rendered_l2vn_anycast }}" + version_description: "Auto-generated BGP EVPN L2VN anycast config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "evpn_l2vn_anycast" + template_name: "evpn_l2vn_anycast_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml new file mode 100644 index 0000000000..271a7c37d1 --- /dev/null +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml @@ -0,0 +1,72 @@ +--- +- name: Deploy BGP EVPN L2VN Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/evpn_l2vn_anycast_mcast_delete_var.yml + tasks: + - name: Validate top-level L2VN delete input + ansible.builtin.assert: + that: + - l2vns is defined + - l2vns is iterable + - l2vns | length > 0 + fail_msg: "Invalid top-level vars: need non-empty 'l2vns' list." + + - name: Validate each L2VN entry mandatory fields + ansible.builtin.assert: + that: + - item.l2vn_name is defined + - item.vlan_id is defined + - item.vni is defined + - item.multicast_group is defined + fail_msg: >- + L2VN validation failed for index {{ idx }} (name={{ item.l2vn_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ l2vns }}" + loop_control: + index_var: idx + label: "{{ item.l2vn_name | default('UNNAMED') }}" + + - name: Render l2vn_anycast_mcast_delete_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_l2vn_anycast_mcast_delete: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_delete_cli.j2') }}" + + - name: Print rendered_l2vn_anycast_mcast_delete + ansible.builtin.debug: + var: rendered_l2vn_anycast_mcast_delete + + - name: Create new l2vn anycast multicast delete template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "evpn_l2vn_anycast_mcast_delete" + template_name: "evpn_l2vn_anycast_mcast_delete_template" + template_content: "{{ rendered_l2vn_anycast_mcast_delete }}" + version_description: "Auto-generated BGP EVPN L2VN anycast multicast delete config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "evpn_l2vn_anycast_mcast_delete" + template_name: "evpn_l2vn_anycast_mcast_delete_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml new file mode 100644 index 0000000000..387b8b1061 --- /dev/null +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml @@ -0,0 +1,83 @@ +--- +- name: Deploy BGP EVPN L2VN Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/evpn_l2vn_anycast_mcast_var.yml + tasks: + - name: Validate top-level L2VN input + ansible.builtin.assert: + that: + - asn is defined + - mac_address is defined + - l2vns is defined + - l2vns is iterable + - l2vns | length > 0 + fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list." + + - name: Validate each L2VN entry mandatory fields + ansible.builtin.assert: + that: + - item.l2vn_name is defined + - item.vlan_id is defined + - item.vni is defined + - item.associated_l3vn is defined + - item.multicast_group is defined + - item.l2vn_pool is defined + - item.l2vn_pool.ipv4_address is defined + - item.l2vn_pool.ipv4_netmask is defined + - (item.l2vn_pool.ipv6_address is not defined) or (item.l2vn_pool.ipv6_netmask is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv4_relay is not defined) or (item.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv4_relay is not defined) or (item.dhcp_relay.dhcpv4_relay.dhcpv4_server_vrf is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv6_relay is not defined) or (item.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv6_relay is not defined) or (item.dhcp_relay.dhcpv6_relay.dhcpv6_server_vrf is defined) + fail_msg: >- + L2VN validation failed for index {{ idx }} (name={{ item.l2vn_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ l2vns }}" + loop_control: + index_var: idx + label: "{{ item.l2vn_name | default('UNNAMED') }}" + + - name: Render l2vn_anycast_mcast_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_l2vn_anycast_mcast: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_cli.j2') }}" + + - name: Print rendered_l2vn_anycast_mcast + ansible.builtin.debug: + var: rendered_l2vn_anycast_mcast + + - name: Create new l2vn anycast multicast template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "evpn_l2vn_anycast_mcast" + template_name: "evpn_l2vn_anycast_mcast_template" + template_content: "{{ rendered_l2vn_anycast_mcast }}" + version_description: "Auto-generated BGP EVPN L2VN anycast multicast config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "evpn_l2vn_anycast_mcast" + template_name: "evpn_l2vn_anycast_mcast_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml new file mode 100644 index 0000000000..1341471d7a --- /dev/null +++ b/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml @@ -0,0 +1,70 @@ +--- +- name: Deploy BGP EVPN L2VN Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/evpn_l2vn_localized_delete_var.yml + tasks: + - name: Validate top-level L2VN delete input + ansible.builtin.assert: + that: + - l2vns is defined + - l2vns is iterable + - l2vns | length > 0 + fail_msg: "Invalid top-level vars: need non-empty 'l2vns' list." + + - name: Validate each L2VN entry mandatory fields + ansible.builtin.assert: + that: + - item.l2vn_name is defined + - item.vlan_id is defined + fail_msg: >- + L2VN validation failed for index {{ idx }} (name={{ item.l2vn_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ l2vns }}" + loop_control: + index_var: idx + label: "{{ item.l2vn_name | default('UNNAMED') }}" + + - name: Render l2vn_localized_delete_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_l2vn_localized_delete: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_localized_delete_cli.j2') }}" + + - name: Print rendered_l2vn_localized_delete + ansible.builtin.debug: + var: rendered_l2vn_localized_delete + + - name: Create new l2vn localized delete template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "evpn_l2vn_localized_delete" + template_name: "evpn_l2vn_localized_delete_template" + template_content: "{{ rendered_l2vn_localized_delete }}" + version_description: "Auto-generated BGP EVPN L2VN localized delete config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "evpn_l2vn_localized_delete" + template_name: "evpn_l2vn_localized_delete_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml new file mode 100644 index 0000000000..b3053e7d00 --- /dev/null +++ b/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml @@ -0,0 +1,81 @@ +--- +- name: Deploy BGP EVPN L2VN Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/evpn_l2vn_localized_var.yml + tasks: + - name: Validate top-level L2VN input + ansible.builtin.assert: + that: + - asn is defined + - mac_address is defined + - l2vns is defined + - l2vns is iterable + - l2vns | length > 0 + fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list." + + - name: Validate each L2VN entry mandatory fields + ansible.builtin.assert: + that: + - item.l2vn_name is defined + - item.vlan_id is defined + - item.associated_l3vn is defined + - item.l2vn_pool is defined + - item.l2vn_pool.ipv4_address is defined + - item.l2vn_pool.ipv4_netmask is defined + - (item.l2vn_pool.ipv6_address is not defined) or (item.l2vn_pool.ipv6_netmask is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv4_relay is not defined) or (item.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv4_relay is not defined) or (item.dhcp_relay.dhcpv4_relay.dhcpv4_server_vrf is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv6_relay is not defined) or (item.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip is defined) + - (item.dhcp_relay is not defined) or (item.dhcp_relay.dhcpv6_relay is not defined) or (item.dhcp_relay.dhcpv6_relay.dhcpv6_server_vrf is defined) + fail_msg: >- + L2VN validation failed for index {{ idx }} (name={{ item.l2vn_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ l2vns }}" + loop_control: + index_var: idx + label: "{{ item.l2vn_name | default('UNNAMED') }}" + + - name: Render l2vn_localized_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_l2vn_localized: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_localized_cli.j2') }}" + + - name: Print rendered_l2vn_localized + ansible.builtin.debug: + var: rendered_l2vn_localized + + - name: Create new l2vn localized template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "evpn_l2vn_localized" + template_name: "evpn_l2vn_localized_template" + template_content: "{{ rendered_l2vn_localized }}" + version_description: "Auto-generated BGP EVPN L2VN localized config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "evpn_l2vn_localized" + template_name: "evpn_l2vn_localized_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/l2vn_anycast_cli.j2 b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_cli.j2 new file mode 100644 index 0000000000..769ea38a8f --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_cli.j2 @@ -0,0 +1,48 @@ +{% for l2vn in l2vns %} +l2vpn evpn instance {{ l2vn.vlan_id }} vlan-based + encapsulation vxlan + route-target export {{ asn }}:{{ l2vn.vlan_id }} + route-target import {{ asn }}:{{ l2vn.vlan_id }} + no auto-route-target + replication-type ingress +! +vlan configuration {{ l2vn.vlan_id }} + member evpn-instance {{ l2vn.vlan_id }} vni {{ l2vn.vni }} +! +vlan {{ l2vn.vlan_id }} + name {{ l2vn.l2vn_name }} + no shutdown + exit +! +interface nve1 + member vni {{ l2vn.vni }} ingress-replication +! +interface Vlan{{ l2vn.vlan_id }} + mac-address {{ mac_address }} + vrf forwarding {{ l2vn.associated_l3vn }} + ip address {{ l2vn.l2vn_pool.ipv4_address }} {{ l2vn.l2vn_pool.ipv4_netmask }} +{% if l2vn.l2vn_pool.ipv6_address is defined %} + ipv6 enable + ipv6 address {{ l2vn.l2vn_pool.ipv6_address }}/{{ l2vn.l2vn_pool.ipv6_netmask }} +{% if l2vn.dhcp_relay is defined and l2vn.dhcp_relay.dhcpv6_relay is defined %} +{% if l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_vrf == "global" %} + ipv6 dhcp relay destination global {{ l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip }} +{% else %} + ipv6 dhcp relay destination {{ l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip }} + ipv6 dhcp relay source-interface Loopback5838 +{% endif %} +{% endif %} +{% endif %} + ip pim sparse-mode +{% if l2vn.dhcp_relay is defined and l2vn.dhcp_relay.dhcpv4_relay is defined %} +{% if l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_vrf == "global" %} + ip helper-address global {{ l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip }} +{% else %} + ip helper-address {{ l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip }} + ip dhcp relay source-interface Loopback5838 +{% endif %} +{% endif %} + ip igmp version 3 + exit +! +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l2vn_anycast_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_delete_cli.j2 new file mode 100644 index 0000000000..6f150de6c4 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_delete_cli.j2 @@ -0,0 +1,11 @@ +{% for l2vn in l2vns %} +no vlan configuration {{ l2vn.vlan_id }} +no vlan {{ l2vn.vlan_id }} +no interface Vlan{{ l2vn.vlan_id }} +no l2vpn evpn instance {{ l2vn.vlan_id }} vlan-based +{% endfor %} +interface nve1 +{% for l2vn in l2vns %} + no member vni {{ l2vn.vni }} +{% endfor %} + exit diff --git a/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_cli.j2 b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_cli.j2 new file mode 100644 index 0000000000..40356ac20d --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_cli.j2 @@ -0,0 +1,48 @@ +{% for l2vn in l2vns %} +l2vpn evpn instance {{ l2vn.vlan_id }} vlan-based + encapsulation vxlan + route-target export {{ asn }}:{{ l2vn.vlan_id }} + route-target import {{ asn }}:{{ l2vn.vlan_id }} + no auto-route-target + replication-type static +! +vlan configuration {{ l2vn.vlan_id }} + member evpn-instance {{ l2vn.vlan_id }} vni {{ l2vn.vni }} +! +vlan {{ l2vn.vlan_id }} + name {{ l2vn.l2vn_name }} + no shutdown + exit +! +interface nve1 + member vni {{ l2vn.vni }} mcast-group {{ l2vn.multicast_group }} +! +interface Vlan{{ l2vn.vlan_id }} + mac-address {{ mac_address }} + vrf forwarding {{ l2vn.associated_l3vn }} + ip address {{ l2vn.l2vn_pool.ipv4_address }} {{ l2vn.l2vn_pool.ipv4_netmask }} +{% if l2vn.l2vn_pool.ipv6_address is defined %} + ipv6 enable + ipv6 address {{ l2vn.l2vn_pool.ipv6_address }}/{{ l2vn.l2vn_pool.ipv6_netmask }} +{% if l2vn.dhcp_relay is defined and l2vn.dhcp_relay.dhcpv6_relay is defined %} +{% if l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_vrf == "global" %} + ipv6 dhcp relay destination global {{ l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip }} +{% else %} + ipv6 dhcp relay destination {{ l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip }} + ipv6 dhcp relay source-interface Loopback5838 +{% endif %} +{% endif %} +{% endif %} + ip pim sparse-mode +{% if l2vn.dhcp_relay is defined and l2vn.dhcp_relay.dhcpv4_relay is defined %} +{% if l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_vrf == "global" %} + ip helper-address global {{ l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip }} +{% else %} + ip helper-address {{ l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip }} + ip dhcp relay source-interface Loopback5838 +{% endif %} +{% endif %} + ip igmp version 3 + exit +! +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_delete_cli.j2 new file mode 100644 index 0000000000..a65c3cf402 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_delete_cli.j2 @@ -0,0 +1,11 @@ +{% for l2vn in l2vns %} +no vlan configuration {{ l2vn.vlan_id }} +no vlan {{ l2vn.vlan_id }} +no interface Vlan{{ l2vn.vlan_id }} +no l2vpn evpn instance {{ l2vn.vlan_id }} vlan-based +{% endfor %} +interface nve1 +{% for l2vn in l2vns %} + no member vni {{ l2vn.vni }} mcast-group {{ l2vn.multicast_group }} +{% endfor %} + exit diff --git a/playbooks/bgpevpn/jinja_templates/l2vn_localized_cli.j2 b/playbooks/bgpevpn/jinja_templates/l2vn_localized_cli.j2 new file mode 100644 index 0000000000..75959eb3b5 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l2vn_localized_cli.j2 @@ -0,0 +1,35 @@ +{% for l2vn in l2vns %} +vlan {{ l2vn.vlan_id }} + name {{ l2vn.l2vn_name }} + no shutdown + exit +! +interface Vlan{{ l2vn.vlan_id }} + mac-address {{ mac_address }} + vrf forwarding {{ l2vn.associated_l3vn }} + ip address {{ l2vn.l2vn_pool.ipv4_address }} {{ l2vn.l2vn_pool.ipv4_netmask }} +{% if l2vn.l2vn_pool.ipv6_address is defined %} + ipv6 enable + ipv6 address {{ l2vn.l2vn_pool.ipv6_address }}/{{ l2vn.l2vn_pool.ipv6_netmask }} +{% if l2vn.dhcp_relay is defined and l2vn.dhcp_relay.dhcpv6_relay is defined %} +{% if l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_vrf == "global" %} + ipv6 dhcp relay destination global {{ l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip }} +{% else %} + ipv6 dhcp relay destination {{ l2vn.dhcp_relay.dhcpv6_relay.dhcpv6_server_ip }} + ipv6 dhcp relay source-interface Loopback5838 +{% endif %} +{% endif %} +{% endif %} + ip pim sparse-mode +{% if l2vn.dhcp_relay is defined and l2vn.dhcp_relay.dhcpv4_relay is defined %} +{% if l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_vrf == "global" %} + ip helper-address global {{ l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip }} +{% else %} + ip helper-address {{ l2vn.dhcp_relay.dhcpv4_relay.dhcpv4_server_ip }} + ip dhcp relay source-interface Loopback5838 +{% endif %} +{% endif %} + ip igmp version 3 + exit +! +{% endfor %} \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/l2vn_localized_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l2vn_localized_delete_cli.j2 new file mode 100644 index 0000000000..7dc8ecd0a2 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l2vn_localized_delete_cli.j2 @@ -0,0 +1,4 @@ +{% for l2vn in l2vns %} +no vlan {{ l2vn.vlan_id }} +no interface Vlan{{ l2vn.vlan_id }} +{% endfor %} diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml new file mode 100644 index 0000000000..355cc758ee --- /dev/null +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml @@ -0,0 +1,4 @@ +l2vns: + - l2vn_name: L2VN-11 + vlan_id: 10 + vni: 5001 \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml new file mode 100644 index 0000000000..7f2c834d56 --- /dev/null +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml @@ -0,0 +1,5 @@ +l2vns: + - l2vn_name: L2VN-mcast-12 + vlan_id: 12 + vni: 5001 + multicast_group: 237.1.1.1 \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml new file mode 100644 index 0000000000..1672ee6375 --- /dev/null +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml @@ -0,0 +1,14 @@ +asn: 1001 +mac_address: 00:0c:29:a1:bd:78 +l2vns: + - l2vn_name: L2VN-mcast-12 + vlan_id: 12 + vni: 5001 + associated_l3vn: vrf-1 + multicast_group: 237.1.1.1 + l2vn_pool: + ipv4_address: 12.12.0.1 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2001::1 + ipv6_netmask: 64 + diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml new file mode 100644 index 0000000000..d4d204d695 --- /dev/null +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml @@ -0,0 +1,13 @@ +asn: 1001 +mac_address: 00:0c:29:a1:bd:78 +l2vns: + - l2vn_name: L2VN-11 + vlan_id: 10 + vni: 5001 + associated_l3vn: vrf-1 + l2vn_pool: + ipv4_address: 12.12.0.1 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2001::1 + ipv6_netmask: 64 + diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml new file mode 100644 index 0000000000..83e8999df9 --- /dev/null +++ b/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml @@ -0,0 +1,3 @@ +l2vns: + - l2vn_name: L2VN-loc-10 + vlan_id: 11 \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml new file mode 100644 index 0000000000..97a3ce16e6 --- /dev/null +++ b/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml @@ -0,0 +1,11 @@ +asn: 1001 +mac_address: 00:0c:29:a1:bd:78 +l2vns: + - l2vn_pool: + ipv4_address: 13.13.0.1 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2002::1 + ipv6_netmask: 64 + l2vn_name: L2VN-loc-10 + vlan_id: 11 + associated_l3vn: vrf-1 \ No newline at end of file From 082e8b69cf7838e13abc989e0eaba9c52efa3c30 Mon Sep 17 00:00:00 2001 From: Penke Vivek Raj Date: Thu, 23 Oct 2025 14:09:14 +0530 Subject: [PATCH 16/41] Ansible playbooks and jinja templates for EVPN static host onboarding --- .../static_host_offboarding_cli.j2 | 10 +++ .../static_host_onboarding_cli.j2 | 10 +++ playbooks/bgpevpn/static_host_offboarding.yml | 72 ++++++++++++++++++ playbooks/bgpevpn/static_host_onboarding.yml | 73 +++++++++++++++++++ .../vars/static_host_offboarding_var.yml | 3 + .../vars/static_host_onboarding_var.yml | 4 + 6 files changed, 172 insertions(+) create mode 100644 playbooks/bgpevpn/jinja_templates/static_host_offboarding_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/static_host_onboarding_cli.j2 create mode 100644 playbooks/bgpevpn/static_host_offboarding.yml create mode 100644 playbooks/bgpevpn/static_host_onboarding.yml create mode 100644 playbooks/bgpevpn/vars/static_host_offboarding_var.yml create mode 100644 playbooks/bgpevpn/vars/static_host_onboarding_var.yml diff --git a/playbooks/bgpevpn/jinja_templates/static_host_offboarding_cli.j2 b/playbooks/bgpevpn/jinja_templates/static_host_offboarding_cli.j2 new file mode 100644 index 0000000000..a94111f021 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/static_host_offboarding_cli.j2 @@ -0,0 +1,10 @@ +{% for port in ports %} +interface {{ port.port_name }} + no switchport mode +{% if port.switchport_mode == 'access' %} + no switchport access vlan +{% elif port.switchport_mode == 'trunk' %} + no switchport trunk allowed vlan +{% endif %} + exit +{% endfor %} \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/static_host_onboarding_cli.j2 b/playbooks/bgpevpn/jinja_templates/static_host_onboarding_cli.j2 new file mode 100644 index 0000000000..4945aab7ce --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/static_host_onboarding_cli.j2 @@ -0,0 +1,10 @@ +{% for port in ports %} +interface {{ port.port_name }} + switchport mode {{ port.switchport_mode }} +{% if port.switchport_mode == 'access' %} + switchport access vlan {{ port.access_vlan }} +{% elif port.switchport_mode == 'trunk' %} + switchport trunk allowed vlan {{ port.allowed_vlans }} +{% endif %} + exit +{% endfor %} \ No newline at end of file diff --git a/playbooks/bgpevpn/static_host_offboarding.yml b/playbooks/bgpevpn/static_host_offboarding.yml new file mode 100644 index 0000000000..0d4d010513 --- /dev/null +++ b/playbooks/bgpevpn/static_host_offboarding.yml @@ -0,0 +1,72 @@ +--- +- name: Static Host Offboarding Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/static_host_offboarding_var.yml + + tasks: + - name: Validate top-level Ports input + ansible.builtin.assert: + that: + - ports is defined + - ports is iterable + - ports | length > 0 + fail_msg: "Missing or invalid top-level vars: need non-empty 'ports' list." + + - name: Validate each port entry mandatory fields + ansible.builtin.assert: + that: + - item.port_name is defined + - item.switchport_mode is defined + + fail_msg: >- + Port validation failed for index {{ idx }} (name={{ item.port_name|default('UNNAMED') }}): Required fields missing or malformed. Item={{ item | to_nice_yaml }} + loop: "{{ ports }}" + loop_control: + index_var: idx + label: "{{ item.port_name | default('UNNAMED') }}" + + - name: Render static_host_offboarding_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_static_host_offboarding: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/static_host_offboarding_cli.j2') }}" + + - name: Print rendered_static_host_offboarding + ansible.builtin.debug: + var: rendered_static_host_offboarding + + - name: Create new static host offboarding template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "static_host_offboarding" + template_name: "static_host_offboarding_template" + template_content: "{{ rendered_static_host_offboarding }}" + version_description: "Auto-generated static host offboarding config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "static_host_offboarding" + template_name: "static_host_offboarding_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/static_host_onboarding.yml b/playbooks/bgpevpn/static_host_onboarding.yml new file mode 100644 index 0000000000..d331166a07 --- /dev/null +++ b/playbooks/bgpevpn/static_host_onboarding.yml @@ -0,0 +1,73 @@ +--- +- name: Static Host Onboarding Configuration + hosts: localhost + gather_facts: false + vars_files: + - ../credentials.yml + - ./vars/static_host_onboarding_var.yml + + tasks: + - name: Validate top-level Ports input + ansible.builtin.assert: + that: + - ports is defined + - ports is iterable + - ports | length > 0 + fail_msg: "Missing or invalid top-level vars: need non-empty 'ports' list." + + - name: Validate each port entry mandatory fields + ansible.builtin.assert: + that: + - item.port_name is defined + - item.switchport_mode is defined + - (item.access_vlan is defined and item.allowed_vlans is not defined) or (item.access_vlan is not defined and item.allowed_vlans is defined) + + fail_msg: >- + Port validation failed for index {{ idx }} (name={{ item.port_name|default('UNNAMED') }}): Required fields missing or malformed, or both/neither 'access_vlan' and 'allowed_vlans' defined. Item={{ item | to_nice_yaml }} + loop: "{{ ports }}" + loop_control: + index_var: idx + label: "{{ item.port_name | default('UNNAMED') }}" + + - name: Render static_host_onboarding_cli.j2 to a variable + ansible.builtin.set_fact: + rendered_static_host_onboarding: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/static_host_onboarding_cli.j2') }}" + + - name: Print rendered_static_host_onboarding + ansible.builtin.debug: + var: rendered_static_host_onboarding + + - name: Create new static host onboarding template. + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: true + state: merged + config: + - configuration_templates: + project_name: "static_host_onboarding" + template_name: "static_host_onboarding_template" + template_content: "{{ rendered_static_host_onboarding }}" + version_description: "Auto-generated static host onboarding config" + language: JINJA + software_type: "IOS-XE" + device_types: + - product_family: Switches and Hubs + - deploy_template: + project_name: "static_host_onboarding" + template_name: "static_host_onboarding_template" + force_push: true + template_parameters: + - param_name: "vlan_id" + param_value: "1431" + - param_name: "vlan_name" + param_value: "testvlan31" + device_details: + device_ips: ["172.27.248.81"] \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/static_host_offboarding_var.yml b/playbooks/bgpevpn/vars/static_host_offboarding_var.yml new file mode 100644 index 0000000000..0298c4ab6e --- /dev/null +++ b/playbooks/bgpevpn/vars/static_host_offboarding_var.yml @@ -0,0 +1,3 @@ +ports: + - port_name: "GigabitEthernet1/0/5" + switchport_mode: "trunk" diff --git a/playbooks/bgpevpn/vars/static_host_onboarding_var.yml b/playbooks/bgpevpn/vars/static_host_onboarding_var.yml new file mode 100644 index 0000000000..7397453f20 --- /dev/null +++ b/playbooks/bgpevpn/vars/static_host_onboarding_var.yml @@ -0,0 +1,4 @@ +ports: + - port_name: "GigabitEthernet1/0/5" + switchport_mode: "trunk" + allowed_vlans: "10,20,30,40,45-60" \ No newline at end of file From 99e5f4aa75b29a54292ff757c39af7750b7a9c81 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam Date: Thu, 23 Oct 2025 19:14:38 +0530 Subject: [PATCH 17/41] Handled delete cases too --- .../bgpevpn/evpn_fabric_delete_deploy.yml | 185 ++++++++++++++++++ playbooks/bgpevpn/evpn_fabric_deploy.yml | 181 +++++++++++++++++ .../bgp_evpn_fabric_border_cli.j2 | 68 +++++++ .../bgp_evpn_fabric_border_delete_cli.j2 | 19 ++ .../bgp_evpn_fabric_leaf_cli.j2 | 62 ++++++ .../bgp_evpn_fabric_leaf_delete_cli.j2 | 19 ++ .../bgp_evpn_fabric_spine_cli.j2 | 44 +++++ .../bgp_evpn_fabric_spine_delete_cli.j2 | 1 + .../vars/fabric_overlay_topology_devices.yml | 14 +- .../fabric_overlay_topology_localdevices.yml | 73 +++++++ 10 files changed, 663 insertions(+), 3 deletions(-) create mode 100644 playbooks/bgpevpn/evpn_fabric_delete_deploy.yml create mode 100644 playbooks/bgpevpn/evpn_fabric_deploy.yml create mode 100644 playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_delete_cli.j2 create mode 100644 playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml diff --git a/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml b/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml new file mode 100644 index 0000000000..945ce420fa --- /dev/null +++ b/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml @@ -0,0 +1,185 @@ +--- +- name: Delete BGP EVPN Fabric Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml + - ../credentials.yml + vars: + output_dir: ./rendered_delete + dnac_credentials_file: ../credentials.yml + + tasks: + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ fabric_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Extract delete templates from role_template_map + ansible.builtin.set_fact: + delete_template_map: >- + {{ role_template_map | dict2items | selectattr('key', 'match', '.*_delete$') | items2dict(key_name='key', value_name='value') }} + + - name: Check delete template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ delete_template_map | dict2items }}" + register: delete_template_stats + + - name: Build missing delete template list + ansible.builtin.set_fact: + missing_delete_templates: >- + {{ delete_template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all delete template files found + ansible.builtin.assert: + that: + - missing_delete_templates | length == 0 + fail_msg: "Missing delete template files: {{ missing_delete_templates }}" + + - name: Build render item pairs (role/device) for delete + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device delete configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render delete configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key + '_delete'] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-delete.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + peer_policy_template: "{{ peer_policy[item.0.key] }}" + peer_session_template: "{{ peer_session[item.0.key] }}" + dhcp_source_interface: "{{ router_id_interface }}" + neighbor: "{{ item.1.neighbors | default([]) }}" + loopback_ip: "{{ item.1.loopback_ip }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered delete files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered delete config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered delete configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_delete_files + + - name: Include DNAC credentials (if available) + ansible.builtin.include_vars: + file: "{{ dnac_credentials_file }}" + ignore_errors: true + + - name: Ensure evpn_delete_items is defined + ansible.builtin.set_fact: + evpn_delete_items: [] + + - name: Define DNAC project name for delete + ansible.builtin.set_fact: + proj_name: BGP_EVPN_FABRIC_DELETE_AUTOGEN + + - name: Push delete configs via DNAC template API + when: dnac_host is defined + block: + - name: Build delete payload list + ansible.builtin.set_fact: + evpn_delete_items: "{{ evpn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-delete\\.cfg$','')), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_delete_files.results }}" + + - name: Debug delete deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-DELETE" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ evpn_delete_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each delete config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ evpn_delete_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-DELETE" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated BGP EVPN fabric delete config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-DELETE" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" + register: dnac_delete_results + + - name: Show DNAC delete push summary + ansible.builtin.debug: + msg: "Deployed {{ dnac_delete_results.results | length }} delete templates via DNAC" \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_fabric_deploy.yml b/playbooks/bgpevpn/evpn_fabric_deploy.yml new file mode 100644 index 0000000000..26076fcb94 --- /dev/null +++ b/playbooks/bgpevpn/evpn_fabric_deploy.yml @@ -0,0 +1,181 @@ +--- +- name: Deploy BGP EVPN Fabric Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml + - ../credentials.yml + vars: + output_dir: ./rendered + dnac_credentials_file: ../credentials.yml + + tasks: + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ fabric_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Check template files exist + ansible.builtin.stat: + path: "{{ item }}" + loop: "{{ role_template_map.values() | list }}" + register: template_stats + + - name: Build missing template list (no jmespath) + ansible.builtin.set_fact: + missing_templates: >- + {{ template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='invocation') + | map(attribute='module_args') + | map(attribute='path') + | list }} + + - name: Assert all template files found + ansible.builtin.assert: + that: + - missing_templates | length == 0 + fail_msg: "Missing template files: {{ missing_templates }}" + + - name: Build render item pairs (role/device) for debug + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render configs for all devices + ansible.builtin.template: + src: "{{ (role_template_map[item.0.key] if (role_template_map is defined and item.0.key in role_template_map) else ('bgp_evpn_fabric_' ~ item.0.key ~ '_cli.j2')) }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + vars: + peer_policy_template: "{{ peer_policy[item.0.key] }}" + peer_session_template: "{{ peer_session[item.0.key] }}" + dhcp_source_interface: "{{ router_id_interface }}" + neighbor: "{{ item.1.neighbors | default([]) }}" + loopback_ip: "{{ item.1.loopback_ip }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_files + + - name: Include DNAC credentials (if available) + ansible.builtin.include_vars: + file: "{{ dnac_credentials_file }}" + ignore_errors: true + + - name: Ensure evpn_push_items is defined + ansible.builtin.set_fact: + evpn_push_items: [] + - name: Push configs via DNAC template API (create ephemeral template per device) + when: dnac_host is defined + block: + - name: Build push payload list + ansible.builtin.set_fact: + evpn_push_items: "{{ evpn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+\\.cfg$','')), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_files.results }}" + + - name: Debug deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-EVPN" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + vars: + proj_name: BGP_EVPN_FABRIC_AUTOGEN + loop: "{{ evpn_push_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each config using template_workflow_manager module + vars: + proj_name: BGP_EVPN_FABRIC_AUTOGEN + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ evpn_push_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ dnac_host }}" + dnac_port: "{{ dnac_port }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_version: "{{ dnac_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-EVPN" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated BGP EVPN fabric config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-EVPN" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" + register: dnac_push_results + + - name: Show DNAC push summary + ansible.builtin.debug: + msg: "Deployed {{ dnac_push_results.results | length }} templates via DNAC" diff --git a/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_cli.j2 new file mode 100644 index 0000000000..6ad60763f7 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_cli.j2 @@ -0,0 +1,68 @@ +interface nve1 + no ip address + source-interface {{ source_interface }} + host-reachability protocol bgp + group-based-policy +! + +ip prefix-list ip_route_map_towards_wan_handoff seq 5 permit 0.0.0.0/0 le 31 +ip prefix-list ip_route_map_towards_wan_handoff seq 10 permit 177.0.0.0/16 ge 17 +ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 5 permit ::/0 le 127 +ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 10 permit 2002:177:0:1::/64 ge 65 +route-map ip_route_map_towards_wan_handoff permit 10 + match ip address prefix-list ip_route_map_towards_wan_handoff +route-map ipv6_route_map_towards_wan_handoff permit 10 + match ipv6 address prefix-list ipv6_route_map_towards_wan_handoff +! +router bgp {{ asn }} + template peer-policy {{ peer_policy_template }} + send-community both + exit-peer-policy + ! + template peer-session {{ peer_session_template }} + remote-as {{ asn }} + log-neighbor-changes + update-source {{ update_source }} + exit-peer-session + ! + bgp router-id interface {{ router_id_interface }} + bgp log-neighbor-changes + bgp graceful-restart + no bgp default ipv4-unicast +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} +{% endfor %} + ! + address-family ipv4 + exit-address-family + ! + address-family ipv4 mvpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family ipv6 mvpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family l2vpn evpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family +! + +ip dhcp relay information option vpn +ip dhcp relay information option +ip dhcp compatibility suboption link-selection standard +ip dhcp compatibility suboption server-override standard +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_delete_cli.j2 new file mode 100644 index 0000000000..f53ea1e28e --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_border_delete_cli.j2 @@ -0,0 +1,19 @@ +no interface nve1 +! + +no ip prefix-list ip_route_map_towards_wan_handoff seq 5 permit 0.0.0.0/0 le 31 +no ip prefix-list ip_route_map_towards_wan_handoff seq 10 permit 177.0.0.0/16 ge 17 +no ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 5 permit ::/0 le 127 +no ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 10 permit 2002:177:0:1::/64 ge 65 +no route-map ip_route_map_towards_wan_handoff permit 10 +no route-map ipv6_route_map_towards_wan_handoff permit 10 +! + +no ip dhcp relay information option vpn +no ip dhcp relay information option +no ip dhcp compatibility suboption link-selection standard +no ip dhcp compatibility suboption server-override standard +! + +no router bgp {{ asn }} +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_cli.j2 new file mode 100644 index 0000000000..155f829871 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_cli.j2 @@ -0,0 +1,62 @@ +interface nve1 + no ip address + source-interface {{ source_interface }} + host-reachability protocol bgp + group-based-policy +! + +router bgp {{ asn }} + template peer-policy {{ peer_policy_template }} + send-community both + exit-peer-policy + ! + template peer-session {{ peer_session_template }} + remote-as {{ asn }} + log-neighbor-changes + update-source {{ update_source }} + exit-peer-session + ! + bgp router-id interface {{ router_id_interface }} + bgp log-neighbor-changes + bgp graceful-restart + no bgp default ipv4-unicast +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} +{% endfor %} + ! + address-family ipv4 + exit-address-family + ! + address-family ipv4 mvpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family ipv6 mvpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family l2vpn evpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + +! + +ip dhcp-relay source-interface {{ dhcp_source_interface }} +ip dhcp relay information option vpn +ip dhcp relay information option +ip dhcp compatibility suboption link-selection standard +ip dhcp compatibility suboption server-override standard +ipv6 dhcp-relay source-interface {{ dhcp_source_interface }} +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_delete_cli.j2 new file mode 100644 index 0000000000..e9efdfb5af --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_leaf_delete_cli.j2 @@ -0,0 +1,19 @@ +no interface nve1 +! +{# l2vpn needed?#} +no l2vpn evpn +! + +no ip dhcp-relay source-interface {{dhcp_source_interface}} +no ip dhcp relay information option vpn +no ip dhcp relay information option +no ip dhcp compatibility suboption link-selection standard +no ip dhcp compatibility suboption server-override standard +! + +no ipv6 dhcp-relay source-interface {{dhcp_source_interface}} +no ipv6 dhcp-relay option vpn +! + +no router bgp {{asn}} +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_cli.j2 b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_cli.j2 new file mode 100644 index 0000000000..11e8420d14 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_cli.j2 @@ -0,0 +1,44 @@ +router bgp {{ asn }} + template peer-policy {{ peer_policy_template }} + route-reflector-client + send-community both + exit-peer-policy + ! + template peer-session {{ peer_session_template }} + remote-as {{ asn }} + log-neighbor-changes + update-source {{ update_source }} + exit-peer-session + ! + bgp router-id interface {{ router_id_interface }} + bgp log-neighbor-changes + bgp graceful-restart + no bgp default ipv4-unicast +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} +{% endfor %} + ! + address-family ipv4 mvpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family ipv6 mvpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family + ! + address-family l2vpn evpn +{% for neighbor_ip in neighbor %} + neighbor {{ neighbor_ip }} activate + neighbor {{ neighbor_ip }} send-community both + neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} +{% endfor %} + exit-address-family +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_delete_cli.j2 new file mode 100644 index 0000000000..bcff2295cd --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/bgp_evpn_fabric_spine_delete_cli.j2 @@ -0,0 +1 @@ +no router bgp {{ asn }} \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml b/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml index 29ecb5b400..d0443f4cd9 100644 --- a/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml +++ b/playbooks/bgpevpn/vars/fabric_overlay_topology_devices.yml @@ -17,6 +17,11 @@ peer_groups: leaf: [] # leaves will peer with spines (populated dynamically if needed) spine: [] # spines will peer with leaves, borders and other spines (populated dynamically if needed) border: [] # borders will peer with spines (populated dynamically if needed) +proj_name: BGP_EVPN_FABRIC_AUTOGEN +soft_type: IOS-XE +state: merged +language: JINJA +product_family: Switches and Hubs # Device inventory grouped by role fabric_devices: @@ -130,6 +135,9 @@ fabric_devices: # Mapping role to template path role_template_map: - leaf: ../jinja_templates/fabric_leaf_cli.j2 - spine: ../jinja_templates/fabric_spine_cli.j2 - border: ../jinja_templates/fabric_border_cli.j2 + leaf: ./jinja_templates/bgp_evpn_fabric_leaf_cli.j2 + spine: ./jinja_templates/bgp_evpn_fabric_spine_cli.j2 + border: ./jinja_templates/bgp_evpn_fabric_border_cli.j2 + leaf_delete: ./jinja_templates/bgp_evpn_fabric_leaf_delete_cli.j2 + spine_delete: ./jinja_templates/bgp_evpn_fabric_spine_delete_cli.j2 + border_delete: ./jinja_templates/bgp_evpn_fabric_border_delete_cli.j2 diff --git a/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml b/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml new file mode 100644 index 0000000000..2213c9f330 --- /dev/null +++ b/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml @@ -0,0 +1,73 @@ +--- +# Global fabric BGP EVPN VxLAN parameters +asn: 1001 +peer_policy: + leaf: leaf_peer_policy_template + spine: spine_peer_policy_template + border: border_peer_policy_template +peer_session: + leaf: leaf_peer_session_template + spine: spine_peer_session_template + border: border_peer_session_template +router_id_interface: Loopback0 +update_source: Loopback0 +source_interface: Loopback0 +peer_groups: + # Each device refers to neighbors by name so we can resolve IP automatically + leaf: [] # leaves will peer with spines (populated dynamically if needed) + spine: [] # spines will peer with leaves, borders and other spines (populated dynamically if needed) + border: [] # borders will peer with spines (populated dynamically if needed) +proj_name: BGP_EVPN_FABRIC_AUTOGEN +soft_type: IOS-XE +state: merged +language: JINJA +product_family: Switches and Hubs + +# Device inventory grouped by role +fabric_devices: + spine: # only one spine group typically + - group_name: spine-group + devices: + - device_name: evpn-app-c9k-29 + mgmt_ip: 172.27.248.81 + mac_address: 00:0c:29:a1:bd:78 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.1.1.1 + neighbors: + - 99.3.3.3 + - 99.4.4.4 + border: # multiple border groups allowed + - group_name: border-group1 + devices: + - device_name: evpn-app-c9k-30 + mgmt_ip: 172.27.248.82 + mac_address: 00:0c:29:c9:ee:a0 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.3.3.3 + neighbors: + - 99.1.1.1 + leaf: # multiple leaf groups allowed + - group_name: leaf-group1 + devices: + - device_name: evpn-app-c9k-31 + mgmt_ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.4.4.4 + neighbors: + - 99.1.1.1 + +# Mapping role to template path +role_template_map: + leaf: ./jinja_templates/bgp_evpn_fabric_leaf_cli.j2 + spine: ./jinja_templates/bgp_evpn_fabric_spine_cli.j2 + border: ./jinja_templates/bgp_evpn_fabric_border_cli.j2 + leaf_delete: ./jinja_templates/bgp_evpn_fabric_leaf_delete_cli.j2 + spine_delete: ./jinja_templates/bgp_evpn_fabric_spine_delete_cli.j2 + border_delete: ./jinja_templates/bgp_evpn_fabric_border_delete_cli.j2 From c94faa89530f1dbb9f7393899f3108aaa59ea0ea Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:18:35 +0530 Subject: [PATCH 18/41] Delete playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 --- .../jinja_templates/fabric_spine_cli.j2 | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 b/playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 deleted file mode 100644 index 0d92abaaac..0000000000 --- a/playbooks/bgpevpn/jinja_templates/fabric_spine_cli.j2 +++ /dev/null @@ -1,44 +0,0 @@ -router bgp {{ asn }} - template peer-policy {{ peer_policy_template }} - route-reflector-client - send-community both - exit-peer-policy - ! - template peer-session {{ peer_session_template }} - remote-as {{ asn }} - log-neighbor-changes - update-source {{ update_source }} - exit-peer-session - ! - bgp router-id interface {{ router_id_interface }} - bgp log-neighbor-changes - bgp graceful-restart - no bgp default ipv4-unicast -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} -{% endfor %} - ! - address-family ipv4 mvpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - ! - address-family ipv6 mvpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - ! - address-family l2vpn evpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family -! From 4bfe899818b245899fb86bb1857d66dfb11ec964 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:18:49 +0530 Subject: [PATCH 19/41] Delete playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 --- .../jinja_templates/fabric_border_cli.j2 | 68 ------------------- 1 file changed, 68 deletions(-) delete mode 100644 playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 deleted file mode 100644 index 4e31fab398..0000000000 --- a/playbooks/bgpevpn/jinja_templates/fabric_border_cli.j2 +++ /dev/null @@ -1,68 +0,0 @@ -interface nve1 - no ip address - source-interface {{ source_interface }} - host-reachability protocol bgp - group-based-policy -! - -ip prefix-list ip_route_map_towards_wan_handoff seq 5 permit 0.0.0.0/0 le 31 -ip prefix-list ip_route_map_towards_wan_handoff seq 10 permit 177.0.0.0/16 ge 17 -ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 5 permit ::/0 le 127 -ipv6 prefix-list ipv6_route_map_towards_wan_handoff seq 10 permit 2002:177:0:1::/64 ge 65 -route-map ip_route_map_towards_wan_handoff permit 10 - match ip address prefix-list ip_route_map_towards_wan_handoff -route-map ipv6_route_map_towards_wan_handoff permit 10 - match ipv6 address prefix-list ipv6_route_map_towards_wan_handoff -! -router bgp {{ asn }} - template peer-policy {{ peer_policy_template }} - send-community both - exit-peer-policy - ! - template peer-session {{ peer_session_template }} - remote-as {{ asn }} - log-neighbor-changes - update-source {{ update_source }} - exit-peer-session - ! - bgp router-id interface {{ router_id_interface }} - bgp log-neighbor-changes - bgp graceful-restart - no bgp default ipv4-unicast -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} -{% endfor %} - ! - address-family ipv4 - exit-address-family - ! - address-family ipv4 mvpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - ! - address-family ipv6 mvpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - ! - address-family l2vpn evpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family -! - -ip dhcp relay information option vpn -ip dhcp relay information option -ip dhcp compatibility suboption link-selection standard -ip dhcp compatibility suboption server-override standard -! From ac566bb0b4e87ef51358e075b14d0fdd62c4bb3a Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:19:02 +0530 Subject: [PATCH 20/41] Delete playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 --- .../jinja_templates/fabric_leaf_cli.j2 | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 deleted file mode 100644 index 02b739034d..0000000000 --- a/playbooks/bgpevpn/jinja_templates/fabric_leaf_cli.j2 +++ /dev/null @@ -1,62 +0,0 @@ -interface nve1 - no ip address - source-interface {{ source_interface }} - host-reachability protocol bgp - group-based-policy -! - -router bgp {{ asn }} - template peer-policy {{ peer_policy_template }} - send-community both - exit-peer-policy - ! - template peer-session {{ peer_session_template }} - remote-as {{ asn }} - log-neighbor-changes - update-source {{ update_source }} - exit-peer-session - ! - bgp router-id interface {{ router_id_interface }} - bgp log-neighbor-changes - bgp graceful-restart - no bgp default ipv4-unicast -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} inherit peer-session {{ peer_session_template }} -{% endfor %} - ! - address-family ipv4 - exit-address-family - ! - address-family ipv4 mvpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - ! - address-family ipv6 mvpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - ! - address-family l2vpn evpn -{% for neighbor_ip in neighbors %} - neighbor {{ neighbor_ip }} activate - neighbor {{ neighbor_ip }} send-community both - neighbor {{ neighbor_ip }} inherit peer-policy {{ peer_policy_template }} -{% endfor %} - exit-address-family - -! - -ip dhcp-relay source-interface {{ dhcp_source_interface }} -ip dhcp relay information option vpn -ip dhcp relay information option -ip dhcp compatibility suboption link-selection standard -ip dhcp compatibility suboption server-override standard -ipv6 dhcp-relay source-interface {{ dhcp_source_interface }} -! From 061746f961b7b1a841af2799fb4d1dfd872e5ce5 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:19:50 +0530 Subject: [PATCH 21/41] Delete playbooks/bgpevpn/fabric_workflow_manager.yml --- playbooks/bgpevpn/fabric_workflow_manager.yml | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 playbooks/bgpevpn/fabric_workflow_manager.yml diff --git a/playbooks/bgpevpn/fabric_workflow_manager.yml b/playbooks/bgpevpn/fabric_workflow_manager.yml deleted file mode 100644 index 222c24e949..0000000000 --- a/playbooks/bgpevpn/fabric_workflow_manager.yml +++ /dev/null @@ -1,108 +0,0 @@ ---- -- name: Deploy BGP EVPN Fabric Configuration - hosts: localhost - gather_facts: false - vars_files: - - ./vars/fabric_overlay_topology_devices.yml - vars: - output_dir: ./rendered - dnac_credentials_file: ../credentials.yml - - tasks: - - name: Ensure rendered output directory exists - ansible.builtin.file: - path: "{{ output_dir }}" - state: directory - mode: '0755' - - - name: Build flattened role -> devices structure - ansible.builtin.set_fact: - role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" - loop: "{{ fabric_devices | dict2items }}" - loop_control: - loop_var: item - - - name: Render configs for all devices - ansible.builtin.template: - src: "{{ (role_template_map[item.0.key] if (role_template_map is defined and item.0.key in role_template_map) else ('bgp_evpn_fabric_' ~ item.0.key ~ '_cli.j2')) }}" - dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}.cfg" - with_subelements: - - "{{ role_devices | dict2items }}" - - value - - skip_missing: True - loop_control: - label: "{{ item.1.device_name }} ({{ item.0.key }})" - vars: - peer_policy_template: "{{ peer_policy[item.0.key] }}" - peer_session_template: "{{ peer_session[item.0.key] }}" - dhcp_source_interface: "{{ router_id_interface }}" - neighbors: "{{ item.1.neighbors | default([]) }}" - loopback_ip: "{{ item.1.loopback_ip }}" - device_mgmt_ip: "{{ item.1.mgmt_ip }}" - device_username: "{{ item.1.username }}" - device_password: "{{ item.1.password }}" - - - name: Display rendered files list - ansible.builtin.command: ls -1 {{ output_dir }} - register: ls_output - - - name: Show rendered config file names - ansible.builtin.debug: - var: ls_output.stdout_lines - - - name: Read rendered configs content - ansible.builtin.slurp: - src: "{{ output_dir }}/{{ item }}" - loop: "{{ ls_output.stdout_lines }}" - register: rendered_files - - - name: Include DNAC credentials (if available) - ansible.builtin.include_vars: - file: "{{ dnac_credentials_file }}" - ignore_errors: true - - - name: Ensure evpn_push_items is defined - ansible.builtin.set_fact: - evpn_push_items: [] - - name: Push configs via DNAC template API (create ephemeral template per device) - when: dnac_host is defined - block: - - name: Build push payload list - ansible.builtin.set_fact: - evpn_push_items: "{{ evpn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+\\.cfg$','')), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_files.results }}" - - - name: Deploy each config using evpn_cli_template_workflow_manager module - vars: - proj_name: BGP_EVPN_FABRIC_AUTOGEN - loop: "{{ evpn_push_items }}" - loop_control: - loop_var: cfg - cisco.dnac.template_workflow_manager: - dnac_host: "{{ dnac_host }}" - dnac_port: "{{ dnac_port }}" - dnac_username: "{{ dnac_username }}" - dnac_password: "{{ dnac_password }}" - dnac_verify: "{{ dnac_verify }}" - state: merged - config: - - configuration_templates: - project_name: "{{ proj_name }}" - template_name: "{{ cfg.device_name }}-EVPN" - template_content: "{{ cfg.config }}" - version_description: "Auto-generated BGP EVPN fabric config" - language: JINJA - device_types: - - product_family: Switches and Hubs - # deploy_template: - # project_name: "{{ proj_name }}" - # template_name: "{{ cfg.device_name }}-EVPN" - # force_push: true - # device_details: - # device_hostnames: - # - "{{ cfg.device_name }}" - register: dnac_push_results - - - name: Show DNAC push summary - ansible.builtin.debug: - msg: "Deployed {{ dnac_push_results.results | length }} templates via DNAC" From 61a4a76e08dfb83f61fde36d35470c5bd35c411f Mon Sep 17 00:00:00 2001 From: Penke Vivek Raj Date: Fri, 24 Oct 2025 10:38:23 +0530 Subject: [PATCH 22/41] evpn l2vn playbook changes --- playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml | 4 ++-- playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml | 4 ++-- playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml | 4 ++-- playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml | 4 ++-- playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml | 4 ++-- playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml | 4 ++-- playbooks/bgpevpn/static_host_offboarding.yml | 2 +- playbooks/bgpevpn/static_host_onboarding.yml | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml index 23bd60c746..3fb8656f0d 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml @@ -1,5 +1,5 @@ --- -- name: Deploy BGP EVPN L2VN Configuration +- name: Deploy BGP EVPN L2VN Anycast delete Configuration hosts: localhost gather_facts: false vars_files: @@ -29,7 +29,7 @@ - name: Render l2vn_anycast_delete_cli.j2 to a variable ansible.builtin.set_fact: - rendered_l2vn_anycast_delete: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_delete_cli.j2') }}" + rendered_l2vn_anycast_delete: "{{ lookup('template', './jinja_templates/l2vn_anycast_delete_cli.j2') }}" - name: Print rendered_l2vn_anycast_delete ansible.builtin.debug: diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml index 4d734b4aa1..ecaffcb414 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml @@ -1,5 +1,5 @@ --- -- name: Deploy BGP EVPN L2VN Configuration +- name: Deploy BGP EVPN L2VN Anycast Configuration hosts: localhost gather_facts: false vars_files: @@ -40,7 +40,7 @@ - name: Render l2vn_anycast_cli.j2 to a variable ansible.builtin.set_fact: - rendered_l2vn_anycast: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_cli.j2') }}" + rendered_l2vn_anycast: "{{ lookup('template', './jinja_templates/l2vn_anycast_cli.j2') }}" - name: Print rendered_l2vn_anycast ansible.builtin.debug: diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml index 271a7c37d1..6af18dc58f 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml @@ -1,5 +1,5 @@ --- -- name: Deploy BGP EVPN L2VN Configuration +- name: Deploy BGP EVPN L2VN Anycast Multicast delete Configuration hosts: localhost gather_facts: false vars_files: @@ -30,7 +30,7 @@ - name: Render l2vn_anycast_mcast_delete_cli.j2 to a variable ansible.builtin.set_fact: - rendered_l2vn_anycast_mcast_delete: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_delete_cli.j2') }}" + rendered_l2vn_anycast_mcast_delete: "{{ lookup('template', './jinja_templates/l2vn_anycast_mcast_delete_cli.j2') }}" - name: Print rendered_l2vn_anycast_mcast_delete ansible.builtin.debug: diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml index 387b8b1061..3c18026ecc 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml @@ -1,5 +1,5 @@ --- -- name: Deploy BGP EVPN L2VN Configuration +- name: Deploy BGP EVPN L2VN Anycast Multicast Configuration hosts: localhost gather_facts: false vars_files: @@ -41,7 +41,7 @@ - name: Render l2vn_anycast_mcast_cli.j2 to a variable ansible.builtin.set_fact: - rendered_l2vn_anycast_mcast: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_anycast_mcast_cli.j2') }}" + rendered_l2vn_anycast_mcast: "{{ lookup('template', './jinja_templates/l2vn_anycast_mcast_cli.j2') }}" - name: Print rendered_l2vn_anycast_mcast ansible.builtin.debug: diff --git a/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml index 1341471d7a..820f1afd14 100644 --- a/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml @@ -1,5 +1,5 @@ --- -- name: Deploy BGP EVPN L2VN Configuration +- name: Deploy BGP EVPN L2VN Localized delete Configuration hosts: localhost gather_facts: false vars_files: @@ -28,7 +28,7 @@ - name: Render l2vn_localized_delete_cli.j2 to a variable ansible.builtin.set_fact: - rendered_l2vn_localized_delete: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_localized_delete_cli.j2') }}" + rendered_l2vn_localized_delete: "{{ lookup('template', './jinja_templates/l2vn_localized_delete_cli.j2') }}" - name: Print rendered_l2vn_localized_delete ansible.builtin.debug: diff --git a/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml index b3053e7d00..37cb47ebea 100644 --- a/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml @@ -1,5 +1,5 @@ --- -- name: Deploy BGP EVPN L2VN Configuration +- name: Deploy BGP EVPN L2VN Localized Configuration hosts: localhost gather_facts: false vars_files: @@ -39,7 +39,7 @@ - name: Render l2vn_localized_cli.j2 to a variable ansible.builtin.set_fact: - rendered_l2vn_localized: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/l2vn_localized_cli.j2') }}" + rendered_l2vn_localized: "{{ lookup('template', './jinja_templates/l2vn_localized_cli.j2') }}" - name: Print rendered_l2vn_localized ansible.builtin.debug: diff --git a/playbooks/bgpevpn/static_host_offboarding.yml b/playbooks/bgpevpn/static_host_offboarding.yml index 0d4d010513..105fb9515b 100644 --- a/playbooks/bgpevpn/static_host_offboarding.yml +++ b/playbooks/bgpevpn/static_host_offboarding.yml @@ -30,7 +30,7 @@ - name: Render static_host_offboarding_cli.j2 to a variable ansible.builtin.set_fact: - rendered_static_host_offboarding: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/static_host_offboarding_cli.j2') }}" + rendered_static_host_offboarding: "{{ lookup('template', './jinja_templates/static_host_offboarding_cli.j2') }}" - name: Print rendered_static_host_offboarding ansible.builtin.debug: diff --git a/playbooks/bgpevpn/static_host_onboarding.yml b/playbooks/bgpevpn/static_host_onboarding.yml index d331166a07..f6b7c8cec1 100644 --- a/playbooks/bgpevpn/static_host_onboarding.yml +++ b/playbooks/bgpevpn/static_host_onboarding.yml @@ -31,7 +31,7 @@ - name: Render static_host_onboarding_cli.j2 to a variable ansible.builtin.set_fact: - rendered_static_host_onboarding: "{{ lookup('template', '/Users/vpenke/ansible/dev/collections/ansible_collections/cisco/dnac/playbooks/bgpevpn/jinja_templates/static_host_onboarding_cli.j2') }}" + rendered_static_host_onboarding: "{{ lookup('template', './jinja_templates/static_host_onboarding_cli.j2') }}" - name: Print rendered_static_host_onboarding ansible.builtin.debug: From 7b14a51ddadf972515c7ed51b7a363242275c74f Mon Sep 17 00:00:00 2001 From: Penke Vivek Raj Date: Mon, 27 Oct 2025 15:07:10 +0530 Subject: [PATCH 23/41] added mutiple device support for l2vn --- .../bgpevpn/evpn_l2vn_anycast_deploy.yml | 38 +++++++++++-------- .../evpn_l2vn_anycast_mcast_deploy.yml | 36 +++++++++++------- .../bgpevpn/evpn_l2vn_localized_deploy.yml | 36 +++++++++++------- .../vars/evpn_l2vn_anycast_mcast_var.yml | 31 ++++++++++----- .../bgpevpn/vars/evpn_l2vn_anycast_var.yml | 30 +++++++++++---- .../bgpevpn/vars/evpn_l2vn_localized_var.yml | 16 +++++++- 6 files changed, 125 insertions(+), 62 deletions(-) diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml index ecaffcb414..94be0f5d0f 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_deploy.yml @@ -10,7 +10,7 @@ ansible.builtin.assert: that: - asn is defined - - mac_address is defined + - devices is defined and devices|length > 0 - l2vns is defined - l2vns is iterable - l2vns | length > 0 @@ -38,15 +38,20 @@ index_var: idx label: "{{ item.l2vn_name | default('UNNAMED') }}" - - name: Render l2vn_anycast_cli.j2 to a variable - ansible.builtin.set_fact: - rendered_l2vn_anycast: "{{ lookup('template', './jinja_templates/l2vn_anycast_cli.j2') }}" - - - name: Print rendered_l2vn_anycast - ansible.builtin.debug: - var: rendered_l2vn_anycast + - name: Validate per-device structure + ansible.builtin.assert: + that: + - item.ip is defined + - item.mac_address is defined + loop: "{{ devices | default([]) }}" + loop_control: + label: "{{ item.ip }}" + when: devices is defined - - name: Create new l2vn anycast template. + - name: Deploy L2VN anycast config to each device + vars: + mac_address: "{{ item.mac_address }}" + rendered_l2vn_anycast: "{{ lookup('template', './jinja_templates/l2vn_anycast_cli.j2') }}" cisco.dnac.template_workflow_manager: dnac_host: "{{ dnac_host }}" dnac_port: "{{ dnac_port }}" @@ -62,21 +67,24 @@ config: - configuration_templates: project_name: "evpn_l2vn_anycast" - template_name: "evpn_l2vn_anycast_template" + template_name: "evpn_l2vn_anycast_template_{{ item.ip }}" template_content: "{{ rendered_l2vn_anycast }}" - version_description: "Auto-generated BGP EVPN L2VN anycast config" + version_description: "Auto-generated BGP EVPN L2VN anycast config for {{ item.ip }}" language: JINJA software_type: "IOS-XE" device_types: - product_family: Switches and Hubs - deploy_template: project_name: "evpn_l2vn_anycast" - template_name: "evpn_l2vn_anycast_template" + template_name: "evpn_l2vn_anycast_template_{{ item.ip }}" force_push: true template_parameters: - - param_name: "vlan_id" + - param_name: "vlan_id1" param_value: "1431" - - param_name: "vlan_name" + - param_name: "vlan_name1" param_value: "testvlan31" device_details: - device_ips: ["172.27.248.81"] \ No newline at end of file + device_ips: ["{{ item.ip }}"] + loop: "{{ devices | default([{'ip': device_ip, 'mac_address': mac_address}]) }}" + loop_control: + label: "{{ item.ip }}" \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml index 3c18026ecc..18cfafaf22 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_deploy.yml @@ -10,11 +10,11 @@ ansible.builtin.assert: that: - asn is defined - - mac_address is defined + - devices is defined and devices|length > 0 - l2vns is defined - l2vns is iterable - l2vns | length > 0 - fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list." + fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list (and either mac_address or devices list)." - name: Validate each L2VN entry mandatory fields ansible.builtin.assert: @@ -39,15 +39,20 @@ index_var: idx label: "{{ item.l2vn_name | default('UNNAMED') }}" - - name: Render l2vn_anycast_mcast_cli.j2 to a variable - ansible.builtin.set_fact: - rendered_l2vn_anycast_mcast: "{{ lookup('template', './jinja_templates/l2vn_anycast_mcast_cli.j2') }}" - - - name: Print rendered_l2vn_anycast_mcast - ansible.builtin.debug: - var: rendered_l2vn_anycast_mcast + - name: Validate per-device structure + ansible.builtin.assert: + that: + - item.ip is defined + - item.mac_address is defined + loop: "{{ devices | default([]) }}" + loop_control: + label: "{{ item.ip }}" + when: devices is defined - - name: Create new l2vn anycast multicast template. + - name: Deploy L2VN anycast multicast config to each device + vars: + mac_address: "{{ item.mac_address }}" + rendered_l2vn_anycast_mcast: "{{ lookup('template', './jinja_templates/l2vn_anycast_mcast_cli.j2') }}" cisco.dnac.template_workflow_manager: dnac_host: "{{ dnac_host }}" dnac_port: "{{ dnac_port }}" @@ -63,16 +68,16 @@ config: - configuration_templates: project_name: "evpn_l2vn_anycast_mcast" - template_name: "evpn_l2vn_anycast_mcast_template" + template_name: "evpn_l2vn_anycast_mcast_template_{{ item.ip }}" template_content: "{{ rendered_l2vn_anycast_mcast }}" - version_description: "Auto-generated BGP EVPN L2VN anycast multicast config" + version_description: "Auto-generated BGP EVPN L2VN anycast multicast config for {{ item.ip }}" language: JINJA software_type: "IOS-XE" device_types: - product_family: Switches and Hubs - deploy_template: project_name: "evpn_l2vn_anycast_mcast" - template_name: "evpn_l2vn_anycast_mcast_template" + template_name: "evpn_l2vn_anycast_mcast_template_{{ item.ip }}" force_push: true template_parameters: - param_name: "vlan_id" @@ -80,4 +85,7 @@ - param_name: "vlan_name" param_value: "testvlan31" device_details: - device_ips: ["172.27.248.81"] \ No newline at end of file + device_ips: ["{{ item.ip }}"] + loop: "{{ devices | default([{'ip': device_ip, 'mac_address': mac_address}]) }}" + loop_control: + label: "{{ item.ip }}" \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml index 37cb47ebea..2a8efc8b99 100644 --- a/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_localized_deploy.yml @@ -10,11 +10,11 @@ ansible.builtin.assert: that: - asn is defined - - mac_address is defined + - devices is defined and devices|length > 0 - l2vns is defined - l2vns is iterable - l2vns | length > 0 - fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list." + fail_msg: "Missing or invalid top-level vars: need 'asn' and non-empty 'l2vns' list (and either mac_address or devices list)." - name: Validate each L2VN entry mandatory fields ansible.builtin.assert: @@ -37,15 +37,20 @@ index_var: idx label: "{{ item.l2vn_name | default('UNNAMED') }}" - - name: Render l2vn_localized_cli.j2 to a variable - ansible.builtin.set_fact: - rendered_l2vn_localized: "{{ lookup('template', './jinja_templates/l2vn_localized_cli.j2') }}" - - - name: Print rendered_l2vn_localized - ansible.builtin.debug: - var: rendered_l2vn_localized + - name: Validate per-device structure + ansible.builtin.assert: + that: + - item.ip is defined + - item.mac_address is defined + loop: "{{ devices | default([]) }}" + loop_control: + label: "{{ item.ip }}" + when: devices is defined - - name: Create new l2vn localized template. + - name: Deploy L2VN localized config to each device + vars: + mac_address: "{{ item.mac_address }}" + rendered_l2vn_localized: "{{ lookup('template', './jinja_templates/l2vn_localized_cli.j2') }}" cisco.dnac.template_workflow_manager: dnac_host: "{{ dnac_host }}" dnac_port: "{{ dnac_port }}" @@ -61,16 +66,16 @@ config: - configuration_templates: project_name: "evpn_l2vn_localized" - template_name: "evpn_l2vn_localized_template" + template_name: "evpn_l2vn_localized_template_{{ item.ip }}" template_content: "{{ rendered_l2vn_localized }}" - version_description: "Auto-generated BGP EVPN L2VN localized config" + version_description: "Auto-generated BGP EVPN L2VN localized config for {{ item.ip }}" language: JINJA software_type: "IOS-XE" device_types: - product_family: Switches and Hubs - deploy_template: project_name: "evpn_l2vn_localized" - template_name: "evpn_l2vn_localized_template" + template_name: "evpn_l2vn_localized_template_{{ item.ip }}" force_push: true template_parameters: - param_name: "vlan_id" @@ -78,4 +83,7 @@ - param_name: "vlan_name" param_value: "testvlan31" device_details: - device_ips: ["172.27.248.81"] \ No newline at end of file + device_ips: ["{{ item.ip }}"] + loop: "{{ devices | default([{'ip': device_ip, 'mac_address': mac_address}]) }}" + loop_control: + label: "{{ item.ip }}" \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml index 1672ee6375..3bff1663d3 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml @@ -1,14 +1,27 @@ asn: 1001 -mac_address: 00:0c:29:a1:bd:78 l2vns: - - l2vn_name: L2VN-mcast-12 - vlan_id: 12 - vni: 5001 + - l2vn_name: L2VN-20 + vlan_id: 20 + vni: 5020 associated_l3vn: vrf-1 multicast_group: 237.1.1.1 l2vn_pool: - ipv4_address: 12.12.0.1 - ipv4_netmask: 255.255.0.0 - ipv6_address: 2001::1 - ipv6_netmask: 64 - + ipv4_address: 14.14.0.1 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2004::1 + ipv6_netmask: 64 + - l2vn_name: L2VN-30 + vlan_id: 30 + vni: 5030 + associated_l3vn: vrf-1 + multicast_group: 237.1.1.2 + l2vn_pool: + ipv4_address: 15.15.0.2 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2005::2 + ipv6_netmask: 64 +devices: + - ip: 172.27.248.81 + mac_address: 00:0c:29:a1:bd:78 + - ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml index d4d204d695..3e094b3d9a 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml @@ -1,13 +1,27 @@ asn: 1001 -mac_address: 00:0c:29:a1:bd:78 l2vns: - - l2vn_name: L2VN-11 - vlan_id: 10 - vni: 5001 + - l2vn_name: L2VN-20 + vlan_id: 20 + vni: 5020 associated_l3vn: vrf-1 l2vn_pool: - ipv4_address: 12.12.0.1 - ipv4_netmask: 255.255.0.0 - ipv6_address: 2001::1 - ipv6_netmask: 64 + ipv4_address: 14.14.0.1 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2004::1 + ipv6_netmask: 64 + - l2vn_name: L2VN-30 + vlan_id: 30 + vni: 5030 + associated_l3vn: vrf-1 + l2vn_pool: + ipv4_address: 15.15.0.2 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2005::2 + ipv6_netmask: 64 +devices: + - ip: 172.27.248.81 + mac_address: 00:0c:29:a1:bd:78 + - ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 + diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml index 97a3ce16e6..22a017d6eb 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml @@ -1,5 +1,4 @@ asn: 1001 -mac_address: 00:0c:29:a1:bd:78 l2vns: - l2vn_pool: ipv4_address: 13.13.0.1 @@ -8,4 +7,17 @@ l2vns: ipv6_netmask: 64 l2vn_name: L2VN-loc-10 vlan_id: 11 - associated_l3vn: vrf-1 \ No newline at end of file + associated_l3vn: vrf-1 + - l2vn_pool: + ipv4_address: 14.14.0.2 + ipv4_netmask: 255.255.0.0 + ipv6_address: 2003::2 + ipv6_netmask: 64 + l2vn_name: L2VN-loc-20 + vlan_id: 12 + associated_l3vn: vrf-1 +devices: + - ip: 172.27.248.81 + mac_address: 00:0c:29:a1:bd:78 + - ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 \ No newline at end of file From 7abd0b898cbc019e9a3511a53067abb8accdc64d Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam Date: Tue, 28 Oct 2025 15:02:13 +0530 Subject: [PATCH 24/41] bug fixes --- .../bgpevpn/evpn_fabric_delete_deploy.yml | 57 +++-- playbooks/bgpevpn/evpn_fabric_deploy.yml | 55 +++-- .../l3vn_unicast_border_cli.j2 | 107 +++++++++ .../l3vn_unicast_border_delete_cli.j2 | 39 ++++ .../jinja_templates/l3vn_unicast_leaf_cli.j2 | 107 +++++++++ .../l3vn_unicast_leaf_delete_cli.j2 | 39 ++++ .../bgpevpn/l3vn_unicast_delete_deploy.yml | 215 ++++++++++++++++++ playbooks/bgpevpn/l3vn_unicast_deploy.yml | 210 +++++++++++++++++ .../fabric_overlay_topology_localdevices.yml | 42 +++- .../vars/l3vn_unicast_topology_vars.yml | 173 ++++++++++++++ 10 files changed, 1010 insertions(+), 34 deletions(-) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_delete_cli.j2 create mode 100644 playbooks/bgpevpn/l3vn_unicast_delete_deploy.yml create mode 100644 playbooks/bgpevpn/l3vn_unicast_deploy.yml create mode 100644 playbooks/bgpevpn/vars/l3vn_unicast_topology_vars.yml diff --git a/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml b/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml index 945ce420fa..851ff8c5f1 100644 --- a/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_fabric_delete_deploy.yml @@ -6,19 +6,31 @@ - ./vars/fabric_overlay_topology_localdevices.yml - ../credentials.yml vars: - output_dir: ./rendered_delete - dnac_credentials_file: ../credentials.yml + output_dir: ./fabric_rendered_delete + catc_credentials_file: ../credentials.yml tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./fabric_rendered_delete" + - name: Ensure rendered output directory exists ansible.builtin.file: path: "{{ output_dir }}" state: directory mode: '0755' + - name: Initialize empty role_devices structure + ansible.builtin.set_fact: + role_devices: {} + - name: Build flattened role -> devices structure ansible.builtin.set_fact: - role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + role_devices: "{{ role_devices | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" loop: "{{ fabric_devices | dict2items }}" loop_control: loop_var: item @@ -109,21 +121,21 @@ loop: "{{ ls_output.stdout_lines }}" register: rendered_delete_files - - name: Include DNAC credentials (if available) + - name: Include Catalyst Center credentials (if available) ansible.builtin.include_vars: - file: "{{ dnac_credentials_file }}" + file: "{{ catc_credentials_file }}" ignore_errors: true - name: Ensure evpn_delete_items is defined ansible.builtin.set_fact: evpn_delete_items: [] - - name: Define DNAC project name for delete + - name: Define Catalyst Center project name for delete ansible.builtin.set_fact: proj_name: BGP_EVPN_FABRIC_DELETE_AUTOGEN - - name: Push delete configs via DNAC template API - when: dnac_host is defined + - name: Push delete configs via Catalyst Center template API + when: catc_host is defined block: - name: Build delete payload list ansible.builtin.set_fact: @@ -151,12 +163,12 @@ loop_control: loop_var: cfg cisco.dnac.template_workflow_manager: - dnac_host: "{{ dnac_host }}" - dnac_port: "{{ dnac_port }}" - dnac_username: "{{ dnac_username }}" - dnac_password: "{{ dnac_password }}" - dnac_verify: "{{ dnac_verify }}" - dnac_version: "{{ dnac_version }}" + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" state: "{{ state }}" config: - configuration_templates: @@ -178,8 +190,19 @@ template_parameters: - param_name: "device_name" param_value: "{{ cfg.device_name }}" - register: dnac_delete_results + register: catc_delete_results + + - name: Show Catalyst Center delete push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_delete_results.results | length }} delete templates via Catalyst Center" - - name: Show DNAC delete push summary + - name: Preserve rendered delete files for debugging (optional) ansible.builtin.debug: - msg: "Deployed {{ dnac_delete_results.results | length }} delete templates via DNAC" \ No newline at end of file + msg: "Rendered delete config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered delete directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_fabric_deploy.yml b/playbooks/bgpevpn/evpn_fabric_deploy.yml index 26076fcb94..a0b221c14f 100644 --- a/playbooks/bgpevpn/evpn_fabric_deploy.yml +++ b/playbooks/bgpevpn/evpn_fabric_deploy.yml @@ -6,19 +6,31 @@ - ./vars/fabric_overlay_topology_localdevices.yml - ../credentials.yml vars: - output_dir: ./rendered - dnac_credentials_file: ../credentials.yml + output_dir: ./fabric_rendered + catc_credentials_file: ../credentials.yml tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./fabric_rendered" + - name: Ensure rendered output directory exists ansible.builtin.file: path: "{{ output_dir }}" state: directory mode: '0755' + - name: Initialize empty role_devices structure + ansible.builtin.set_fact: + role_devices: {} + - name: Build flattened role -> devices structure ansible.builtin.set_fact: - role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + role_devices: "{{ role_devices | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" loop: "{{ fabric_devices | dict2items }}" loop_control: loop_var: item @@ -107,16 +119,16 @@ loop: "{{ ls_output.stdout_lines }}" register: rendered_files - - name: Include DNAC credentials (if available) + - name: Include Catalyst Center credentials (if available) ansible.builtin.include_vars: - file: "{{ dnac_credentials_file }}" + file: "{{ catc_credentials_file }}" ignore_errors: true - name: Ensure evpn_push_items is defined ansible.builtin.set_fact: evpn_push_items: [] - - name: Push configs via DNAC template API (create ephemeral template per device) - when: dnac_host is defined + - name: Push configs via Catalyst Center template API (create ephemeral template per device) + when: catc_host is defined block: - name: Build push payload list ansible.builtin.set_fact: @@ -147,12 +159,12 @@ loop_control: loop_var: cfg cisco.dnac.template_workflow_manager: - dnac_host: "{{ dnac_host }}" - dnac_port: "{{ dnac_port }}" - dnac_username: "{{ dnac_username }}" - dnac_password: "{{ dnac_password }}" - dnac_verify: "{{ dnac_verify }}" - dnac_version: "{{ dnac_version }}" + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" state: "{{ state }}" config: - configuration_templates: @@ -174,8 +186,19 @@ template_parameters: - param_name: "device_name" param_value: "{{ cfg.device_name }}" - register: dnac_push_results + register: catc_push_results - - name: Show DNAC push summary + - name: Show Catalyst Center push summary ansible.builtin.debug: - msg: "Deployed {{ dnac_push_results.results | length }} templates via DNAC" + msg: "Deployed {{ catc_push_results.results | length }} templates via Catalyst Center" + + - name: Preserve rendered files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered output directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 index 8b13789179..cca615d4eb 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_cli.j2 @@ -1 +1,108 @@ +{% for l3vn in l3vns %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ l3vn.rd }} + address-family ipv4 +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} + exit-address-family + + address-family ipv6 +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} + exit-address-family +! + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +! +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit +! + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Loopback Interface for L3VN using IP pools #} +{% if l3vn.ip_pools is defined and l3vn.ip_pools|length > 0 %} +{% for pool in l3vn.ip_pools %} +interface Loopback{{ l3vn.loopback_id }}{{ loop.index }} + vrf forwarding {{ l3vn.vrf_name }} + ip address {{ pool.network_v4 }} {{ pool.subnet_v4 }} + {% if l3vn.ipv6_pools is defined and l3vn.ipv6_pools|length > 0 %} + {% for ipv6_pool in l3vn.ipv6_pools %} + + ipv6 address {{ ipv6_pool.network | regex_replace('::/\d+', '::1/128') }} + ipv6 enable + {% endfor %} + {% endif %} + exit +! +{% endfor %} +{% endif %} + +{# SVI Interface #} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} + {% if l3vn.ipv6_unicast %} + + ipv6 enable + {% endif %} + + ip unnumbered Loopback0 + ip pim sparse-mode + no autostate + exit +! + +{% endfor %} + +{# BGP Configuration for all L3VNs #} +router bgp {{ asn }} +{% for l3vn in l3vns %} +{% if l3vn.ipv4_unicast %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn + maximum-paths eibgp 4 +{% for redist in l3vn.redistribute %} + redistribute {{ redist }} +{% endfor %} + exit-address-family +{% endif %} +{% if l3vn.ipv6_unicast %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn + maximum-paths eibgp 4 +{% for redist in l3vn.redistribute %} + redistribute {{ redist }} +{% endfor %} + exit-address-family +{% endif %} +{% endfor %} + exit +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_delete_cli.j2 new file mode 100644 index 0000000000..e60cd81a96 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_border_delete_cli.j2 @@ -0,0 +1,39 @@ +{% for l3vn in l3vns %} +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.ipv4_unicast %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.ipv6_unicast %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} +! + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Loopback Interfaces for L3VN IP pools #} +{% if l3vn.ip_pools is defined and l3vn.ip_pools|length > 0 %} +{% for pool in l3vn.ip_pools %} +no interface Loopback{{ l3vn.loopback_id }}{{ loop.index }} +! +{% endfor %} +{% endif %} + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 index 8b13789179..cca615d4eb 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_cli.j2 @@ -1 +1,108 @@ +{% for l3vn in l3vns %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ l3vn.rd }} + address-family ipv4 +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} + exit-address-family + + address-family ipv6 +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} + exit-address-family +! + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +! +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit +! + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Loopback Interface for L3VN using IP pools #} +{% if l3vn.ip_pools is defined and l3vn.ip_pools|length > 0 %} +{% for pool in l3vn.ip_pools %} +interface Loopback{{ l3vn.loopback_id }}{{ loop.index }} + vrf forwarding {{ l3vn.vrf_name }} + ip address {{ pool.network_v4 }} {{ pool.subnet_v4 }} + {% if l3vn.ipv6_pools is defined and l3vn.ipv6_pools|length > 0 %} + {% for ipv6_pool in l3vn.ipv6_pools %} + + ipv6 address {{ ipv6_pool.network | regex_replace('::/\d+', '::1/128') }} + ipv6 enable + {% endfor %} + {% endif %} + exit +! +{% endfor %} +{% endif %} + +{# SVI Interface #} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} + {% if l3vn.ipv6_unicast %} + + ipv6 enable + {% endif %} + + ip unnumbered Loopback0 + ip pim sparse-mode + no autostate + exit +! + +{% endfor %} + +{# BGP Configuration for all L3VNs #} +router bgp {{ asn }} +{% for l3vn in l3vns %} +{% if l3vn.ipv4_unicast %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn + maximum-paths eibgp 4 +{% for redist in l3vn.redistribute %} + redistribute {{ redist }} +{% endfor %} + exit-address-family +{% endif %} +{% if l3vn.ipv6_unicast %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn + maximum-paths eibgp 4 +{% for redist in l3vn.redistribute %} + redistribute {{ redist }} +{% endfor %} + exit-address-family +{% endif %} +{% endfor %} + exit +! \ No newline at end of file diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_delete_cli.j2 new file mode 100644 index 0000000000..e60cd81a96 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_unicast_leaf_delete_cli.j2 @@ -0,0 +1,39 @@ +{% for l3vn in l3vns %} +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.ipv4_unicast %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.ipv6_unicast %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} +! + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Loopback Interfaces for L3VN IP pools #} +{% if l3vn.ip_pools is defined and l3vn.ip_pools|length > 0 %} +{% for pool in l3vn.ip_pools %} +no interface Loopback{{ l3vn.loopback_id }}{{ loop.index }} +! +{% endfor %} +{% endif %} + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_unicast_delete_deploy.yml b/playbooks/bgpevpn/l3vn_unicast_delete_deploy.yml new file mode 100644 index 0000000000..5a8012860c --- /dev/null +++ b/playbooks/bgpevpn/l3vn_unicast_delete_deploy.yml @@ -0,0 +1,215 @@ +--- +- name: Delete L3VN Unicast Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_unicast_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_unicast_delete + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_unicast_delete" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Initialize empty role_devices structure + ansible.builtin.set_fact: + role_devices: {} + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Extract delete templates from role_template_map + ansible.builtin.set_fact: + delete_template_map: >- + {{ role_template_map | dict2items | selectattr('key', 'match', '.*_delete$') | items2dict(key_name='key', value_name='value') }} + + - name: Check delete template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ delete_template_map | dict2items }}" + register: delete_template_stats + + - name: Build missing delete template list + ansible.builtin.set_fact: + missing_delete_templates: >- + {{ delete_template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all delete template files found + ansible.builtin.assert: + that: + - missing_delete_templates | length == 0 + fail_msg: "Missing delete template files: {{ missing_delete_templates }}" + + - name: Build render item pairs (role/device) for delete + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN unicast delete configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN unicast delete configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key + '_delete'] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-unicast-delete.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered delete files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered delete config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered delete configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_delete_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_delete_items is defined + ansible.builtin.set_fact: + l3vn_delete_items: [] + + - name: Define Catalyst Center project name for delete + ansible.builtin.set_fact: + proj_name: L3VN_UNICAST_DELETE_AUTOGEN + + - name: Push delete configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build delete payload list from L3VN devices + ansible.builtin.set_fact: + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_delete_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-unicast-delete\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + + - name: Debug delete deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-UNICAST-DELETE" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each delete config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-UNICAST-DELETE" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN unicast delete config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-UNICAST-DELETE" + force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" + register: catc_delete_results + + - name: Show Catalyst Center delete push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_delete_results.results | length }} L3VN unicast delete templates via Catalyst Center" + + - name: Preserve rendered delete files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered delete config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered delete directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_unicast_deploy.yml b/playbooks/bgpevpn/l3vn_unicast_deploy.yml new file mode 100644 index 0000000000..0b8db46904 --- /dev/null +++ b/playbooks/bgpevpn/l3vn_unicast_deploy.yml @@ -0,0 +1,210 @@ +--- +- name: Deploy L3VN Unicast Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_unicast_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_unicast + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_unicast" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Initialize empty role_devices structure + ansible.builtin.set_fact: + role_devices: {} + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Check template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ role_template_map | dict2items }}" + register: template_stats + + - name: Build missing template list + ansible.builtin.set_fact: + missing_templates: >- + {{ template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all template files found + ansible.builtin.assert: + that: + - missing_templates | length == 0 + fail_msg: "Missing template files: {{ missing_templates }}" + + - name: Build render item pairs (role/device) + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN unicast configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN unicast configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-unicast.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_push_items is defined + ansible.builtin.set_fact: + l3vn_push_items: [] + + - name: Define Catalyst Center project name + ansible.builtin.set_fact: + proj_name: L3VN_UNICAST_AUTOGEN + + - name: Push configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build push payload list from L3VN devices + ansible.builtin.set_fact: + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-unicast\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + + - name: Debug deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-UNICAST" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-UNICAST" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN unicast config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-UNICAST" + force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" + register: catc_push_results + + - name: Show Catalyst Center push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_push_results.results | length }} L3VN unicast templates via Catalyst Center" + + - name: Preserve rendered files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered output directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml b/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml index 2213c9f330..da34133513 100644 --- a/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml +++ b/playbooks/bgpevpn/vars/fabric_overlay_topology_localdevices.yml @@ -23,7 +23,7 @@ state: merged language: JINJA product_family: Switches and Hubs -# Device inventory grouped by role +# Device inventory grouped by role for BGP EVPN Fabric fabric_devices: spine: # only one spine group typically - group_name: spine-group @@ -38,6 +38,22 @@ fabric_devices: neighbors: - 99.3.3.3 - 99.4.4.4 + - 99.5.5.5 + - 99.6.6.6 + - 99.2.2.2 + - device_name: evpn-app-c9k-27 + mgmt_ip: 172.27.248.223 + mac_address: 00:0c:29:82:f9:62 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.6.6.6 + neighbors: + - 99.3.3.3 + - 99.4.4.4 + - 99.1.1.1 + - 99.5.5.5 + - 99.2.2.2 border: # multiple border groups allowed - group_name: border-group1 devices: @@ -50,6 +66,7 @@ fabric_devices: loopback_ip: 99.3.3.3 neighbors: - 99.1.1.1 + - 99.6.6.6 leaf: # multiple leaf groups allowed - group_name: leaf-group1 devices: @@ -62,6 +79,29 @@ fabric_devices: loopback_ip: 99.4.4.4 neighbors: - 99.1.1.1 + - 99.6.6.6 + - group_name: leaf-group2 + devices: + - device_name: evpn-app-c9k-28 + mgmt_ip: 172.27.248.224 + mac_address: 00:0c:29:4e:63:a9 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.2.2.2 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - device_name: evpn-app-vm26 + mgmt_ip: 172.27.248.222 + mac_address: 00:0c:29:b4:2b:aa + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.5.5.5 + neighbors: + - 99.1.1.1 + - 99.6.6.6 # Mapping role to template path role_template_map: diff --git a/playbooks/bgpevpn/vars/l3vn_unicast_topology_vars.yml b/playbooks/bgpevpn/vars/l3vn_unicast_topology_vars.yml new file mode 100644 index 0000000000..ce24ee5695 --- /dev/null +++ b/playbooks/bgpevpn/vars/l3vn_unicast_topology_vars.yml @@ -0,0 +1,173 @@ +--- +# L3VN Unicast specific parameters +# Common variables (asn, l3vn_devices, soft_type, state, language, product_family) +# are imported from fabric_overlay_topology_localdevices.yml + +# L3VN Unicast definitions with IP pool details +l3vns: + - l3vn_name: "PROD_VRF" + vrf_name: "PROD" + vlan_id: 4001 + vni: 50001 + rd: "1001:50001" + rt_import: + - "1001:50001" + rt_export: + - "1001:50001" + ipv4_unicast: true + ipv6_unicast: false + redistribute: + - connected + #- static + # IP Pool details for PROD L3VN + ip_pools: + - pool_name: "PROD_POOL_1" + network_v4: "11.11.0.0" + subnet_v4: "255.255.255.255" + gateway: "11.11.0.1" + dhcp_server: "11.11.0.10" + loopback_id: "101" + - l3vn_name: "DEV_VRF" + vrf_name: "DEV" + vlan_id: 4002 + vni: 50002 + rd: "1001:50002" + rt_import: + - "1001:50002" + rt_export: + - "1001:50002" + ipv4_unicast: true + ipv6_unicast: false + redistribute: + - connected + #- static + # IP Pool details for DEV L3VN + ip_pools: + - pool_name: "DEV_POOL_1" + network_v4: "21.11.0.0" + subnet_v4: "255.255.255.255" + gateway: "21.11.0.1" + dhcp_server: "21.11.0.10" + loopback_id: "102" + - l3vn_name: "TEST_VRF" + vrf_name: "TEST" + vlan_id: 4003 + vni: 50003 + rd: "1001:50003" + rt_import: + - "1001:50003" + rt_export: + - "1001:50003" + ipv4_unicast: true + ipv6_unicast: true + redistribute: + - connected + #- static + #- ospf + # IP Pool details for TEST L3VN (dual-stack) + ip_pools: + - pool_name: "TEST_POOL_1" + network_v4: "11.11.0.0" + subnet_v4: "255.255.255.255" + gateway: "11.11.0.1" + dhcp_server: "11.11.0.10" + ipv6_pools: + - pool_name: "TEST_IPV6_POOL_1" + network: "2001:db8:300::/48" + gateway: "2001:db8:300::1" + loopback_id: "103" + +l3vn_devices: # Spine is not included as no config needed for spine in L3VN Unicast + border: # multiple border groups allowed + - group_name: border-group1 + devices: + - device_name: evpn-app-c9k-30 + mgmt_ip: 172.27.248.82 + mac_address: 00:0c:29:c9:ee:a0 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.3.3.3 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + leaf: # multiple leaf groups allowed + - group_name: leaf-group1 + devices: + - device_name: evpn-app-c9k-31 + mgmt_ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.4.4.4 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - group_name: leaf-group2 + devices: + - device_name: evpn-app-c9k-28 + mgmt_ip: 172.27.248.224 + mac_address: 00:0c:29:4e:63:a9 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.2.2.2 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - device_name: evpn-app-vm26 + mgmt_ip: 172.27.248.222 + mac_address: 00:0c:29:b4:2b:aa + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.5.5.5 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + +# Mapping role to template path for L3VN Unicast +role_template_map: + leaf: ./jinja_templates/l3vn_unicast_leaf_cli.j2 + border: ./jinja_templates/l3vn_unicast_border_cli.j2 + leaf_delete: ./jinja_templates/l3vn_unicast_leaf_delete_cli.j2 + border_delete: ./jinja_templates/l3vn_unicast_border_delete_cli.j2 + +# L3VN Unicast global configuration parameters +l3vn_global_config: + # BGP Configuration + bgp_confederation_id: null + bgp_cluster_id: null + maximum_paths: 4 + maximum_paths_eibgp: 4 + + # L3VN Interface defaults + svi_mtu: 9000 + svi_autostate: false + + # Loopback interface range for L3VNs + loopback_range_start: 101 + loopback_range_end: 200 + + # DHCP relay configuration + dhcp_relay_enabled: true + dhcp_relay_information_option: true + +# Security and QoS policies for L3VNs +l3vn_policies: + # Access control lists + default_acl: + - "permit ip any any" + + # QoS class maps + qos_class_maps: + - name: "VOICE_TRAFFIC" + match_dscp: "ef" + - name: "VIDEO_TRAFFIC" + match_dscp: "af41" + - name: "DATA_TRAFFIC" + match_dscp: "default" + +# L3VN Unicast specific deployment parameters +proj_name: L3VN_UNICAST_AUTOGEN \ No newline at end of file From 9fda9968101b484fd881a3e690adeb336e660578 Mon Sep 17 00:00:00 2001 From: Penke Vivek Raj Date: Tue, 28 Oct 2025 16:34:28 +0530 Subject: [PATCH 25/41] l2vn sample var file changes --- .../evpn_l2vn_anycast_delete_deploy.yml | 10 ++++++++- .../evpn_l2vn_anycast_mcast_delete_deploy.yml | 10 ++++++++- .../evpn_l2vn_localized_delete_deploy.yml | 10 ++++++++- .../vars/evpn_l2vn_anycast_delete_var.yml | 12 +++++++--- .../evpn_l2vn_anycast_mcast_delete_var.yml | 15 +++++++++---- .../vars/evpn_l2vn_anycast_mcast_var.yml | 16 +++++++------- .../bgpevpn/vars/evpn_l2vn_anycast_var.yml | 12 +++++----- .../vars/evpn_l2vn_localized_delete_var.yml | 9 ++++++-- .../bgpevpn/vars/evpn_l2vn_localized_var.yml | 22 +++++++++---------- 9 files changed, 79 insertions(+), 37 deletions(-) diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml index 3fb8656f0d..6181c42a51 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_delete_deploy.yml @@ -27,6 +27,14 @@ index_var: idx label: "{{ item.l2vn_name | default('UNNAMED') }}" + - name: Validate top-level devices_mgmt_ips input + ansible.builtin.assert: + that: + - devices_mgmt_ips is defined + - devices_mgmt_ips is iterable + - devices_mgmt_ips | length > 0 + fail_msg: "Invalid top-level vars: need non-empty 'devices_mgmt_ips' list." + - name: Render l2vn_anycast_delete_cli.j2 to a variable ansible.builtin.set_fact: rendered_l2vn_anycast_delete: "{{ lookup('template', './jinja_templates/l2vn_anycast_delete_cli.j2') }}" @@ -68,4 +76,4 @@ - param_name: "vlan_name" param_value: "testvlan31" device_details: - device_ips: ["172.27.248.81"] \ No newline at end of file + device_ips: "{{ devices_mgmt_ips }}" \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml index 6af18dc58f..4f1be96b24 100644 --- a/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_anycast_mcast_delete_deploy.yml @@ -28,6 +28,14 @@ index_var: idx label: "{{ item.l2vn_name | default('UNNAMED') }}" + - name: Validate top-level devices_mgmt_ips input + ansible.builtin.assert: + that: + - devices_mgmt_ips is defined + - devices_mgmt_ips is iterable + - devices_mgmt_ips | length > 0 + fail_msg: "Invalid top-level vars: need non-empty 'devices_mgmt_ips' list." + - name: Render l2vn_anycast_mcast_delete_cli.j2 to a variable ansible.builtin.set_fact: rendered_l2vn_anycast_mcast_delete: "{{ lookup('template', './jinja_templates/l2vn_anycast_mcast_delete_cli.j2') }}" @@ -69,4 +77,4 @@ - param_name: "vlan_name" param_value: "testvlan31" device_details: - device_ips: ["172.27.248.81"] \ No newline at end of file + device_ips: "{{ devices_mgmt_ips }}" \ No newline at end of file diff --git a/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml b/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml index 820f1afd14..765b517519 100644 --- a/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml +++ b/playbooks/bgpevpn/evpn_l2vn_localized_delete_deploy.yml @@ -26,6 +26,14 @@ index_var: idx label: "{{ item.l2vn_name | default('UNNAMED') }}" + - name: Validate top-level devices_mgmt_ips input + ansible.builtin.assert: + that: + - devices_mgmt_ips is defined + - devices_mgmt_ips is iterable + - devices_mgmt_ips | length > 0 + fail_msg: "Invalid top-level vars: need non-empty 'devices_mgmt_ips' list." + - name: Render l2vn_localized_delete_cli.j2 to a variable ansible.builtin.set_fact: rendered_l2vn_localized_delete: "{{ lookup('template', './jinja_templates/l2vn_localized_delete_cli.j2') }}" @@ -67,4 +75,4 @@ - param_name: "vlan_name" param_value: "testvlan31" device_details: - device_ips: ["172.27.248.81"] \ No newline at end of file + device_ips: "{{ devices_mgmt_ips }}" \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml index 355cc758ee..687e789840 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_delete_var.yml @@ -1,4 +1,10 @@ l2vns: - - l2vn_name: L2VN-11 - vlan_id: 10 - vni: 5001 \ No newline at end of file + - l2vn_name: L2VN-ANYCAST-1 + vlan_id: 20 + vni: 5020 + - l2vn_name: L2VN-ANYCAST-2 + vlan_id: 30 + vni: 5030 +devices_mgmt_ips: + - 172.27.248.81 + - 172.27.248.83 \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml index 7f2c834d56..443f4f0593 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_delete_var.yml @@ -1,5 +1,12 @@ l2vns: - - l2vn_name: L2VN-mcast-12 - vlan_id: 12 - vni: 5001 - multicast_group: 237.1.1.1 \ No newline at end of file + - l2vn_name: L2VN-MCAST-1 + vlan_id: 40 + vni: 5040 + multicast_group: 237.1.1.1 + - l2vn_name: L2VN-MCAST-2 + vlan_id: 50 + vni: 5050 + multicast_group: 237.1.1.2 +devices_mgmt_ips: + - 172.27.248.81 + - 172.27.248.83 \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml index 3bff1663d3..13c4152bf0 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_mcast_var.yml @@ -1,8 +1,8 @@ asn: 1001 l2vns: - - l2vn_name: L2VN-20 - vlan_id: 20 - vni: 5020 + - l2vn_name: L2VN-MCAST-1 + vlan_id: 40 + vni: 5040 associated_l3vn: vrf-1 multicast_group: 237.1.1.1 l2vn_pool: @@ -10,15 +10,15 @@ l2vns: ipv4_netmask: 255.255.0.0 ipv6_address: 2004::1 ipv6_netmask: 64 - - l2vn_name: L2VN-30 - vlan_id: 30 - vni: 5030 + - l2vn_name: L2VN-MCAST-2 + vlan_id: 50 + vni: 5050 associated_l3vn: vrf-1 multicast_group: 237.1.1.2 l2vn_pool: - ipv4_address: 15.15.0.2 + ipv4_address: 15.15.0.1 ipv4_netmask: 255.255.0.0 - ipv6_address: 2005::2 + ipv6_address: 2005::1 ipv6_netmask: 64 devices: - ip: 172.27.248.81 diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml index 3e094b3d9a..ec62385748 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_anycast_var.yml @@ -1,22 +1,22 @@ asn: 1001 l2vns: - - l2vn_name: L2VN-20 + - l2vn_name: L2VN-ANYCAST-1 vlan_id: 20 vni: 5020 associated_l3vn: vrf-1 l2vn_pool: - ipv4_address: 14.14.0.1 + ipv4_address: 12.12.0.1 ipv4_netmask: 255.255.0.0 - ipv6_address: 2004::1 + ipv6_address: 2002::1 ipv6_netmask: 64 - - l2vn_name: L2VN-30 + - l2vn_name: L2VN-ANYCAST-2 vlan_id: 30 vni: 5030 associated_l3vn: vrf-1 l2vn_pool: - ipv4_address: 15.15.0.2 + ipv4_address: 13.13.0.1 ipv4_netmask: 255.255.0.0 - ipv6_address: 2005::2 + ipv6_address: 2003::1 ipv6_netmask: 64 devices: - ip: 172.27.248.81 diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml index 83e8999df9..ca04783339 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_localized_delete_var.yml @@ -1,3 +1,8 @@ l2vns: - - l2vn_name: L2VN-loc-10 - vlan_id: 11 \ No newline at end of file + - l2vn_name: L2VN-LOC-1 + vlan_id: 60 + - l2vn_name: L2VN-LOC-2 + vlan_id: 70 +devices_mgmt_ips: + - 172.27.248.81 + - 172.27.248.83 \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml b/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml index 22a017d6eb..e85d36275a 100644 --- a/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml +++ b/playbooks/bgpevpn/vars/evpn_l2vn_localized_var.yml @@ -1,21 +1,21 @@ asn: 1001 l2vns: - - l2vn_pool: - ipv4_address: 13.13.0.1 + - l2vn_name: L2VN-LOC-1 + vlan_id: 60 + associated_l3vn: vrf-1 + l2vn_pool: + ipv4_address: 16.16.0.1 ipv4_netmask: 255.255.0.0 - ipv6_address: 2002::1 + ipv6_address: 2006::1 ipv6_netmask: 64 - l2vn_name: L2VN-loc-10 - vlan_id: 11 + - l2vn_name: L2VN-LOC-2 + vlan_id: 70 associated_l3vn: vrf-1 - - l2vn_pool: - ipv4_address: 14.14.0.2 + l2vn_pool: + ipv4_address: 17.17.0.1 ipv4_netmask: 255.255.0.0 - ipv6_address: 2003::2 + ipv6_address: 2007::1 ipv6_netmask: 64 - l2vn_name: L2VN-loc-20 - vlan_id: 12 - associated_l3vn: vrf-1 devices: - ip: 172.27.248.81 mac_address: 00:0c:29:a1:bd:78 From bb063fedbb737715faa37353402c5ca72c02c9d0 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:08:50 +0530 Subject: [PATCH 26/41] L3vn Multicast playbooks --- ...3vn_multicast_anycast_rp_delete_deploy.yml | 208 ++++++++++++++++++ .../l3vn_multicast_anycast_rp_deploy.yml | 200 +++++++++++++++++ ...vn_multicast_external_rp_delete_deploy.yml | 204 +++++++++++++++++ .../l3vn_multicast_external_rp_deploy.yml | 200 +++++++++++++++++ ...vn_multicast_internal_rp_delete_deploy.yml | 204 +++++++++++++++++ .../l3vn_multicast_internal_rp_deploy.yml | 200 +++++++++++++++++ 6 files changed, 1216 insertions(+) create mode 100644 playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml create mode 100644 playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml create mode 100644 playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml create mode 100644 playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml create mode 100644 playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml create mode 100644 playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml diff --git a/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml new file mode 100644 index 0000000000..17c4de8bc6 --- /dev/null +++ b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml @@ -0,0 +1,208 @@ +--- +- name: Delete L3VN Multicast Anycast RP Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_multicast_anycast_rp_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_multicast_anycast_rp_delete + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_multicast_anycast_rp_delete" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Initialize empty role_devices structure + ansible.builtin.set_fact: + role_devices: {} + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Extract delete templates from role_template_map + ansible.builtin.set_fact: + delete_template_map: >- + {{ role_template_map | dict2items | selectattr('key', 'match', '.*_delete$') | items2dict(key_name='key', value_name='value') }} + + - name: Check delete template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ delete_template_map | dict2items }}" + register: delete_template_stats + + - name: Build missing delete template list + ansible.builtin.set_fact: + missing_delete_templates: >- + {{ delete_template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all delete template files found + ansible.builtin.assert: + that: + - missing_delete_templates | length == 0 + fail_msg: "Missing delete template files: {{ missing_delete_templates }}" + + - name: Build render item pairs (role/device) for delete + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN multicast anycast RP delete configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN multicast anycast RP delete configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key + '_delete'] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-multicast-anycast-rp-delete.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + asn: "{{ asn }}" + l3vns: "{{ l3vns }}" + l3vn_device_overrides: "{{ l3vn_device_overrides }}" + anycast_rp_sets: "{{ anycast_rp_sets | default([]) }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered delete files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered delete config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered delete configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_delete_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_delete_items is defined + ansible.builtin.set_fact: + l3vn_delete_items: [] + + - name: Define Catalyst Center project name for delete + ansible.builtin.set_fact: + proj_name: L3VN_MULTICAST_ANYCAST_RP_DELETE_AUTOGEN + + - name: Push delete configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build delete payload list + ansible.builtin.set_fact: + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-anycast-rp-delete\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_delete_files.results }}" + + - name: Debug delete deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP-DELETE" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each delete config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP-DELETE" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN multicast anycast RP delete config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP-DELETE" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_params: + - name: "device_name" + value: "{{ cfg.device_name }}" + register: catc_delete_results + + - name: Show Catalyst Center delete push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_delete_results.results | length }} L3VN multicast anycast RP delete templates via Catalyst Center" + + - name: Preserve rendered delete files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered delete config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered delete directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml new file mode 100644 index 0000000000..f78a531c4c --- /dev/null +++ b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml @@ -0,0 +1,200 @@ +--- +- name: Deploy L3VN Multicast Anycast RP Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_multicast_anycast_rp_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_multicast_anycast_rp + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_multicast_anycast_rp_delete" + - "./rendered_l3vn_multicast_anycast_rp" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Check template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ role_template_map | dict2items }}" + register: template_stats + + - name: Build missing template list + ansible.builtin.set_fact: + missing_templates: >- + {{ template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all template files found + ansible.builtin.assert: + that: + - missing_templates | length == 0 + fail_msg: "Missing template files: {{ missing_templates }}" + + - name: Build render item pairs (role/device) + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN multicast anycast RP configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN multicast anycast RP configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-multicast-anycast-rp.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + asn: "{{ asn }}" + l3vns: "{{ l3vns }}" + l3vn_device_overrides: "{{ l3vn_device_overrides }}" + anycast_rp_sets: "{{ anycast_rp_sets | default([]) }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_push_items is defined + ansible.builtin.set_fact: + l3vn_push_items: [] + + - name: Define Catalyst Center project name + ansible.builtin.set_fact: + proj_name: L3VN_MULTICAST_ANYCAST_RP_AUTOGEN + + - name: Push configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build push payload list + ansible.builtin.set_fact: + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-anycast-rp\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_files.results }}" + + - name: Debug deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN multicast anycast RP config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_params: + - name: "device_name" + value: "{{ cfg.device_name }}" + register: catc_push_results + + - name: Show Catalyst Center push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_push_results.results | length }} L3VN multicast anycast RP templates via Catalyst Center" + + - name: Preserve rendered files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered output directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml new file mode 100644 index 0000000000..b7b4b079b3 --- /dev/null +++ b/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml @@ -0,0 +1,204 @@ +--- +- name: Delete L3VN Multicast External RP Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_multicast_external_rp_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_multicast_external_rp_delete + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_multicast_external_rp_delete" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Extract delete templates from role_template_map + ansible.builtin.set_fact: + delete_template_map: >- + {{ role_template_map | dict2items | selectattr('key', 'match', '.*_delete$') | items2dict(key_name='key', value_name='value') }} + + - name: Check delete template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ delete_template_map | dict2items }}" + register: delete_template_stats + + - name: Build missing delete template list + ansible.builtin.set_fact: + missing_delete_templates: >- + {{ delete_template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all delete template files found + ansible.builtin.assert: + that: + - missing_delete_templates | length == 0 + fail_msg: "Missing delete template files: {{ missing_delete_templates }}" + + - name: Build render item pairs (role/device) for delete + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN multicast external RP delete configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN multicast external RP delete configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key + '_delete'] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-multicast-external-rp-delete.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + asn: "{{ asn }}" + l3vns: "{{ l3vns }}" + l3vn_device_overrides: "{{ l3vn_device_overrides }}" + external_rp_servers: "{{ external_rp_servers | default([]) }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered delete files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered delete config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered delete configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_delete_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_delete_items is defined + ansible.builtin.set_fact: + l3vn_delete_items: [] + + - name: Define Catalyst Center project name for delete + ansible.builtin.set_fact: + proj_name: L3VN_MULTICAST_EXTERNAL_RP_DELETE_AUTOGEN + + - name: Push delete configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build delete payload list + ansible.builtin.set_fact: + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-external-rp-delete\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_delete_files.results }}" + + - name: Debug delete deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP-DELETE" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each delete config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP-DELETE" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN multicast external RP delete config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP-DELETE" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_params: + - name: "device_name" + value: "{{ cfg.device_name }}" + register: catc_delete_results + + - name: Show Catalyst Center delete push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_delete_results.results | length }} L3VN multicast external RP delete templates via Catalyst Center" + + - name: Preserve rendered delete files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered delete config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered delete directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml new file mode 100644 index 0000000000..3ccf194e3f --- /dev/null +++ b/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml @@ -0,0 +1,200 @@ +--- +- name: Deploy L3VN Multicast External RP Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_multicast_external_rp_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_multicast_external_rp + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_multicast_external_rp_delete" + - "./rendered_l3vn_multicast_external_rp" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Check template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ role_template_map | dict2items }}" + register: template_stats + + - name: Build missing template list + ansible.builtin.set_fact: + missing_templates: >- + {{ template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all template files found + ansible.builtin.assert: + that: + - missing_templates | length == 0 + fail_msg: "Missing template files: {{ missing_templates }}" + + - name: Build render item pairs (role/device) + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN multicast external RP configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN multicast external RP configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-multicast-external-rp.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + asn: "{{ asn }}" + l3vns: "{{ l3vns }}" + l3vn_device_overrides: "{{ l3vn_device_overrides }}" + external_rp_servers: "{{ external_rp_servers | default([]) }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_push_items is defined + ansible.builtin.set_fact: + l3vn_push_items: [] + + - name: Define Catalyst Center project name + ansible.builtin.set_fact: + proj_name: L3VN_MULTICAST_EXTERNAL_RP_AUTOGEN + + - name: Push configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build push payload list + ansible.builtin.set_fact: + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-external-rp\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_files.results }}" + + - name: Debug deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN multicast external RP config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_params: + - name: "device_name" + value: "{{ cfg.device_name }}" + register: catc_push_results + + - name: Show Catalyst Center push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_push_results.results | length }} L3VN multicast external RP templates via Catalyst Center" + + - name: Preserve rendered files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered output directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml new file mode 100644 index 0000000000..7e5153e0b7 --- /dev/null +++ b/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml @@ -0,0 +1,204 @@ +--- +- name: Delete L3VN Multicast Internal RP Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_multicast_internal_rp_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_multicast_internal_rp_delete + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_multicast_internal_rp_delete" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Extract delete templates from role_template_map + ansible.builtin.set_fact: + delete_template_map: >- + {{ role_template_map | dict2items | selectattr('key', 'match', '.*_delete$') | items2dict(key_name='key', value_name='value') }} + + - name: Check delete template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ delete_template_map | dict2items }}" + register: delete_template_stats + + - name: Build missing delete template list + ansible.builtin.set_fact: + missing_delete_templates: >- + {{ delete_template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all delete template files found + ansible.builtin.assert: + that: + - missing_delete_templates | length == 0 + fail_msg: "Missing delete template files: {{ missing_delete_templates }}" + + - name: Build render item pairs (role/device) for delete + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN multicast internal RP delete configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN multicast internal RP delete configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key + '_delete'] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-multicast-internal-rp-delete.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + asn: "{{ asn }}" + l3vns: "{{ l3vns }}" + l3vn_device_overrides: "{{ l3vn_device_overrides }}" + internal_rp_groups: "{{ internal_rp_groups | default([]) }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered delete files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered delete config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered delete configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_delete_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_delete_items is defined + ansible.builtin.set_fact: + l3vn_delete_items: [] + + - name: Define Catalyst Center project name for delete + ansible.builtin.set_fact: + proj_name: L3VN_MULTICAST_INTERNAL_RP_DELETE_AUTOGEN + + - name: Push delete configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build delete payload list + ansible.builtin.set_fact: + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-internal-rp-delete\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_delete_files.results }}" + + - name: Debug delete deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP-DELETE" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each delete config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_delete_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP-DELETE" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN multicast internal RP delete config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP-DELETE" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_params: + - name: "device_name" + value: "{{ cfg.device_name }}" + register: catc_delete_results + + - name: Show Catalyst Center delete push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_delete_results.results | length }} L3VN multicast internal RP delete templates via Catalyst Center" + + - name: Preserve rendered delete files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered delete config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered delete directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file diff --git a/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml new file mode 100644 index 0000000000..3ae9dafe37 --- /dev/null +++ b/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml @@ -0,0 +1,200 @@ +--- +- name: Deploy L3VN Multicast Internal RP Configuration + hosts: localhost + gather_facts: false + vars_files: + - ./vars/fabric_overlay_topology_localdevices.yml # Common variables + - ./vars/l3vn_multicast_internal_rp_topology_vars.yml # L3VN specific variables + - ../credentials.yml + vars: + output_dir: ./rendered_l3vn_multicast_internal_rp + catc_credentials_file: ../credentials.yml + + tasks: + - name: Clean up any existing rendered directories + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ output_dir }}" + - "./rendered_l3vn_multicast_internal_rp_delete" + - "./rendered_l3vn_multicast_internal_rp" + + - name: Ensure rendered output directory exists + ansible.builtin.file: + path: "{{ output_dir }}" + state: directory + mode: '0755' + + - name: Build flattened role -> devices structure + ansible.builtin.set_fact: + role_devices: "{{ role_devices | default({}) | combine({ item.key: (item.value | map(attribute='devices') | list | flatten(levels=1)) }) }}" + loop: "{{ l3vn_devices | dict2items }}" + loop_control: + loop_var: item + + - name: Debug role_devices structure + ansible.builtin.debug: + var: role_devices + + - name: Debug role_template_map + ansible.builtin.debug: + var: role_template_map + + - name: Sanity check role_template_map defined + ansible.builtin.assert: + that: + - role_template_map is defined + - role_template_map | length > 0 + fail_msg: "role_template_map is undefined or empty; cannot select templates" + + - name: Check template files exist + ansible.builtin.stat: + path: "{{ item.value }}" + loop: "{{ role_template_map | dict2items }}" + register: template_stats + + - name: Build missing template list + ansible.builtin.set_fact: + missing_templates: >- + {{ template_stats.results + | selectattr('stat.exists','equalto', False) + | map(attribute='item.value') + | list }} + + - name: Assert all template files found + ansible.builtin.assert: + that: + - missing_templates | length == 0 + fail_msg: "Missing template files: {{ missing_templates }}" + + - name: Build render item pairs (role/device) + ansible.builtin.set_fact: + render_items: "{{ role_devices | dict2items | subelements('value') }}" + + - name: Debug planned render items count + ansible.builtin.debug: + msg: "Will render {{ render_items | length }} device L3VN multicast internal RP configs" + + - name: Debug first 3 render items + ansible.builtin.debug: + var: render_items[0:3] + + - name: Render L3VN multicast internal RP configs for all devices + ansible.builtin.template: + src: "{{ role_template_map[item.0.key] }}" + dest: "{{ output_dir }}/{{ item.1.device_name }}-{{ item.0.key }}-l3vn-multicast-internal-rp.cfg" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" + vars: + asn: "{{ asn }}" + l3vns: "{{ l3vns }}" + l3vn_device_overrides: "{{ l3vn_device_overrides }}" + internal_rp_groups: "{{ internal_rp_groups | default([]) }}" + device_mgmt_ip: "{{ item.1.mgmt_ip }}" + device_username: "{{ item.1.username }}" + device_password: "{{ item.1.password }}" + + - name: Display rendered files list + ansible.builtin.command: ls -1 {{ output_dir }} + register: ls_output + + - name: Show rendered config file names + ansible.builtin.debug: + var: ls_output.stdout_lines + + - name: Read rendered configs content + ansible.builtin.slurp: + src: "{{ output_dir }}/{{ item }}" + loop: "{{ ls_output.stdout_lines }}" + register: rendered_files + + - name: Include Catalyst Center credentials (if available) + ansible.builtin.include_vars: + file: "{{ catc_credentials_file }}" + ignore_errors: true + + - name: Ensure l3vn_push_items is defined + ansible.builtin.set_fact: + l3vn_push_items: [] + + - name: Define Catalyst Center project name + ansible.builtin.set_fact: + proj_name: L3VN_MULTICAST_INTERNAL_RP_AUTOGEN + + - name: Push configs via Catalyst Center template API + when: catc_host is defined + block: + - name: Build push payload list + ansible.builtin.set_fact: + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-internal-rp\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" + loop: "{{ rendered_files.results }}" + + - name: Debug deployment configuration + ansible.builtin.debug: + msg: + - "Project: {{ proj_name }}" + - "Template: {{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP" + - "Device: {{ cfg.device_name }}" + - "Config length: {{ cfg.config | length }}" + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + + - name: Deploy each config using template_workflow_manager module + vars: + state: merged + language: JINJA + soft_type: IOS-XE + product_family: Switches and Hubs + loop: "{{ l3vn_push_items }}" + loop_control: + loop_var: cfg + cisco.dnac.template_workflow_manager: + dnac_host: "{{ catc_host }}" + dnac_port: "{{ catc_port }}" + dnac_username: "{{ catc_username }}" + dnac_password: "{{ catc_password }}" + dnac_verify: "{{ catc_verify }}" + dnac_version: "{{ catc_version }}" + state: "{{ state }}" + config: + - configuration_templates: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP" + template_content: "{{ cfg.config }}" + version_description: "Auto-generated L3VN multicast internal RP config" + language: "{{ language }}" + software_type: "{{ soft_type }}" + device_types: + - product_family: "{{ product_family }}" + - deploy_template: + project_name: "{{ proj_name }}" + template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP" + force_push: true + device_details: + device_hostnames: + - "{{ cfg.device_name }}" + template_params: + - name: "device_name" + value: "{{ cfg.device_name }}" + register: catc_push_results + + - name: Show Catalyst Center push summary + ansible.builtin.debug: + msg: "Deployed {{ catc_push_results.results | length }} L3VN multicast internal RP templates via Catalyst Center" + + - name: Preserve rendered files for debugging (optional) + ansible.builtin.debug: + msg: "Rendered config files preserved in {{ output_dir }} for debugging" + when: not (cleanup_rendered_files | default(true) | bool) + + - name: Clean up rendered output directory after deployment + ansible.builtin.file: + path: "{{ output_dir }}" + state: absent + when: cleanup_rendered_files | default(true) | bool \ No newline at end of file From 46881cc8158b5786e5fea00288ecfce838f1c530 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:10:05 +0530 Subject: [PATCH 27/41] L3vn multicast var files --- ...3vn_multicast_anycast_rp_topology_vars.yml | 141 ++++++++++++++++++ ...vn_multicast_external_rp_topology_vars.yml | 139 +++++++++++++++++ ...vn_multicast_internal_rp_topology_vars.yml | 140 +++++++++++++++++ 3 files changed, 420 insertions(+) create mode 100644 playbooks/bgpevpn/vars/l3vn_multicast_anycast_rp_topology_vars.yml create mode 100644 playbooks/bgpevpn/vars/l3vn_multicast_external_rp_topology_vars.yml create mode 100644 playbooks/bgpevpn/vars/l3vn_multicast_internal_rp_topology_vars.yml diff --git a/playbooks/bgpevpn/vars/l3vn_multicast_anycast_rp_topology_vars.yml b/playbooks/bgpevpn/vars/l3vn_multicast_anycast_rp_topology_vars.yml new file mode 100644 index 0000000000..1817f609e5 --- /dev/null +++ b/playbooks/bgpevpn/vars/l3vn_multicast_anycast_rp_topology_vars.yml @@ -0,0 +1,141 @@ +--- +# L3VN Multicast Anycast RP specific parameters +# Common variables (asn, l3vn_devices, soft_type, state, language, product_family) +# are imported from fabric_overlay_topology_localdevices.yml + +# L3VN Multicast definitions with Anycast RP +l3vns: + - vrf_name: "mcastrp" + vlan_id: 3841 + vni: 4099 + rd_suffix: 3841 + rt_import: + - "1111:3841" + rt_export: + - "1111:3841" + multicast: + ipv4_enabled: true + ipv6_enabled: true + pim_ipv4_mode: "sparse-mode" + pim_ipv6_mode: "sparse-mode" + rp_address: "11.11.0.7" + mdt: + overlay_use_bgp_spt_only: true + auto_discovery: "vxlan" + default_vxlan: "227.1.0.4" + data_groups: + - group: "239.0.1.20" + mask: "0.0.3.255" + data_threshold: 234 + svi: + ipv6_enable: true + ip_unnumbered: "Loopback0" + pim_mode: "sparse-mode" + no_autostate: true + loopbacks: + - name: "Loopback5841" + ipv4: "11.11.0.2 255.255.255.255" + ipv6: "2001::2/128" + ipv6_enable: true + pim_sparse: true + - name: "Loopback6841" + ipv4: "11.11.0.7 255.255.255.255" + pim_sparse: true + router_bgp: + maximum_paths_eibgp: 4 + redistribute: + - connected + +# Device specific overrides for multicast anycast RP +l3vn_device_overrides: + evpn-app-c9k-28: + mcastrp: + rd_prefix: "99.2.2.2" + evpn-app-vm26: + mcastrp: + rd_prefix: "99.5.5.5" + evpn-app-c9k-31: + mcastrp: + rd_prefix: "99.4.4.4" + evpn-app-c9k-30: + mcastrp: + rd_prefix: "99.3.3.3" + loopbacks: + - name: "Loopback5841" + ipv4: "11.11.0.3 255.255.255.255" + ipv6: "2001::3/128" + ipv6_enable: true + pim_sparse: true + - name: "Loopback6841" + ipv4: "11.11.0.7 255.255.255.255" + pim_sparse: true + +l3vn_devices: # Spine is not included as no config needed for spine in L3VN Unicast + border: # multiple border groups allowed + - group_name: border-group1 + devices: + - device_name: evpn-app-c9k-30 + mgmt_ip: 172.27.248.82 + mac_address: 00:0c:29:c9:ee:a0 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.3.3.3 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + leaf: # multiple leaf groups allowed + - group_name: leaf-group1 + devices: + - device_name: evpn-app-c9k-31 + mgmt_ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.4.4.4 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - group_name: leaf-group2 + devices: + - device_name: evpn-app-c9k-28 + mgmt_ip: 172.27.248.224 + mac_address: 00:0c:29:4e:63:a9 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.2.2.2 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - device_name: evpn-app-vm26 + mgmt_ip: 172.27.248.222 + mac_address: 00:0c:29:b4:2b:aa + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.5.5.5 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + +# Anycast RP Sets configuration (optional, for documentation / future use) +anycast_rp_sets: + - group_range: "239.1.1.0/24" + anycast_rp_address: "10.255.255.1" + rp_set_members: + - rp_address: "10.10.10.1" + device_name: "leaf-device1" + - rp_address: "10.10.10.2" + device_name: "leaf-device2" + +# Mapping role to template path for L3VN Multicast Anycast RP +role_template_map: + leaf: ./jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 + border: ./jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 + leaf_delete: ./jinja_templates/l3vn_multicast_anycast_rp_leaf_delete_cli.j2 + border_delete: ./jinja_templates/l3vn_multicast_anycast_rp_border_delete_cli.j2 + +# L3VN Multicast Anycast RP specific deployment parameters +proj_name: L3VN_MULTICAST_ANYCAST_RP_AUTOGEN \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/l3vn_multicast_external_rp_topology_vars.yml b/playbooks/bgpevpn/vars/l3vn_multicast_external_rp_topology_vars.yml new file mode 100644 index 0000000000..5ca820cc5d --- /dev/null +++ b/playbooks/bgpevpn/vars/l3vn_multicast_external_rp_topology_vars.yml @@ -0,0 +1,139 @@ +--- +# L3VN Multicast External RP specific parameters +# Common variables (asn, l3vn_devices, soft_type, state, language, product_family) +# are imported from fabric_overlay_topology_localdevices.yml + +# L3VN Multicast definitions with External RP +l3vns: + - vrf_name: "mcastexternal" + vlan_id: 3841 + vni: 4099 + rd_suffix: 3841 + rt_import: + - "1111:3841" + rt_export: + - "1111:3841" + multicast: + ipv4_enabled: true + ipv6_enabled: true + pim_ipv4_mode: "sparse-mode" + pim_ipv6_mode: "sparse-mode" + rp_address_ipv4: "12.12.5.1" + rp_address_ipv6: "2134::5" + register_source: "Loopback5841" + mdt: + overlay_use_bgp: true + auto_discovery: "vxlan" + default_vxlan: "227.1.0.4" + data_groups: + - group: "239.0.1.20" + mask: "0.0.3.255" + data_threshold: 234 + svi: + ipv6_enable: true + ip_unnumbered: "Loopback0" + pim_mode: "sparse-mode" + no_autostate: true + loopbacks: + - name: "Loopback5841" + ipv4: "11.11.0.2 255.255.255.255" + ipv6: "2001::2/128" + ipv6_enable: true + pim_sparse: true + router_bgp: + maximum_paths_eibgp: 4 + redistribute: + - connected + +# Device specific overrides for multicast external RP +l3vn_device_overrides: + evpn-app-c9k-28: + mcastexternal: + rd_prefix: "99.2.2.2" + evpn-app-vm26: + mcastexternal: + rd_prefix: "99.5.5.5" + evpn-app-c9k-31: + mcastexternal: + rd_prefix: "99.4.4.4" + evpn-app-c9k-30: + mcastexternal: + rd_prefix: "99.3.3.3" + loopbacks: + - name: "Loopback5841" + ipv4: "11.11.0.3 255.255.255.255" + ipv6: "2001::3/128" + ipv6_enable: true + pim_sparse: true + +l3vn_devices: # Spine is not included as no config needed for spine in L3VN Unicast + border: # multiple border groups allowed + - group_name: border-group1 + devices: + - device_name: evpn-app-c9k-30 + mgmt_ip: 172.27.248.82 + mac_address: 00:0c:29:c9:ee:a0 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.3.3.3 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + leaf: # multiple leaf groups allowed + - group_name: leaf-group1 + devices: + - device_name: evpn-app-c9k-31 + mgmt_ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.4.4.4 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - group_name: leaf-group2 + devices: + - device_name: evpn-app-c9k-28 + mgmt_ip: 172.27.248.224 + mac_address: 00:0c:29:4e:63:a9 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.2.2.2 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - device_name: evpn-app-vm26 + mgmt_ip: 172.27.248.222 + mac_address: 00:0c:29:b4:2b:aa + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.5.5.5 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + +# External RP Servers configuration (optional, for documentation / future use) +external_rp_servers: + - rp_address: "192.168.100.10" + rp_description: "External RP Server 1" + group_ranges: + - "239.20.1.0/24" + - "239.20.2.0/24" + access_method: "static" + redundancy_priority: 100 + - ipv4_unicast + - ipv4_multicast + +# Mapping role to template path for L3VN Multicast External RP +role_template_map: + leaf: ./jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 + border: ./jinja_templates/l3vn_multicast_external_rp_border_cli.j2 + leaf_delete: ./jinja_templates/l3vn_multicast_external_rp_leaf_delete_cli.j2 + border_delete: ./jinja_templates/l3vn_multicast_external_rp_border_delete_cli.j2 + +# L3VN Multicast External RP specific deployment parameters +proj_name: L3VN_MULTICAST_EXTERNAL_RP_AUTOGEN \ No newline at end of file diff --git a/playbooks/bgpevpn/vars/l3vn_multicast_internal_rp_topology_vars.yml b/playbooks/bgpevpn/vars/l3vn_multicast_internal_rp_topology_vars.yml new file mode 100644 index 0000000000..fd8d311f5c --- /dev/null +++ b/playbooks/bgpevpn/vars/l3vn_multicast_internal_rp_topology_vars.yml @@ -0,0 +1,140 @@ +--- +# L3VN Multicast Internal RP specific parameters +# Common variables (asn, l3vn_devices, soft_type, state, language, product_family) +# are imported from fabric_overlay_topology_localdevices.yml + +# L3VN Multicast definitions with Internal RP +l3vns: + - vrf_name: "mcastinternal" + vlan_id: 3841 + vni: 4099 + rd_suffix: 3841 + rt_import: + - "1111:3841" + rt_export: + - "1111:3841" + multicast: + ipv4_enabled: true + ipv6_enabled: true + pim_ipv4_mode: "sparse-mode" + pim_ipv6_mode: "sparse-mode" + rp_address_ipv4: "11.11.0.7" + rp_address_ipv6: "2001::7" + register_source: "Loopback5841" + mdt: + overlay_use_bgp: true + auto_discovery: "vxlan" + default_vxlan: "227.1.0.4" + data_groups: + - group: "239.0.1.20" + mask: "0.0.3.255" + data_threshold: 234 + svi: + ipv6_enable: true + ip_unnumbered: "Loopback0" + pim_mode: "sparse-mode" + no_autostate: true + loopbacks: + - name: "Loopback5841" + ipv4: "11.11.0.2 255.255.255.255" + ipv6: "2001::2/128" + ipv6_enable: true + pim_sparse: true + router_bgp: + maximum_paths_eibgp: 4 + redistribute: + - connected + +# Device specific overrides for multicast internal RP +l3vn_device_overrides: + evpn-app-c9k-28: + mcastinternal: + rd_prefix: "99.2.2.2" + evpn-app-vm26: + mcastinternal: + rd_prefix: "99.5.5.5" + evpn-app-c9k-31: + mcastinternal: + rd_prefix: "99.4.4.4" + evpn-app-c9k-30: + mcastinternal: + rd_prefix: "99.3.3.3" + loopbacks: + - name: "Loopback5841" + ipv4: "11.11.0.3 255.255.255.255" + ipv6: "2001::3/128" + ipv6_enable: true + pim_sparse: true + - name: "Loopback6841" + ipv4: "11.11.0.7 255.255.255.255" + ipv6: "2001::7/128" + ipv6_enable: true + pim_sparse: true + +l3vn_devices: # Spine is not included as no config needed for spine in L3VN Unicast + border: # multiple border groups allowed + - group_name: border-group1 + devices: + - device_name: evpn-app-c9k-30 + mgmt_ip: 172.27.248.82 + mac_address: 00:0c:29:c9:ee:a0 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.3.3.3 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + leaf: # multiple leaf groups allowed + - group_name: leaf-group1 + devices: + - device_name: evpn-app-c9k-31 + mgmt_ip: 172.27.248.83 + mac_address: 00:50:56:be:98:20 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.4.4.4 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - group_name: leaf-group2 + devices: + - device_name: evpn-app-c9k-28 + mgmt_ip: 172.27.248.224 + mac_address: 00:0c:29:4e:63:a9 + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.2.2.2 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + - device_name: evpn-app-vm26 + mgmt_ip: 172.27.248.222 + mac_address: 00:0c:29:b4:2b:aa + username: admin + password: Admin@123 + enable_pwd: Lablab123! + loopback_ip: 99.5.5.5 + neighbors: + - 99.1.1.1 + - 99.6.6.6 + +# Internal RP Groups configuration (optional, for documentation / future use) +internal_rp_groups: + - group_range: "239.10.1.0/24" + rp_address: "10.10.10.1" + rp_device: "leaf-device1" + priority: 100 + hold_time: 150 + +# Mapping role to template path for L3VN Multicast Internal RP +role_template_map: + leaf: ./jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 + border: ./jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 + leaf_delete: ./jinja_templates/l3vn_multicast_internal_rp_leaf_delete_cli.j2 + border_delete: ./jinja_templates/l3vn_multicast_internal_rp_border_delete_cli.j2 + +# L3VN Multicast Internal RP specific deployment parameters +proj_name: L3VN_MULTICAST_INTERNAL_RP_AUTOGEN \ No newline at end of file From 865e19d27724403554e63caa3ae2ada11ece2cb7 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:11:44 +0530 Subject: [PATCH 28/41] Update l3vn_multicast_anycast_rp_border_cli.j2 --- .../l3vn_multicast_anycast_rp_border_cli.j2 | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 index 8b13789179..bc806d7ec1 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_cli.j2 @@ -1 +1,162 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set rd_prefix = device_override.get('rd_prefix', item.1.loopback_ip) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ rd_prefix }}:{{ l3vn.rd_suffix }} + address-family ipv4 +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.overlay_use_bgp_spt_only %} + mdt overlay use-bgp spt-only +{% endif %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.data_groups is defined %} +{% for data_group in l3vn.mdt.data_groups %} + mdt data vxlan {{ data_group.group }} {{ data_group.mask }} +{% endfor %} +{% endif %} +{% if l3vn.mdt.data_threshold is defined %} + mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} +{% endif %} + address-family ipv6 +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.overlay_use_bgp_spt_only %} + mdt overlay use-bgp spt-only +{% endif %} +{% endif %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit + +{# Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv4_enabled %} +ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled %} +ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} + +{# Loopback Interfaces (Border devices may have different loopback IPs) #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +interface {{ loopback.name }} + vrf forwarding {{ l3vn.vrf_name }} +{% if loopback.ipv6_enable is defined and loopback.ipv6_enable %} + ipv6 enable +{% endif %} +{% if loopback.ipv4 %} + ip address {{ loopback.ipv4 }} +{% endif %} +{% if loopback.ipv6 is defined %} + ipv6 address {{ loopback.ipv6 }} +{% endif %} +{% if loopback.pim_sparse %} + ip pim sparse-mode +{% endif %} + exit +{% endfor %} +{% endif %} + +{# SVI Interface #} +{% if l3vn.svi is defined %} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} +{% if l3vn.svi.ipv6_enable %} + ipv6 enable +{% endif %} +{% if l3vn.svi.ip_unnumbered %} + ip unnumbered {{ l3vn.svi.ip_unnumbered }} +{% endif %} +{% if l3vn.svi.pim_mode %} + ip pim {{ l3vn.svi.pim_mode }} +{% endif %} +{% if l3vn.svi.no_autostate %} + no autostate +{% endif %} + exit +{% endif %} + +{# PIM RP Configuration #} +{% if l3vn.multicast is defined and l3vn.multicast.rp_address %} +ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address }} +{% endif %} + +{# BGP Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} + exit + exit + +{% endfor %} From ccb9a0f9eaea9ba16451d95182edab80787542ae Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:12:09 +0530 Subject: [PATCH 29/41] Update l3vn_multicast_anycast_rp_leaf_cli.j2 --- .../l3vn_multicast_anycast_rp_leaf_cli.j2 | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 index 8b13789179..bdd3953603 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_cli.j2 @@ -1 +1,162 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set rd_prefix = device_override.get('rd_prefix', item.1.loopback_ip) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ rd_prefix }}:{{ l3vn.rd_suffix }} + address-family ipv4 +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.overlay_use_bgp_spt_only %} + mdt overlay use-bgp spt-only +{% endif %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.data_groups is defined %} +{% for data_group in l3vn.mdt.data_groups %} + mdt data vxlan {{ data_group.group }} {{ data_group.mask }} +{% endfor %} +{% endif %} +{% if l3vn.mdt.data_threshold is defined %} + mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} +{% endif %} + address-family ipv6 +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.overlay_use_bgp_spt_only %} + mdt overlay use-bgp spt-only +{% endif %} +{% endif %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit + +{# Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv4_enabled %} +ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled %} +ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} + +{# SVI Interface #} +{% if l3vn.svi is defined %} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} +{% if l3vn.svi.ipv6_enable %} + ipv6 enable +{% endif %} +{% if l3vn.svi.ip_unnumbered %} + ip unnumbered {{ l3vn.svi.ip_unnumbered }} +{% endif %} +{% if l3vn.svi.pim_mode %} + ip pim {{ l3vn.svi.pim_mode }} +{% endif %} +{% if l3vn.svi.no_autostate %} + no autostate +{% endif %} + exit +{% endif %} + +{# Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +interface {{ loopback.name }} + vrf forwarding {{ l3vn.vrf_name }} +{% if loopback.ipv6_enable is defined and loopback.ipv6_enable %} + ipv6 enable +{% endif %} +{% if loopback.ipv4 %} + ip address {{ loopback.ipv4 }} +{% endif %} +{% if loopback.ipv6 is defined %} + ipv6 address {{ loopback.ipv6 }} +{% endif %} +{% if loopback.pim_sparse %} + ip pim sparse-mode +{% endif %} + exit +{% endfor %} +{% endif %} + +{# PIM RP Configuration #} +{% if l3vn.multicast is defined and l3vn.multicast.rp_address %} +ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address }} +{% endif %} + +{# BGP Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} + exit + exit + +{% endfor %} From 9c5ebfaeaf26706680bad617873a66a9d1505d10 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:12:32 +0530 Subject: [PATCH 30/41] Update l3vn_multicast_external_rp_border_cli.j2 --- .../l3vn_multicast_external_rp_border_cli.j2 | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 index 8b13789179..ea7d3ba3cb 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_cli.j2 @@ -1 +1,173 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set rd_prefix = device_override.get('rd_prefix', item.1.loopback_ip) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ rd_prefix }}:{{ l3vn.rd_suffix }} + address-family ipv4 +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% endif %} + address-family ipv6 +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% if l3vn.mdt.data_groups is defined %} +{% for data_group in l3vn.mdt.data_groups %} + mdt data vxlan {{ data_group.group }} {{ data_group.mask }} +{% endfor %} +{% endif %} +{% if l3vn.mdt.data_threshold is defined %} + mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} +{% endif %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit + +{# Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv4_enabled %} +ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled %} +ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} + +{# Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +interface {{ loopback.name }} + vrf forwarding {{ l3vn.vrf_name }} +{% if loopback.ipv6_enable is defined and loopback.ipv6_enable %} + ipv6 enable +{% endif %} +{% if loopback.ipv4 %} + ip address {{ loopback.ipv4 }} +{% endif %} +{% if loopback.ipv6 is defined %} + ipv6 address {{ loopback.ipv6 }} +{% endif %} +{% if loopback.pim_sparse %} + ip pim sparse-mode +{% endif %} + exit +{% endfor %} +{% endif %} + +{# SVI Interface #} +{% if l3vn.svi is defined %} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} +{% if l3vn.svi.ipv6_enable %} + ipv6 enable +{% endif %} +{% if l3vn.svi.ip_unnumbered %} + ip unnumbered {{ l3vn.svi.ip_unnumbered }} +{% endif %} +{% if l3vn.svi.pim_mode %} + ip pim {{ l3vn.svi.pim_mode }} +{% endif %} +{% if l3vn.svi.no_autostate %} + no autostate +{% endif %} + exit +{% endif %} + +{# PIM RP Configuration with Register Source (External RP) #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv6 %} +ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% endif %} + +{# BGP Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} + exit + exit + +{% endfor %} From 266337bcb62505f0c682d1f37669798f297cf8ec Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:12:47 +0530 Subject: [PATCH 31/41] Update l3vn_multicast_external_rp_leaf_cli.j2 --- .../l3vn_multicast_external_rp_leaf_cli.j2 | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 index 8b13789179..67a53861c8 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 @@ -1 +1,172 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set rd_prefix = device_override.get('rd_prefix', item.1.loopback_ip) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ rd_prefix }}:{{ l3vn.rd_suffix }} + address-family ipv4 +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.data_groups is defined %} +{% for data_group in l3vn.mdt.data_groups %} + mdt data vxlan {{ data_group.group }} {{ data_group.mask }} +{% endfor %} +{% endif %} +{% if l3vn.mdt.data_threshold is defined %} + mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} + address-family ipv6 +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% endif %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit + +{# Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv4_enabled %} +ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled %} +ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} + +{# SVI Interface #} +{% if l3vn.svi is defined %} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} +{% if l3vn.svi.ipv6_enable %} + ipv6 enable +{% endif %} +{% if l3vn.svi.ip_unnumbered %} + ip unnumbered {{ l3vn.svi.ip_unnumbered }} +{% endif %} +{% if l3vn.svi.pim_mode %} + ip pim {{ l3vn.svi.pim_mode }} +{% endif %} +{% if l3vn.svi.no_autostate %} + no autostate +{% endif %} + exit +{% endif %} + +{# Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +interface {{ loopback.name }} + vrf forwarding {{ l3vn.vrf_name }} +{% if loopback.ipv6_enable is defined and loopback.ipv6_enable %} + ipv6 enable +{% endif %} +{% if loopback.ipv4 %} + ip address {{ loopback.ipv4 }} +{% endif %} +{% if loopback.ipv6 is defined %} + ipv6 address {{ loopback.ipv6 }} +{% endif %} +{% if loopback.pim_sparse %} + ip pim sparse-mode +{% endif %} + exit +{% endfor %} +{% endif %} + +{# PIM RP Configuration with Register Source (External RP) #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv6 %} +ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% endif %} + +{# BGP Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} + exit + exit + +{% endfor %} From 867400d0e69252e7747b399081e486cb3b6d2fc8 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:13:15 +0530 Subject: [PATCH 32/41] Update l3vn_multicast_internal_rp_border_cli.j2 --- .../l3vn_multicast_internal_rp_border_cli.j2 | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 index 8b13789179..a10bd72c2d 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_cli.j2 @@ -1 +1,173 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set rd_prefix = device_override.get('rd_prefix', item.1.loopback_ip) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ rd_prefix }}:{{ l3vn.rd_suffix }} + address-family ipv4 +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.data_groups is defined %} +{% for data_group in l3vn.mdt.data_groups %} + mdt data vxlan {{ data_group.group }} {{ data_group.mask }} +{% endfor %} +{% endif %} +{% if l3vn.mdt.data_threshold is defined %} + mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} +{% endif %} + address-family ipv6 +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% endif %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit + +{# Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv4_enabled %} +ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled %} +ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} + +{# Loopback Interfaces (Border devices may have additional loopbacks) #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +interface {{ loopback.name }} + vrf forwarding {{ l3vn.vrf_name }} +{% if loopback.ipv6_enable is defined and loopback.ipv6_enable %} + ipv6 enable +{% endif %} +{% if loopback.ipv4 %} + ip address {{ loopback.ipv4 }} +{% endif %} +{% if loopback.ipv6 is defined %} + ipv6 address {{ loopback.ipv6 }} +{% endif %} +{% if loopback.pim_sparse %} + ip pim sparse-mode +{% endif %} + exit +{% endfor %} +{% endif %} + +{# SVI Interface #} +{% if l3vn.svi is defined %} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} +{% if l3vn.svi.ipv6_enable %} + ipv6 enable +{% endif %} +{% if l3vn.svi.ip_unnumbered %} + ip unnumbered {{ l3vn.svi.ip_unnumbered }} +{% endif %} +{% if l3vn.svi.pim_mode %} + ip pim {{ l3vn.svi.pim_mode }} +{% endif %} +{% if l3vn.svi.no_autostate %} + no autostate +{% endif %} + exit +{% endif %} + +{# PIM RP Configuration with Register Source #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv6 %} +ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% endif %} + +{# BGP Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} + exit + exit + +{% endfor %} From 1e180fa751f06fd564fec8e06a95c1acb067d052 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:13:31 +0530 Subject: [PATCH 33/41] Update l3vn_multicast_internal_rp_leaf_cli.j2 --- .../l3vn_multicast_internal_rp_leaf_cli.j2 | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 index 8b13789179..ffde6850cc 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_cli.j2 @@ -1 +1,173 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set rd_prefix = device_override.get('rd_prefix', item.1.loopback_ip) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} +{# VRF Definition #} +vrf definition {{ l3vn.vrf_name }} + rd {{ rd_prefix }}:{{ l3vn.rd_suffix }} + address-family ipv4 +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.data_groups is defined %} +{% for data_group in l3vn.mdt.data_groups %} + mdt data vxlan {{ data_group.group }} {{ data_group.mask }} +{% endfor %} +{% endif %} +{% if l3vn.mdt.data_threshold is defined %} + mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} +{% endif %} + address-family ipv6 +{% if l3vn.mdt is defined %} +{% if l3vn.mdt.auto_discovery %} + mdt auto-discovery {{ l3vn.mdt.auto_discovery }} +{% endif %} +{% if l3vn.mdt.default_vxlan %} + mdt default vxlan {{ l3vn.mdt.default_vxlan }} +{% endif %} +{% if l3vn.mdt.overlay_use_bgp is defined and l3vn.mdt.overlay_use_bgp %} + mdt overlay use-bgp +{% endif %} +{% endif %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} +{% endfor %} +{% for rt in l3vn.rt_import %} + route-target import {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} stitching +{% endfor %} +{% for rt in l3vn.rt_export %} + route-target export {{ rt }} +{% endfor %} + +{# VLAN Configuration #} +vlan configuration {{ l3vn.vlan_id }} + member vni {{ l3vn.vni }} +vlan {{ l3vn.vlan_id }} + name Vlan{{ l3vn.vlan_id }} + exit + +{# Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv4_enabled %} +ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled %} +ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} + +{# NVE Interface - VNI to VRF mapping #} +interface nve1 + member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} + +{# Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +interface {{ loopback.name }} + vrf forwarding {{ l3vn.vrf_name }} +{% if loopback.ipv6_enable is defined and loopback.ipv6_enable %} + ipv6 enable +{% endif %} +{% if loopback.ipv4 %} + ip address {{ loopback.ipv4 }} +{% endif %} +{% if loopback.ipv6 is defined %} + ipv6 address {{ loopback.ipv6 }} +{% endif %} +{% if loopback.pim_sparse %} + ip pim sparse-mode +{% endif %} + exit +{% endfor %} +{% endif %} + +{# SVI Interface #} +{% if l3vn.svi is defined %} +interface Vlan{{ l3vn.vlan_id }} + vrf forwarding {{ l3vn.vrf_name }} +{% if l3vn.svi.ipv6_enable %} + ipv6 enable +{% endif %} +{% if l3vn.svi.ip_unnumbered %} + ip unnumbered {{ l3vn.svi.ip_unnumbered }} +{% endif %} +{% if l3vn.svi.pim_mode %} + ip pim {{ l3vn.svi.pim_mode }} +{% endif %} +{% if l3vn.svi.no_autostate %} + no autostate +{% endif %} + exit +{% endif %} + +{# PIM RP Configuration with Register Source #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv6 %} +ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% endif %} + +{# BGP Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + address-family ipv6 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + address-family ipv4 vrf {{ l3vn.vrf_name }} + advertise l2vpn evpn +{% if l3vn.router_bgp is defined and l3vn.router_bgp.maximum_paths_eibgp is defined %} + maximum-paths eibgp {{ l3vn.router_bgp.maximum_paths_eibgp }} +{% endif %} +{% if l3vn.router_bgp is defined and l3vn.router_bgp.redistribute is defined %} +{% for redist in l3vn.router_bgp.redistribute %} + redistribute {{ redist }} +{% endfor %} +{% endif %} +{% endif %} + exit + exit + +{% endfor %} From 00dfe665cf4d01c5435d03a045c4232c7fe1261a Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:14:36 +0530 Subject: [PATCH 34/41] L3VN Multicast delete templates --- ..._multicast_anycast_rp_border_delete_cli.j2 | 61 ++++++++++++++++ ...vn_multicast_anycast_rp_leaf_delete_cli.j2 | 61 ++++++++++++++++ ...multicast_external_rp_border_delete_cli.j2 | 72 +++++++++++++++++++ ...n_multicast_external_rp_leaf_delete_cli.j2 | 72 +++++++++++++++++++ ...multicast_internal_rp_border_delete_cli.j2 | 72 +++++++++++++++++++ ...n_multicast_internal_rp_leaf_delete_cli.j2 | 72 +++++++++++++++++++ 6 files changed, 410 insertions(+) create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_delete_cli.j2 create mode 100644 playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_delete_cli.j2 diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_delete_cli.j2 new file mode 100644 index 0000000000..9bbe2a808f --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_border_delete_cli.j2 @@ -0,0 +1,61 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} + +{# Remove PIM RP Configuration #} +{% if l3vn.multicast is defined and l3vn.multicast.rp_address %} +no ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address }} +{% endif %} +! + +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} + exit +! + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove Loopback Interfaces (Border devices may have different loopbacks) #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +no interface {{ loopback.name }} +! +{% endfor %} +{% endif %} + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled %} +no ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled %} +no ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} +! + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_delete_cli.j2 new file mode 100644 index 0000000000..03264df1b7 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_anycast_rp_leaf_delete_cli.j2 @@ -0,0 +1,61 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} + +{# Remove PIM RP Configuration #} +{% if l3vn.multicast is defined and l3vn.multicast.rp_address %} +no ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address }} +{% endif %} +! + +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} + exit +! + +{# Remove Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +no interface {{ loopback.name }} +! +{% endfor %} +{% endif %} + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled %} +no ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled %} +no ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} +! + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_delete_cli.j2 new file mode 100644 index 0000000000..58962ed49e --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_border_delete_cli.j2 @@ -0,0 +1,72 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} + +{# Remove PIM RP Configuration with Register Source (External RP) #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.rp_address_ipv6 %} +no ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +no ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +no ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +no ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% endif %} +! + +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} + exit +! + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +no interface {{ loopback.name }} +! +{% endfor %} +{% endif %} + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled %} +no ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled %} +no ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} +! + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_delete_cli.j2 new file mode 100644 index 0000000000..7d0bd6e4a9 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_delete_cli.j2 @@ -0,0 +1,72 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} + +{# Remove PIM RP Configuration with Register Source (External RP) #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.rp_address_ipv6 %} +no ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +no ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +no ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +no ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% endif %} +! + +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} + exit +! + +{# Remove Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +no interface {{ loopback.name }} +! +{% endfor %} +{% endif %} + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled %} +no ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled %} +no ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} +! + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_delete_cli.j2 new file mode 100644 index 0000000000..c163411e25 --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_border_delete_cli.j2 @@ -0,0 +1,72 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} + +{# Remove PIM RP Configuration with Register Source #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.rp_address_ipv6 %} +no ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +no ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +no ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +no ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% endif %} +! + +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} + exit +! + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove Loopback Interfaces (Border devices may have additional loopbacks) #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +no interface {{ loopback.name }} +! +{% endfor %} +{% endif %} + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled %} +no ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled %} +no ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} +! + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_delete_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_delete_cli.j2 new file mode 100644 index 0000000000..ec9370287f --- /dev/null +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_internal_rp_leaf_delete_cli.j2 @@ -0,0 +1,72 @@ +{% for l3vn in l3vns %} +{# Get device-specific overrides if they exist #} +{% set device_override = l3vn_device_overrides.get(item.1.device_name, {}).get(l3vn.vrf_name, {}) %} +{% set device_loopbacks = device_override.get('loopbacks', l3vn.loopbacks) %} + +{# Remove PIM RP Configuration with Register Source #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.rp_address_ipv6 %} +no ipv6 pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv6 }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled and l3vn.multicast.register_source %} +no ip pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% if l3vn.multicast.rp_address_ipv4 %} +no ip pim vrf {{ l3vn.vrf_name }} rp-address {{ l3vn.multicast.rp_address_ipv4 }} +{% endif %} +{% if l3vn.multicast.ipv6_enabled and l3vn.multicast.register_source %} +no ipv6 pim vrf {{ l3vn.vrf_name }} register-source {{ l3vn.multicast.register_source }} +{% endif %} +{% endif %} +! + +{# Remove BGP VRF Configuration #} +router bgp {{ asn }} +{% if l3vn.multicast is defined and l3vn.multicast.ipv4_enabled %} + no address-family ipv4 vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast is defined and l3vn.multicast.ipv6_enabled %} + no address-family ipv6 vrf {{ l3vn.vrf_name }} +{% endif %} + exit +! + +{# Remove SVI Interface #} +no interface Vlan{{ l3vn.vlan_id }} +! + +{# Remove Loopback Interfaces #} +{% if device_loopbacks is defined %} +{% for loopback in device_loopbacks %} +no interface {{ loopback.name }} +! +{% endfor %} +{% endif %} + +{# Remove NVE Interface VNI to VRF mapping #} +interface nve1 + no member vni {{ l3vn.vni }} vrf {{ l3vn.vrf_name }} +! + +{# Remove Multicast Routing #} +{% if l3vn.multicast is defined %} +{% if l3vn.multicast.ipv6_enabled %} +no ipv6 multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% if l3vn.multicast.ipv4_enabled %} +no ip multicast-routing vrf {{ l3vn.vrf_name }} +{% endif %} +{% endif %} +! + +{# Remove VLAN Configuration #} +no vlan {{ l3vn.vlan_id }} +! +no vlan configuration {{ l3vn.vlan_id }} +! + +{# Remove VRF Definition #} +no vrf definition {{ l3vn.vrf_name }} +! + +{% endfor %} From c9d2eeccf17f02938697bfe6dff413349390aef8 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:36:29 +0530 Subject: [PATCH 35/41] Update l3vn_multicast_external_rp_leaf_cli.j2 --- .../jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 | 1 + 1 file changed, 1 insertion(+) diff --git a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 index 67a53861c8..b58d553d78 100644 --- a/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 +++ b/playbooks/bgpevpn/jinja_templates/l3vn_multicast_external_rp_leaf_cli.j2 @@ -37,6 +37,7 @@ vrf definition {{ l3vn.vrf_name }} {% endif %} {% if l3vn.mdt.data_threshold is defined %} mdt data threshold {{ l3vn.mdt.data_threshold }} +{% endif %} {% endif %} address-family ipv6 {% if l3vn.mdt is defined %} From bf2ef7b6fdef52cf87631242e86b9fb85035690a Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:39:09 +0530 Subject: [PATCH 36/41] Update l3vn_multicast_anycast_rp_delete_deploy.yml --- ...3vn_multicast_anycast_rp_delete_deploy.yml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml index 17c4de8bc6..10cd40404d 100644 --- a/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml +++ b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_delete_deploy.yml @@ -99,10 +99,6 @@ loop_control: label: "{{ item.1.device_name }} ({{ item.0.key }})" vars: - asn: "{{ asn }}" - l3vns: "{{ l3vns }}" - l3vn_device_overrides: "{{ l3vn_device_overrides }}" - anycast_rp_sets: "{{ anycast_rp_sets | default([]) }}" device_mgmt_ip: "{{ item.1.mgmt_ip }}" device_username: "{{ item.1.username }}" device_password: "{{ item.1.password }}" @@ -137,10 +133,15 @@ - name: Push delete configs via Catalyst Center template API when: catc_host is defined block: - - name: Build delete payload list + - name: Build delete payload list from L3VN devices ansible.builtin.set_fact: - l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-anycast-rp-delete\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_delete_files.results }}" + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_delete_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-multicast-anycast-rp-delete\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" - name: Debug delete deployment configuration ansible.builtin.debug: @@ -184,12 +185,18 @@ project_name: "{{ proj_name }}" template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP-DELETE" force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" device_details: device_hostnames: - "{{ cfg.device_name }}" - template_params: - - name: "device_name" - value: "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" register: catc_delete_results - name: Show Catalyst Center delete push summary @@ -205,4 +212,4 @@ ansible.builtin.file: path: "{{ output_dir }}" state: absent - when: cleanup_rendered_files | default(true) | bool \ No newline at end of file + when: cleanup_rendered_files | default(true) | bool From fec66a5d2ea7b86dc3eb5f5d5a153285b4bef250 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:39:49 +0530 Subject: [PATCH 37/41] Update l3vn_multicast_anycast_rp_deploy.yml --- .../l3vn_multicast_anycast_rp_deploy.yml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml index f78a531c4c..506732d123 100644 --- a/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml +++ b/playbooks/bgpevpn/l3vn_multicast_anycast_rp_deploy.yml @@ -91,10 +91,6 @@ loop_control: label: "{{ item.1.device_name }} ({{ item.0.key }})" vars: - asn: "{{ asn }}" - l3vns: "{{ l3vns }}" - l3vn_device_overrides: "{{ l3vn_device_overrides }}" - anycast_rp_sets: "{{ anycast_rp_sets | default([]) }}" device_mgmt_ip: "{{ item.1.mgmt_ip }}" device_username: "{{ item.1.username }}" device_password: "{{ item.1.password }}" @@ -129,10 +125,15 @@ - name: Push configs via Catalyst Center template API when: catc_host is defined block: - - name: Build push payload list + - name: Build push payload list from L3VN devices ansible.builtin.set_fact: - l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-anycast-rp\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_files.results }}" + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-multicast-anycast-rp\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" - name: Debug deployment configuration ansible.builtin.debug: @@ -176,12 +177,18 @@ project_name: "{{ proj_name }}" template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-ANYCAST-RP" force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" device_details: device_hostnames: - "{{ cfg.device_name }}" - template_params: - - name: "device_name" - value: "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" register: catc_push_results - name: Show Catalyst Center push summary @@ -197,4 +204,4 @@ ansible.builtin.file: path: "{{ output_dir }}" state: absent - when: cleanup_rendered_files | default(true) | bool \ No newline at end of file + when: cleanup_rendered_files | default(true) | bool From 9e2c842ca38392226c393fec3e97c388e3eb0923 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:40:34 +0530 Subject: [PATCH 38/41] Update l3vn_multicast_external_rp_delete_deploy.yml --- ...vn_multicast_external_rp_delete_deploy.yml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml index b7b4b079b3..208eaa3182 100644 --- a/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml +++ b/playbooks/bgpevpn/l3vn_multicast_external_rp_delete_deploy.yml @@ -95,10 +95,6 @@ loop_control: label: "{{ item.1.device_name }} ({{ item.0.key }})" vars: - asn: "{{ asn }}" - l3vns: "{{ l3vns }}" - l3vn_device_overrides: "{{ l3vn_device_overrides }}" - external_rp_servers: "{{ external_rp_servers | default([]) }}" device_mgmt_ip: "{{ item.1.mgmt_ip }}" device_username: "{{ item.1.username }}" device_password: "{{ item.1.password }}" @@ -133,10 +129,15 @@ - name: Push delete configs via Catalyst Center template API when: catc_host is defined block: - - name: Build delete payload list + - name: Build delete payload list from L3VN devices ansible.builtin.set_fact: - l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-external-rp-delete\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_delete_files.results }}" + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_delete_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-multicast-external-rp-delete\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" - name: Debug delete deployment configuration ansible.builtin.debug: @@ -180,12 +181,18 @@ project_name: "{{ proj_name }}" template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP-DELETE" force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" device_details: device_hostnames: - "{{ cfg.device_name }}" - template_params: - - name: "device_name" - value: "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" register: catc_delete_results - name: Show Catalyst Center delete push summary @@ -201,4 +208,4 @@ ansible.builtin.file: path: "{{ output_dir }}" state: absent - when: cleanup_rendered_files | default(true) | bool \ No newline at end of file + when: cleanup_rendered_files | default(true) | bool From 0a8ac51df2ce6190694d9414e5f1a0a343d91245 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:41:17 +0530 Subject: [PATCH 39/41] Update l3vn_multicast_external_rp_deploy.yml --- .../l3vn_multicast_external_rp_deploy.yml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml index 3ccf194e3f..1255525c89 100644 --- a/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml +++ b/playbooks/bgpevpn/l3vn_multicast_external_rp_deploy.yml @@ -91,10 +91,6 @@ loop_control: label: "{{ item.1.device_name }} ({{ item.0.key }})" vars: - asn: "{{ asn }}" - l3vns: "{{ l3vns }}" - l3vn_device_overrides: "{{ l3vn_device_overrides }}" - external_rp_servers: "{{ external_rp_servers | default([]) }}" device_mgmt_ip: "{{ item.1.mgmt_ip }}" device_username: "{{ item.1.username }}" device_password: "{{ item.1.password }}" @@ -129,10 +125,15 @@ - name: Push configs via Catalyst Center template API when: catc_host is defined block: - - name: Build push payload list + - name: Build push payload list from L3VN devices ansible.builtin.set_fact: - l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-external-rp\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_files.results }}" + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-multicast-external-rp\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" - name: Debug deployment configuration ansible.builtin.debug: @@ -176,12 +177,18 @@ project_name: "{{ proj_name }}" template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-EXTERNAL-RP" force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" device_details: device_hostnames: - "{{ cfg.device_name }}" - template_params: - - name: "device_name" - value: "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" register: catc_push_results - name: Show Catalyst Center push summary @@ -197,4 +204,4 @@ ansible.builtin.file: path: "{{ output_dir }}" state: absent - when: cleanup_rendered_files | default(true) | bool \ No newline at end of file + when: cleanup_rendered_files | default(true) | bool From d9ddadc9716b30451fd4b18de2f6e207f62f6a86 Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:42:14 +0530 Subject: [PATCH 40/41] Update l3vn_multicast_internal_rp_delete_deploy.yml --- ...vn_multicast_internal_rp_delete_deploy.yml | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml index 7e5153e0b7..059b24a91c 100644 --- a/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml +++ b/playbooks/bgpevpn/l3vn_multicast_internal_rp_delete_deploy.yml @@ -95,10 +95,6 @@ loop_control: label: "{{ item.1.device_name }} ({{ item.0.key }})" vars: - asn: "{{ asn }}" - l3vns: "{{ l3vns }}" - l3vn_device_overrides: "{{ l3vn_device_overrides }}" - internal_rp_groups: "{{ internal_rp_groups | default([]) }}" device_mgmt_ip: "{{ item.1.mgmt_ip }}" device_username: "{{ item.1.username }}" device_password: "{{ item.1.password }}" @@ -133,10 +129,15 @@ - name: Push delete configs via Catalyst Center template API when: catc_host is defined block: - - name: Build delete payload list + - name: Build delete payload list from L3VN devices ansible.builtin.set_fact: - l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-internal-rp-delete\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_delete_files.results }}" + l3vn_delete_items: "{{ l3vn_delete_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_delete_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-multicast-internal-rp-delete\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" - name: Debug delete deployment configuration ansible.builtin.debug: @@ -180,12 +181,18 @@ project_name: "{{ proj_name }}" template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP-DELETE" force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" device_details: device_hostnames: - "{{ cfg.device_name }}" - template_params: - - name: "device_name" - value: "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" register: catc_delete_results - name: Show Catalyst Center delete push summary @@ -201,4 +208,4 @@ ansible.builtin.file: path: "{{ output_dir }}" state: absent - when: cleanup_rendered_files | default(true) | bool \ No newline at end of file + when: cleanup_rendered_files | default(true) | bool From edac3f098fc72278c4672c29c0476bebef9ad1dc Mon Sep 17 00:00:00 2001 From: Vidhya Rathinam <35349615+VidhyaGit@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:42:51 +0530 Subject: [PATCH 41/41] Update l3vn_multicast_internal_rp_deploy.yml --- .../l3vn_multicast_internal_rp_deploy.yml | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml b/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml index 3ae9dafe37..bbe66108d6 100644 --- a/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml +++ b/playbooks/bgpevpn/l3vn_multicast_internal_rp_deploy.yml @@ -90,14 +90,6 @@ - skip_missing: True loop_control: label: "{{ item.1.device_name }} ({{ item.0.key }})" - vars: - asn: "{{ asn }}" - l3vns: "{{ l3vns }}" - l3vn_device_overrides: "{{ l3vn_device_overrides }}" - internal_rp_groups: "{{ internal_rp_groups | default([]) }}" - device_mgmt_ip: "{{ item.1.mgmt_ip }}" - device_username: "{{ item.1.username }}" - device_password: "{{ item.1.password }}" - name: Display rendered files list ansible.builtin.command: ls -1 {{ output_dir }} @@ -129,10 +121,15 @@ - name: Push configs via Catalyst Center template API when: catc_host is defined block: - - name: Build push payload list + - name: Build push payload list from L3VN devices ansible.builtin.set_fact: - l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': (item.item | regex_replace('-[^-]+-l3vn-multicast-internal-rp\\.cfg$','')) | regex_replace('-leaf$','') | regex_replace('-border$',''), 'config': (item.content | b64decode) } ] }}" - loop: "{{ rendered_files.results }}" + l3vn_push_items: "{{ l3vn_push_items | default([]) + [ { 'device_name': item.1.device_name, 'device_mgmt_ip': item.1.mgmt_ip, 'device_username': item.1.username, 'device_password': item.1.password, 'config': (rendered_files.results | selectattr('item', 'match', '.*' + item.1.device_name + '-' + item.0.key + '-l3vn-multicast-internal-rp\\.cfg$') | first).content | b64decode } ] }}" + with_subelements: + - "{{ role_devices | dict2items }}" + - value + - skip_missing: True + loop_control: + label: "{{ item.1.device_name }} ({{ item.0.key }})" - name: Debug deployment configuration ansible.builtin.debug: @@ -176,12 +173,18 @@ project_name: "{{ proj_name }}" template_name: "{{ cfg.device_name }}-L3VN-MULTICAST-INTERNAL-RP" force_push: true + template_params: + asn: "{{ asn }}" + device_name: "{{ cfg.device_name }}" + mgmt_ip: "{{ cfg.device_mgmt_ip }}" + username: "{{ cfg.device_username }}" + password: "{{ cfg.device_password }}" device_details: device_hostnames: - "{{ cfg.device_name }}" - template_params: - - name: "device_name" - value: "{{ cfg.device_name }}" + template_parameters: + - param_name: "device_name" + param_value: "{{ cfg.device_name }}" register: catc_push_results - name: Show Catalyst Center push summary @@ -197,4 +200,4 @@ ansible.builtin.file: path: "{{ output_dir }}" state: absent - when: cleanup_rendered_files | default(true) | bool \ No newline at end of file + when: cleanup_rendered_files | default(true) | bool