├── dag ├── output │ └── .gitkeep ├── preview_files │ └── .gitkeep ├── host_vars │ ├── inc_vars │ │ └── .gitkeep │ ├── delete_vars │ │ └── .gitkeep │ ├── ipv6_inc_vars │ │ └── .gitkeep │ ├── ipv6_delete_vars │ │ └── .gitkeep │ └── node_vars │ │ ├── Leaf-02.yml │ │ ├── Leaf-01.yml │ │ ├── Spine-01.yml │ │ └── Spine-02.yml ├── group_vars │ ├── leaf.yml │ ├── spine.yml │ ├── all.yml │ ├── ipv6_delete_vars.yml │ ├── create_vars.yml │ ├── ipv6_create_vars.yml │ ├── delete_vars.yml │ ├── trm_create_vars.yml │ └── overlay_db.yml ├── playbook_dhcp_delete_commit.yml ├── playbook_dhcp_delete_preview.yml ├── inventory.yml ├── playbook_access_incremental_commit.yml ├── playbook_access_incremental_preview.yml ├── playbook_trm_overlay_incremental_commit.yml ├── playbook_trm_overlay_incremental_preview.yml ├── playbook_yml_validation.yml ├── playbook_cleanup.yml ├── playbook_overlay_delete_ipv6_commit.yml ├── playbook_overlay_incremental_ipv6_commit.yml ├── playbook_overlay_delete_generate.yml ├── playbook_overlay_delete_ipv6_generate.yml ├── playbook_overlay_incremental_ipv6_generate.yml ├── playbook_access_add_commit.yml ├── playbook_overlay_delete_commit.yml ├── playbook_underlay_preview.yml ├── playbook_overlay_incremental_ipv6_preview.yml ├── playbook_overlay_delete_ipv6_preview.yml ├── playbook_underlay_commit.yml ├── playbook_overlay_incremental_generate.yml ├── library │ └── dhcp_src_addr_info.py ├── playbook_overlay_delete_preview.yml ├── playbook_output.yml ├── playbook_access_add_preview.yml └── playbook_overlay_precheck.yml ├── l2vni ├── output │ └── .gitkeep ├── preview_files │ └── .gitkeep ├── host_vars │ ├── inc_vars │ │ └── .gitkeep │ ├── delete_vars │ │ └── .gitkeep │ └── node_vars │ │ ├── Leaf-01.yml │ │ ├── Leaf-02.yml │ │ ├── Spine-01.yml │ │ └── Spine-02.yml ├── group_vars │ ├── leaf.yml │ ├── spine.yml │ ├── all.yml │ ├── overlay_db.yml │ ├── delete_vars.yml │ └── create_vars.yml ├── inventory.yml ├── playbook_access_incremental_commit.yml ├── playbook_access_incremental_preview.yml ├── playbook_yml_validation.yml ├── playbook_cleanup.yml ├── playbook_overlay_delete_generate.yml ├── playbook_access_add_commit.yml ├── playbook_overlay_delete_commit.yml ├── playbook_underlay_preview.yml ├── playbook_underlay_commit.yml ├── playbook_output.yml ├── playbook_overlay_incremental_generate.yml ├── playbook_overlay_preview.yml ├── playbook_overlay_delete_preview.yml ├── playbook_access_add_preview.yml └── playbook_overlay_precheck.yml ├── l3vni ├── preview_files │ └── .gitkeep ├── host_vars │ ├── inc_vars │ │ └── .gitkeep │ ├── delete_vars │ │ └── .gitkeep │ ├── ipv6_inc_vars │ │ └── .gitkeep │ ├── ipv6_delete_vars │ │ └── .gitkeep │ └── node_vars │ │ ├── Spine-01.yml │ │ ├── Spine-02.yml │ │ ├── Leaf-01.yml │ │ └── Leaf-02.yml ├── group_vars │ ├── leaf.yml │ ├── spine.yml │ ├── all.yml │ ├── delete_vars.yml │ ├── ipv6_create_vars.yml │ ├── ipv6_delete_vars.yml │ ├── create_vars.yml │ └── overlay_db.yml ├── output │ ├── loopback.txt │ ├── Leaf-01-show_commands.txt │ ├── Leaf-02-show_commands.txt │ ├── Spine-show_commands.txt │ ├── Spine-01-show_commands.txt │ ├── Spine-02-show_commands.txt │ └── Leaf-show_commands.txt ├── inventory.yml ├── playbook_cleanup.yml ├── playbook_yml_validation.yml ├── playbook_overlay_delete_ipv6_commit.yml ├── playbook_overlay_incremental_ipv6_commit.yml ├── playbook_overlay_incremental_generate.yml ├── playbook_overlay_delete_ipv6_generate.yml ├── playbook_overlay_incremental_ipv6_generate.yml ├── playbook_underlay_commit.yml ├── playbook_underlay_preview.yml ├── playbook_overlay_delete_generate.yml ├── playbook_overlay_delete_ipv6_preview.yml ├── playbook_overlay_delete_commit.yml ├── playbook_overlay_incremental_ipv6_preview.yml ├── playbook_output.yml ├── playbook_overlay_delete_preview.yml ├── playbook_overlay_preview.yml └── playbook_overlay_precheck.yml ├── docs ├── requirements.txt ├── .DS_Store ├── include │ ├── ansible.png │ └── topology.png ├── topology.rst ├── notes.rst ├── Makefile ├── make.bat ├── index.rst ├── _static │ └── css │ │ └── custom.css ├── about.rst └── conf.py ├── .DS_Store ├── requirements.txt ├── templates ├── hostname.j2 ├── pim_rp.j2 ├── show_command.j2 ├── spine_show_command.j2 ├── msdp_peering.j2 ├── pim_interfaces.j2 ├── rm_pl_acl.j2 ├── underlay_interfaces.j2 ├── rp_interfaces_trm.j2 ├── l2vpn_evpn_global.j2 ├── global_routing.j2 ├── pim_interfaces_trm.j2 ├── vlan_assignment.j2 ├── ospf_interfaces.j2 ├── overlay_interfaces.j2 ├── vlan_create.j2 ├── evi_vni_vlan_stiching.j2 ├── bgp_ipv4_mvpn_af.j2 ├── bgp_ipv4_mvpn_af_trm.j2 ├── isis_underlay.j2 ├── bgp_l2vpn_ipv46_per_vrf.j2 ├── leaf_show_command.j2 ├── bgp_mvpn_af_trm.j2 ├── ospf_overlay.j2 ├── bgp_l2vpn_evpn_af.j2 ├── nve_create.j2 ├── bgp_global.j2 ├── svi_create.j2 └── ipv6_incremental.j2 ├── pyproject.toml ├── .readthedocs.yaml ├── subtasks ├── subtask_acc_intf_access.yml ├── subtask_get_access_vlan.yml ├── subtask_acc_intf_commit.yml ├── subtask_acc_intf_preview.yml └── subtask_acc_intf_trunk.yml ├── lumache.py └── LICENSE /dag/output/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l2vni/output/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dag/preview_files/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l2vni/preview_files/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l3vni/preview_files/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dag/host_vars/inc_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l2vni/host_vars/inc_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l3vni/host_vars/inc_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dag/host_vars/delete_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dag/host_vars/ipv6_inc_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l2vni/host_vars/delete_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l3vni/host_vars/delete_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /l3vni/host_vars/ipv6_inc_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dag/host_vars/ipv6_delete_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme 2 | -------------------------------------------------------------------------------- /l3vni/host_vars/ipv6_delete_vars/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dag/group_vars/leaf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | role: 'leaf' 4 | -------------------------------------------------------------------------------- /dag/group_vars/spine.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | role: 'spine' 4 | -------------------------------------------------------------------------------- /l2vni/group_vars/leaf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | role: 'leaf' 4 | -------------------------------------------------------------------------------- /l2vni/group_vars/spine.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | role: 'spine' 4 | -------------------------------------------------------------------------------- /l3vni/group_vars/leaf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | role: 'leaf' 4 | -------------------------------------------------------------------------------- /l3vni/group_vars/spine.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | role: 'spine' 4 | -------------------------------------------------------------------------------- /l3vni/output/loopback.txt: -------------------------------------------------------------------------------- 1 | ['172.16.254.3'] 2 | ['172.16.254.4'] 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat9kEVPN/cat9k-evpn-ansible/HEAD/.DS_Store -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible 2 | ansible-pylibssh 3 | paramiko 4 | pyats 5 | genie 6 | -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat9kEVPN/cat9k-evpn-ansible/HEAD/docs/.DS_Store -------------------------------------------------------------------------------- /docs/include/ansible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat9kEVPN/cat9k-evpn-ansible/HEAD/docs/include/ansible.png -------------------------------------------------------------------------------- /docs/include/topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat9kEVPN/cat9k-evpn-ansible/HEAD/docs/include/topology.png -------------------------------------------------------------------------------- /docs/topology.rst: -------------------------------------------------------------------------------- 1 | Topology 2 | ======== 3 | 4 | Next topology is used in the automation scenario 5 | 6 | .. image:: include/topology.png -------------------------------------------------------------------------------- /l3vni/output/Leaf-01-show_commands.txt: -------------------------------------------------------------------------------- 1 | show run nve 2 | show nve peers 3 | show l2vpn evpn peers vxlan 4 | show bgp l2vpn evpn summary 5 | show bgp l2vpn evpn 6 | 7 | 8 | -------------------------------------------------------------------------------- /l3vni/output/Leaf-02-show_commands.txt: -------------------------------------------------------------------------------- 1 | show run nve 2 | show nve peers 3 | show l2vpn evpn peers vxlan 4 | show bgp l2vpn evpn summary 5 | show bgp l2vpn evpn 6 | 7 | 8 | -------------------------------------------------------------------------------- /dag/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | ansible_connection: ansible.netcommon.network_cli 2 | ansible_network_os: cisco.ios.ios 3 | ansible_python_interpreter: "python" 4 | ansible_user: cisco 5 | ansible_ssh_pass: cisco123 6 | -------------------------------------------------------------------------------- /l2vni/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | ansible_connection: ansible.netcommon.network_cli 2 | ansible_network_os: cisco.ios.ios 3 | ansible_python_interpreter: "python" 4 | ansible_user: cisco 5 | ansible_ssh_pass: cisco123 6 | -------------------------------------------------------------------------------- /l3vni/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | ansible_connection: ansible.netcommon.network_cli 2 | ansible_network_os: cisco.ios.ios 3 | ansible_python_interpreter: "python" 4 | ansible_user: cisco 5 | ansible_ssh_pass: cisco123 6 | -------------------------------------------------------------------------------- /templates/hostname.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring hostname for a swith 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% if hostname is defined %} 8 | hostname {{ hostname }} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["flit_core >=3.2,<4"] 3 | build-backend = "flit_core.buildapi" 4 | 5 | [project] 6 | name = "lumache" 7 | authors = [{name = "Graziella", email = "graziella@lumache"}] 8 | dynamic = ["version", "description"] 9 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | # Set the OS, Python version and other tools you might need 4 | build: 5 | os: ubuntu-22.04 6 | tools: 7 | python: "3.11" 8 | 9 | python: 10 | install: 11 | - requirements: docs/requirements.txt 12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/pim_rp.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures RP for the global routing table 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% if pim.rp_address is defined %} 8 | ip pim rp-address {{pim.rp_address}} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /templates/show_command.j2: -------------------------------------------------------------------------------- 1 | show run 2 | show nve peers 3 | show bgp l2vpn evpn summary 4 | show bgp l2vpn evpn 5 | 6 | {% for ivlan,idata in vlans.items() %} 7 | show l2vpn evpn mac evi {{idata.evi}} 8 | show l2fib bridge-domain {{ivlan}} detail 9 | {% endfor %} 10 | 11 | -------------------------------------------------------------------------------- /templates/spine_show_command.j2: -------------------------------------------------------------------------------- 1 | show run 2 | show ip ospf neighbor 3 | show bgp l2vpn evpn summary 4 | show bgp l2vpn evpn 5 | show ip pim neighbor 6 | show ip pim rp map 7 | show ip rpf 172.16.255.255 8 | show ip msdp summary 9 | show ip msdp sa-cache 10 | show ip mroute 11 | 12 | -------------------------------------------------------------------------------- /l3vni/output/Spine-show_commands.txt: -------------------------------------------------------------------------------- 1 | show run 2 | show ip ospf neighbor 3 | show bgp l2vpn evpn summary 4 | show bgp l2vpn evpn 5 | show ip pim neighbor 6 | show ip pim rp map 7 | show ip rpf 172.16.255.255 8 | show ip msdp summary 9 | show ip msdp sa-cache 10 | show ip mroute 11 | 12 | -------------------------------------------------------------------------------- /l3vni/output/Spine-01-show_commands.txt: -------------------------------------------------------------------------------- 1 | show run 2 | show ip ospf neighbor 3 | show bgp l2vpn evpn summary 4 | show bgp l2vpn evpn 5 | show ip pim neighbor 6 | show ip pim rp map 7 | show ip rpf 172.16.255.255 8 | show ip msdp summary 9 | show ip msdp sa-cache 10 | show ip mroute 11 | 12 | -------------------------------------------------------------------------------- /l3vni/output/Spine-02-show_commands.txt: -------------------------------------------------------------------------------- 1 | show run 2 | show ip ospf neighbor 3 | show bgp l2vpn evpn summary 4 | show bgp l2vpn evpn 5 | show ip pim neighbor 6 | show ip pim rp map 7 | show ip rpf 172.16.255.255 8 | show ip msdp summary 9 | show ip msdp sa-cache 10 | show ip mroute 11 | 12 | -------------------------------------------------------------------------------- /dag/playbook_dhcp_delete_commit.yml: -------------------------------------------------------------------------------- 1 | - name: Initialising variable for leaf-s 2 | hosts: leaf 3 | gather_facts: no 4 | tasks: 5 | - set_fact: 6 | action: 'delete' 7 | 8 | - name: Import tasks from playbook_dhcp_add_commit.yml 9 | import_playbook: playbook_dhcp_add_commit.yml 10 | -------------------------------------------------------------------------------- /dag/playbook_dhcp_delete_preview.yml: -------------------------------------------------------------------------------- 1 | - name: Initialising variable for leaf-s 2 | hosts: leaf 3 | gather_facts: no 4 | tasks: 5 | - set_fact: 6 | action: 'delete' 7 | 8 | - name: Import tasks from playbook_dhcp_add_preview.yml 9 | import_playbook: playbook_dhcp_add_preview.yml 10 | -------------------------------------------------------------------------------- /l2vni/inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | leaf: 4 | hosts: 5 | Leaf-01: 6 | ansible_host: 10.1.1.1 7 | Leaf-02: 8 | ansible_host: 10.1.1.2 9 | 10 | spine: 11 | hosts: 12 | Spine-01: 13 | ansible_host: 10.1.1.3 14 | Spine-02: 15 | ansible_host: 10.1.1.4 -------------------------------------------------------------------------------- /l3vni/inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | leaf: 4 | hosts: 5 | Leaf-01: 6 | ansible_host: 10.1.1.1 7 | Leaf-02: 8 | ansible_host: 10.1.1.2 9 | 10 | spine: 11 | hosts: 12 | Spine-01: 13 | ansible_host: 10.1.1.3 14 | Spine-02: 15 | ansible_host: 10.1.1.4 16 | -------------------------------------------------------------------------------- /dag/inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | leaf: 4 | hosts: 5 | Leaf-01: 6 | ansible_host: 10.1.1.1 7 | Leaf-02: 8 | ansible_host: 10.1.1.2 9 | 10 | spine: 11 | hosts: 12 | Spine-01: 13 | ansible_host: 10.1.1.3 14 | Spine-02: 15 | ansible_host: 10.1.1.4 16 | 17 | -------------------------------------------------------------------------------- /templates/msdp_peering.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures MSDP peering 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% for index,idata in msdp.items() %} 8 | ! 9 | ip msdp peer {{ idata.peer_ip }} connect-source {{ idata.source_interface }} remote-as {{ idata.remote_as }} 10 | ip msdp cache-sa-state 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /docs/notes.rst: -------------------------------------------------------------------------------- 1 | Notes 2 | ===== 3 | 4 | CLI commands logging 5 | -------------------- 6 | 7 | To observe the changes which are done be Ansible in the terminal relatime, next configuration could be used: 8 | 9 | .. code-block:: 10 | 11 | conf t 12 | archive 13 | log config 14 | logging enable 15 | notify syslog contenttype plaintext 16 | end 17 | term mon 18 | -------------------------------------------------------------------------------- /templates/pim_interfaces.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures pim under interfaces in underlay 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% for interface in interfaces %} 8 | {% set pim_enable = interfaces[interface].pim_enable | default ('no')%} 9 | {% if pim_enable == 'yes' %} 10 | ! 11 | interface {{ interface }} 12 | ip pim sparse-mode 13 | {% endif %} 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /templates/rm_pl_acl.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures route-map for preserve next-hop. 3 | It is needed when Leafs and Spines are in the different ASes. 4 | By default NH will be changed in eBGP session, but it will break VXLAN tunnel - another end will be Spine, not Leaf. 5 | To fix this issue "next-hop unchanged" is used. 6 | #} 7 | 8 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 9 | 10 | route-map BGP-NHU permit 10 11 | set ip next-hop unchanged 12 | -------------------------------------------------------------------------------- /dag/playbook_access_incremental_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Initialising variable for leaf-s 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - set_fact: 8 | incremental: true 9 | 10 | - name: Initialising variable for localhost 11 | hosts: localhost 12 | gather_facts: no 13 | tasks: 14 | - set_fact: 15 | incremental: true 16 | 17 | - name: Import tasks from playbook_access_add_commit.yml 18 | import_playbook: playbook_access_add_commit.yml 19 | -------------------------------------------------------------------------------- /dag/playbook_access_incremental_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Initialising variable for leaf-s 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - set_fact: 8 | incremental: true 9 | 10 | - name: Initialising variable for localhost 11 | hosts: localhost 12 | gather_facts: no 13 | tasks: 14 | - set_fact: 15 | incremental: true 16 | 17 | - name: Import tasks from playbook_access_add_preview.yml 18 | import_playbook: playbook_access_add_preview.yml 19 | -------------------------------------------------------------------------------- /l2vni/playbook_access_incremental_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Initialising variable for leaf-s 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - set_fact: 8 | incremental: true 9 | 10 | - name: Initialising variable for localhost 11 | hosts: localhost 12 | gather_facts: no 13 | tasks: 14 | - set_fact: 15 | incremental: true 16 | 17 | - name: Import tasks from playbook_access_add_commit.yml 18 | import_playbook: playbook_access_add_commit.yml 19 | -------------------------------------------------------------------------------- /l2vni/playbook_access_incremental_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Initialising variable for leaf-s 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - set_fact: 8 | incremental: true 9 | 10 | - name: Initialising variable for localhost 11 | hosts: localhost 12 | gather_facts: no 13 | tasks: 14 | - set_fact: 15 | incremental: true 16 | 17 | - name: Import tasks from playbook_access_add_preview.yml 18 | import_playbook: playbook_access_add_preview.yml 19 | -------------------------------------------------------------------------------- /l2vni/playbook_yml_validation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Overlay "group_vars/overlay_db.yml" validation 4 | hosts: all 5 | gather_facts: no 6 | tasks: 7 | - name: run the "group_vars/overlay_db.yml" validation module 8 | run_once: true 9 | precheck_l2vni_yml: 10 | fileName: "group_vars/overlay_db.yml" 11 | register: result 12 | 13 | - name: Print result 14 | run_once: true 15 | debug: 16 | msg: "Yaml file validation : {{ result}}'" 17 | -------------------------------------------------------------------------------- /dag/playbook_trm_overlay_incremental_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Initialising variable for nodes 4 | hosts: all 5 | gather_facts: no 6 | tasks: 7 | - set_fact: 8 | trm_inc: true 9 | run_once: true 10 | 11 | - name: Initialising variable for localhost 12 | hosts: localhost 13 | gather_facts: no 14 | run_once: true 15 | tasks: 16 | - set_fact: 17 | trm_inc: true 18 | 19 | - name: Import tasks from playbook_trm_overlay_commit.yml 20 | import_playbook: playbook_trm_overlay_commit.yml 21 | -------------------------------------------------------------------------------- /subtasks/subtask_acc_intf_access.yml: -------------------------------------------------------------------------------- 1 | - name: Set interface type 2 | set_fact: 3 | intf_mode: "access" 4 | 5 | - name: Get VLAN/s list from file in host_vars/access_intf 6 | when: '"access_vlan" in get_Leaf_vars["access_interfaces"]' 7 | set_fact: 8 | vlan_id_common: "{{ get_Leaf_vars['access_interfaces']['access_vlan'] }}" 9 | 10 | - name: Loop through interfaces 11 | include_tasks: "{{ execute_task }}" 12 | loop: "{{ get_Leaf_vars['access_interfaces']['access'] }}" 13 | loop_control: 14 | loop_var: intf_item 15 | -------------------------------------------------------------------------------- /dag/playbook_trm_overlay_incremental_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Initialising variable for nodes 4 | hosts: all 5 | gather_facts: no 6 | tasks: 7 | - set_fact: 8 | trm_inc: true 9 | run_once: true 10 | 11 | - name: Initialising variable for localhost 12 | hosts: localhost 13 | gather_facts: no 14 | run_once: true 15 | tasks: 16 | - set_fact: 17 | trm_inc: true 18 | 19 | - name: Import tasks from playbook_trm_overlay_preview.yml 20 | import_playbook: playbook_trm_overlay_preview.yml 21 | -------------------------------------------------------------------------------- /subtasks/subtask_get_access_vlan.yml: -------------------------------------------------------------------------------- 1 | - name: Collect vars from overlay_db input file 2 | run_once: true 3 | include_vars: 4 | file: "{{ input_vars_path }}group_vars/overlay_db.yml" 5 | name: overlay_data 6 | 7 | - name: Get access-type vlans 8 | block: 9 | - set_fact: 10 | vlan_id_all: [] 11 | 12 | - set_fact: 13 | vlan_id_all: "{{ vlan_id_all + [ item ] }}" 14 | when: overlay_data['vlans'][item]['vlan_type'] == 'access' 15 | loop: "{{ overlay_data.vlans.keys() }}" 16 | 17 | - set_fact: 18 | counter: 1 19 | -------------------------------------------------------------------------------- /lumache.py: -------------------------------------------------------------------------------- 1 | """ 2 | Lumache - Python library for cooks and food lovers. 3 | """ 4 | 5 | __version__ = "0.1.0" 6 | 7 | 8 | class InvalidKindError(Exception): 9 | """Raised if the kind is invalid.""" 10 | pass 11 | 12 | 13 | def get_random_ingredients(kind=None): 14 | """ 15 | Return a list of random ingredients as strings. 16 | 17 | :param kind: Optional "kind" of ingredients. 18 | :type kind: list[str] or None 19 | :raise lumache.InvalidKindError: If the kind is invalid. 20 | :return: The ingredients list. 21 | :rtype: list[str] 22 | """ 23 | return ["shells", "gorgonzola", "parsley"] 24 | -------------------------------------------------------------------------------- /templates/underlay_interfaces.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures ip addresses for underlay interfaces 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% for interface in interfaces%} 8 | ! 9 | interface {{ interface }} 10 | {% if interfaces[interface].name is defined %} 11 | description {{ interfaces[interface].name }} 12 | {% endif %} 13 | 14 | {% if interfaces[interface].loopback == 'no'%} 15 | no switchport 16 | {% endif %} 17 | ip address {{ interfaces[interface].ip_address }} {{ interfaces[interface].subnet_mask }} 18 | no shut 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /templates/rp_interfaces_trm.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures TRM RP loopback interfaces 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% for intf in lpbk_to_be_configd %} 8 | ! 9 | interface {{ intf }} 10 | description RP loopback for VRF {{ rp_intf[intf].vrf }} 11 | 12 | vrf forwarding {{ rp_intf[intf].vrf }} 13 | 14 | {% if rp_intf[intf].ipv4 is defined %} 15 | ip address {{ rp_intf[intf].ipv4 }} 16 | {% endif %} 17 | 18 | ip pim sparse-mode 19 | 20 | {% if rp_intf[intf].ipv6 is defined %} 21 | ipv6 address {{ rp_intf[intf].ipv6 }} 22 | ipv6 enable 23 | {% endif %} 24 | 25 | {% endfor %} 26 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /templates/l2vpn_evpn_global.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures global l2vpn evpn definition. 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {# global l2vpn evpn definition#} 8 | ! 9 | l2vpn evpn 10 | 11 | {# configure default replication type #} 12 | {% if l2vpn_global.replication_type is defined %} 13 | replication-type {{ l2vpn_global.replication_type }} 14 | {% endif %} 15 | 16 | {# configure router-id #} 17 | {% if l2vpn_global.router_id is defined %} 18 | router-id {{ l2vpn_global.router_id }} 19 | {% endif %} 20 | 21 | {# configure default-gateway advertise #} 22 | {% if l2vpn_global.default_gw is defined and l2vpn_global.default_gw == "yes"%} 23 | default-gateway advertise 24 | {% endif %} 25 | -------------------------------------------------------------------------------- /dag/playbook_yml_validation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Overlay "group_vars/overlay_db.yml" validation 4 | hosts: all 5 | gather_facts: no 6 | tasks: 7 | - name: run the "group_vars/overlay_db.yml" validation module 8 | run_once: true 9 | precheck_yml: 10 | fileName: "group_vars/overlay_db.yml" 11 | #debug : 'verbose'( displaying complete output if validation is successful else displays the errors) 12 | #debug : ' '(displays only error senarios if found any) 13 | debug : '' 14 | register: result 15 | 16 | - name: Print result 17 | run_once: true 18 | debug: 19 | msg: "Yaml file validation : {{ result}}'" 20 | -------------------------------------------------------------------------------- /dag/playbook_cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Clean up the config 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | 9 | - name: replace base config 10 | when: inventory_hostname in groups['leaf'] 11 | ios_command: 12 | commands: 13 | - show run 14 | - configure replace bootflash:default_config.txt force 15 | - show run 16 | 17 | - name: replace base config 18 | when: inventory_hostname in groups['spine'] 19 | ios_command: 20 | commands: 21 | - show run 22 | - configure replace bootflash:default_config.txt force 23 | - show run 24 | -------------------------------------------------------------------------------- /l2vni/playbook_cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Clean up the config 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | 9 | - name: replace base config 10 | when: inventory_hostname in groups['leaf'] 11 | ios_command: 12 | commands: 13 | - show run 14 | - configure replace bootflash:default_config.txt force 15 | - show run 16 | 17 | - name: replace base config 18 | when: inventory_hostname in groups['spine'] 19 | ios_command: 20 | commands: 21 | - show run 22 | - configure replace bootflash:default_config.txt force 23 | - show run 24 | -------------------------------------------------------------------------------- /l3vni/playbook_cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Clean up the config 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | 9 | - name: replace base config 10 | when: inventory_hostname in groups['leaf'] 11 | ios_command: 12 | commands: 13 | - show run 14 | - configure replace bootflash:default_config.txt force 15 | - show run 16 | 17 | - name: replace base config 18 | when: inventory_hostname in groups['spine'] 19 | ios_command: 20 | commands: 21 | - show run 22 | - configure replace bootflash:default_config.txt force 23 | - show run 24 | -------------------------------------------------------------------------------- /l3vni/playbook_yml_validation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Overlay "group_vars/overlay_db.yml" validation 4 | hosts: all 5 | gather_facts: no 6 | tasks: 7 | - name: run the "group_vars/overlay_db.yml" validation module 8 | run_once: true 9 | precheck_l3vni_yml: 10 | fileName: "group_vars/overlay_db.yml" 11 | #debug : 'verbose'( displaying complete output if validation is successful else displays the errors) 12 | #debug : ' '(displays only error senarios if found any) 13 | debug : '' 14 | register: result 15 | 16 | - name: Print result 17 | run_once: true 18 | debug: 19 | msg: "Yaml file validation : {{ result}}'" 20 | -------------------------------------------------------------------------------- /templates/global_routing.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is enabling IPv4 unicast, IPv6 unicast and IPv4 multicast routing for global routing table 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% if routing is defined %} 8 | 9 | {# Enabling IPv4 unicast routing #} 10 | {% if routing.ipv4_uni is defined and routing.ipv4_uni == 'yes' %} 11 | ip routing 12 | {% endif %} 13 | 14 | {# Enabling IPv6 unicast routing #} 15 | {% if routing.ipv6_uni is defined and routing.ipv6_uni == 'yes' %} 16 | ipv6 unicast-routing 17 | {% endif %} 18 | 19 | {# Enabling IPv4 multicast routing #} 20 | {% if routing.ipv4_multi is defined and routing.ipv4_multi == 'yes' %} 21 | ip multicast-routing 22 | {% endif %} 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /templates/pim_interfaces_trm.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module enable/disable PIM on the interfaces for incremetal TRM add/delete 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% set action = action | default ('add') %} 8 | {% if action == 'delete' %} 9 | {% set prefix = 'no' %} 10 | {% elif action == 'add' %} 11 | {% set prefix = '' %} 12 | {% endif %} 13 | 14 | {% if svis is defined %} 15 | {% for svi in svis %} 16 | ! 17 | interface Vlan{{svi}} 18 | {{ prefix }} ip pim sparse-mode 19 | {% endfor %} 20 | {% endif %} 21 | 22 | {% if rp_loopbacks is defined %} 23 | {% for rp_lpbk in rp_loopbacks %} 24 | ! 25 | interface {{rp_lpbk}} 26 | {{ prefix }} ip pim sparse-mode 27 | {% endfor %} 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /l3vni/group_vars/delete_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_overlay_delete_preview.yml and playbook_overlay_delete_commit.yml 2 | # 3 | # List the VRFs to be deleted under 'l3vni' keyword 4 | # ______________________________________________ 5 | # __________ EXAMPLE SCENARIOS _________________ 6 | # ______________________________________________ 7 | # 8 | # Example 1: Deletes blue and green VRFs 9 | # 10 | # l3vni: 11 | # - blue <--------- deletes blue and green VRFs 12 | # - green 13 | # 14 | # 15 | # Example 2: Deletes all the VRFs 16 | # 17 | # l3vni: 18 | # - all <--------- deletes all the VRFs 19 | # 20 | # ______________________________________________ 21 | # ______________________________________________ 22 | # ______________________________________________ 23 | 24 | 25 | l3vni: 26 | - all 27 | -------------------------------------------------------------------------------- /templates/vlan_assignment.j2: -------------------------------------------------------------------------------- 1 | {# DEPRICATED. Replaced by access_interfaces.j2 #} 2 | 3 | {% for vlan in vlans %} 4 | {% if ( (vlan_cli is defined and vlan in vlan_cli) or (vlan_cli is not defined) ) %} 5 | {% if vlans[vlan].vlan_type == 'access' %} 6 | 7 | {% if vlans[vlan].port_type == 'access'%} 8 | ! 9 | interface {{ vlans[vlan].port }} 10 | switchport 11 | switchport mode access 12 | switchport access vlan {{ vlan }} 13 | no shut 14 | {% endif %} 15 | 16 | {% if vlans[vlan].port_type == 'trunk'%} 17 | ! 18 | interface {{ vlans[vlan].port }} 19 | switchport 20 | switchport mode trunk 21 | no shut 22 | {% endif %} 23 | 24 | {% endif %} 25 | {% endif %} 26 | {% endfor %} 27 | -------------------------------------------------------------------------------- /l3vni/group_vars/ipv6_create_vars.yml: -------------------------------------------------------------------------------- 1 | # ipv6_incremental: 2 | # ================== 3 | # This is the input file for playbook_overlay_incremental_ipv6_preview.py and playbook_overlay_incremental_ipv6_commit.py 4 | #Please find below the examples of declaring the respective vrf's under l3vni for ipv6_incremental 5 | # 1) with all vrf's 6 | # .......................................... 7 | # . l3vni: . 8 | # . - all ---------------->adds ipv6 for all the vrf's . . 9 | # .......................................... 10 | # 2) with two vrf's 11 | # .......................................... 12 | # . l3vni: . 13 | # . - green ---------------->adds ipv6 for green and blue vrf's . . 14 | # . - blue . . 15 | # .......................................... 16 | # 17 | l3vni: 18 | - all 19 | -------------------------------------------------------------------------------- /dag/group_vars/ipv6_delete_vars.yml: -------------------------------------------------------------------------------- 1 | # ipv6_delete: 2 | # ================== 3 | # This is the input file for playbook_overlay_delete_ipv6_preview.py and playbook_overlay_delete_ipv6_commit.py 4 | #Please find below the examples of declaring the respective dag's for ipv6_delete 5 | # 1) with all vrf's under dag 6 | # .......................................... 7 | # . dag: . 8 | # . - all -------------> deletes ipv6 from all the vrfs which are provisioned . . 9 | # .......................................... 10 | # 2) with two vrf's under dag 11 | # .......................................... 12 | # . dag: . 13 | # . - green --------------> deletes ipv6 from green and blue vrfs 14 | # . - blue . . 15 | # .......................................... 16 | # 17 | dag: 18 | - all 19 | -------------------------------------------------------------------------------- /l3vni/output/Leaf-show_commands.txt: -------------------------------------------------------------------------------- 1 | show run nve 2 | show nve peers 3 | show l2vpn evpn peers vxlan 4 | show bgp l2vpn evpn summary 5 | show bgp l2vpn evpn 6 | 7 | 8 | show l2vpn evpn evi 101 detail 9 | show l2vpn evpn mac evi 101 10 | show l2vpn evpn mac ip evi 101 11 | show l2fib bridge-domain 101 detail 12 | 13 | show l2vpn evpn evi 102 detail 14 | show l2vpn evpn mac evi 102 15 | show l2vpn evpn mac ip evi 102 16 | show l2fib bridge-domain 102 detail 17 | 18 | show l2vpn evpn evi 201 detail 19 | show l2vpn evpn mac evi 201 20 | show l2vpn evpn mac ip evi 201 21 | show l2fib bridge-domain 201 detail 22 | 23 | show l2vpn evpn evi 202 detail 24 | show l2vpn evpn mac evi 202 25 | show l2vpn evpn mac ip evi 202 26 | show l2fib bridge-domain 202 detail 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /templates/ospf_interfaces.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures underlay OSPF process and interfaces. 3 | Default OSPF network type in underlay is P2P for speeding up the convergence time. 4 | #} 5 | 6 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 7 | 8 | {# check if OSPF process is defined in the config for the Leaf/Spine #} 9 | {% if ospf is defined %} 10 | 11 | {% set process_id = ospf.process_id | default ('1') %} 12 | {# configure ospf under interface and set ospf interface P2P if not loopback #} 13 | {% for interface in interfaces%} 14 | ! 15 | interface {{ interface }} 16 | {% if interfaces[interface].loopback == 'no'%} 17 | ip ospf network point-to-point 18 | {% endif %} 19 | ip ospf 1 area 0 20 | {% endfor %} 21 | 22 | {# configure ospf process and ospf router-id #} 23 | ! 24 | router ospf {{ ospf.process_id }} 25 | router-id {{ ospf.router_id }} 26 | {% endif %} -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /l3vni/group_vars/ipv6_delete_vars.yml: -------------------------------------------------------------------------------- 1 | # ipv6_delete: 2 | # ================== 3 | # This is the input file for playbook_overlay_delete_ipv6_preview.py and playbook_overlay_delete_ipv6_commit.py 4 | #Please find below the examples of declaring the respective vrf's under dag for ipv6_delete 5 | # 1) with all vrf's 6 | # .......................................... 7 | # . l3vni: . 8 | # . - all -------------> deletes ipv6 for all the vrf's that are provisioned in the device . . 9 | # .......................................... 10 | # 2) with two vrf's 11 | # .......................................... 12 | # . l3vni: . 13 | # . - green -------------> deletes ipv6 for green and blue vrf's that are provisioned n the device 14 | # . - blue . . 15 | # .......................................... 16 | # 17 | l3vni: 18 | - all 19 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Cat9k EVPN Ansible documentation documentation master file, created by 2 | sphinx-quickstart on Fri Jun 10 12:02:38 2022. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Cat9k EVPN Ansible 7 | ================== 8 | 9 | .. toctree:: 10 | :maxdepth: 3 11 | :caption: General information 12 | 13 | about 14 | topology 15 | installation 16 | notes 17 | 18 | .. toctree:: 19 | :maxdepth: 3 20 | :caption: Distributed Anycast GW (DAG) 21 | 22 | input_dag 23 | playbooks_dag 24 | 25 | .. toctree:: 26 | :maxdepth: 3 27 | :caption: L2 Overlay (L2VNI) 28 | 29 | input_l2vni 30 | playbooks_l2vni 31 | 32 | .. toctree:: 33 | :maxdepth: 3 34 | :caption: L3 Overlay (L3VNI) 35 | 36 | input_l3vni 37 | playbooks_l3vni 38 | 39 | .. Indices and tables 40 | .. ================== 41 | 42 | .. * :ref:`genindex` 43 | .. * :ref:`modindex` 44 | .. * :ref:`search` 45 | -------------------------------------------------------------------------------- /templates/overlay_interfaces.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures overlay interfaces 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% if overlay_interfaces is defined %} 8 | {% for intf in overlay_interfaces %} 9 | {% if ((ovrl_intf_cli is defined and intf in ovrl_intf_cli) or (ovrl_intf_cli is not defined)) %} 10 | {# Checking an action for the vlan - action_vlan. #} 11 | {% set action = overlay_interfaces[intf].action | default ('add') %} 12 | ! 13 | interface {{ intf }} 14 | {% if action == 'delete' -%} 15 | no vrf forwarding {{ overlay_interfaces[intf].vrf }} 16 | {% elif action == 'add' %} 17 | vrf forwarding {{ overlay_interfaces[intf].vrf }} 18 | ip address {{ overlay_interfaces[intf].ip_address }} {{ overlay_interfaces[intf].subnet_mask }} 19 | {% endif %} 20 | no shutdown 21 | {% endif %} 22 | {% endfor %} 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /templates/vlan_create.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring, deleting or modifing vlan configuration. 3 | If action is not set in the configurations, it is "add" by default. 4 | #} 5 | 6 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 7 | 8 | {% if vlans is defined%} 9 | {% for vlan in vlans %} 10 | {% if ( (vlan_cli is defined and vlan in vlan_cli) or (vlan_cli is not defined) ) %} 11 | {# Checking an action for the vlan - action_vlan. #} 12 | {% set action = vlans[vlan].action | default ('add') %} 13 | {% if action == 'delete' %} 14 | ! 15 | no vlan {{vlan}} 16 | no vlan configuration {{vlan}} 17 | {% elif action == 'add' %} 18 | ! 19 | vlan {{vlan}} 20 | {% if vlans[vlan].description is defined %} 21 | name {{ vlans[vlan].description }} 22 | {% endif %} 23 | {% endif %} 24 | {% endif %} 25 | {% endfor %} 26 | {% endif %} 27 | -------------------------------------------------------------------------------- /dag/group_vars/create_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_overlay_incremental_preview.yml and playbook_overlay_incremental_commit.yml 2 | # 3 | # If there are multiple DAGs in the overlay_db.yml and if you want to provision only selective DAGs then declare the respective DAGs as shown below 4 | # 5 | # List the DAG/s to be added under 'dag' keyword 6 | # 7 | # 8 | # ______________________________________________ 9 | # __________ EXAMPLE SCENARIOS _________________ 10 | # ______________________________________________ 11 | # 12 | # Example: Adds blue and green vrf's under DAGs 13 | # 14 | # dag: 15 | # - blue <--------- adds blue and green DAGs 16 | # - green 17 | # 18 | # Example: Adds all the vrf's under DAGs which are not provisioned 19 | # 20 | # dag: 21 | # - all <--------- adds all the vrf's under DAGs which are not provisioned 22 | # ______________________________________________ 23 | # ______________________________________________ 24 | # ______________________________________________ 25 | 26 | dag: 27 | - all 28 | 29 | -------------------------------------------------------------------------------- /l2vni/group_vars/overlay_db.yml: -------------------------------------------------------------------------------- 1 | l2vpn_global: 2 | replication_type: 'static' 3 | router_id: 'Loopback1' 4 | 5 | vlans: 6 | 101: 7 | vlan_type: 'access' 8 | description: 'Access_VLAN_101' 9 | vni: '10101' 10 | evi: '101' 11 | type: 'vlan-based' 12 | encapsulation: 'vxlan' 13 | replication_type: 'static' 14 | replication_mcast: '225.0.0.101' 15 | 16 | 102: 17 | vlan_type: 'access' 18 | description: 'Access_VLAN_102' 19 | vni: '10102' 20 | evi: '102' 21 | type: 'vlan-based' 22 | encapsulation: 'vxlan' 23 | replication_type: 'ingress' 24 | 25 | 103: 26 | vlan_type: 'access' 27 | description: 'Access_VLAN_103' 28 | vni: '10103' 29 | evi: '103' 30 | type: 'vlan-based' 31 | encapsulation: 'vxlan' 32 | replication_type: 'static' 33 | replication_mcast: '225.0.0.101' 34 | 35 | 104: 36 | vlan_type: 'access' 37 | description: 'Access_VLAN_104' 38 | vni: '10104' 39 | evi: '104' 40 | type: 'vlan-based' 41 | encapsulation: 'vxlan' 42 | replication_type: 'ingress' 43 | 44 | nve_interfaces: 45 | 1: 46 | source_interface: 'Loopback1' 47 | -------------------------------------------------------------------------------- /l3vni/group_vars/create_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_overlay_incremental_preview.yml and playbook_overlay_incremental_commit.yml 2 | # 3 | # If there are multiple VRFs in the overlay_db.yml and if you want to provision only selective VRFs or all of VRFs,then declare the respective VRFs under L3VNI as shown below 4 | # 5 | # List the VRFs to be added under 'l3vni' keyword 6 | # 7 | # 8 | # ______________________________________________ 9 | # __________ EXAMPLE SCENARIOS _________________ 10 | # ______________________________________________ 11 | # 12 | # Example-1: Adds blue and green vrf's under DAG 13 | # 14 | # l3vni: 15 | # - blue <--------- adds blue and green vrf's 16 | # - green 17 | # 18 | # Example-2: Adds all the vrf's which are not provisioned in the device 19 | # 20 | # l3vni: 21 | # - all <--------- adds all vrf's which are not provisioned in the device 22 | # ______________________________________________ 23 | # ______________________________________________ 24 | # ______________________________________________ 25 | 26 | l3vni: 27 | - all 28 | -------------------------------------------------------------------------------- /templates/evi_vni_vlan_stiching.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring, deleting or modifing l2vpn evpn evi to vlan stitching. 3 | If action is not set in the configurations, it is "add" by default. 4 | #} 5 | 6 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 7 | 8 | {% for ivlan,idata in vlans.items() %} 9 | {% if ((vlan_cli is defined and ivlan in vlan_cli) or (vlan_cli is not defined)) %} 10 | ! 11 | vlan configuration {{ ivlan }} 12 | 13 | {# Checking an action for vlan - action_stitching #} 14 | {% set action = vlans[ivlan].action_stitching | default ('add') %} 15 | {% if action == 'delete' %} 16 | {% set prefix = 'no' %} 17 | {% else %} 18 | {% set prefix = '' %} 19 | {% endif %} 20 | 21 | {# vlan configuration for l2 or l3 VNI #} 22 | {% if idata.vlan_type == 'access' %} 23 | {{prefix}} member evpn-instance {{idata.evi}} vni {{idata.vni}} 24 | {% elif idata.vlan_type == 'core' %} 25 | {{prefix}} member vni {{idata.vni}} 26 | {% endif %} 27 | {% endif %} 28 | {% endfor %} 29 | -------------------------------------------------------------------------------- /templates/bgp_ipv4_mvpn_af.j2: -------------------------------------------------------------------------------- 1 | {# 2 | 3 | This module is configuring MVPN AF for IPv4 and activates neighbors under AF 4 | 5 | #} 6 | 7 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 8 | 9 | {# Entering bgp process configuration #} 10 | router bgp {{ bgp.as_number }} 11 | 12 | {# Check if IPV4 MVPN AF is enabled #} 13 | {% set ipv4_mvpn_enable = bgp.ipv4_mvpn_enable | default ('no')%} 14 | {% if ipv4_mvpn_enable == 'yes' %} 15 | address-family ipv4 mvpn 16 | 17 | {# Enable neighbour unde MVPN AF if it is needed #} 18 | {% for neighbor in bgp.neighbors %} 19 | {% if bgp.neighbors[neighbor].ipv4_mvpn == 'yes'%} 20 | neighbor {{neighbor }} activate 21 | 22 | {# configuration of RR-client if needed #} 23 | {% if bgp.neighbors[neighbor].rrc is defined and bgp.neighbors[neighbor].rrc == 'yes' %} 24 | neighbor {{ neighbor }} inherit peer-policy SPINE-EVPN-PEER-POLICY 25 | {% else %} 26 | neighbor {{ neighbor }} inherit peer-policy LEAF-EVPN-PEER-POLICY 27 | {% endif %} 28 | 29 | {% endif %} 30 | {% endfor %} 31 | {% endif %} 32 | -------------------------------------------------------------------------------- /templates/bgp_ipv4_mvpn_af_trm.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring MVPN AF for IPv4 and activates neighbors under AF 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% set action = trm_action | default ('add') %} 8 | {% if action == 'delete' %} 9 | {% set prefix = 'no' %} 10 | {% elif action == 'add' %} 11 | {% set prefix = '' %} 12 | {% endif %} 13 | 14 | {# Entering bgp process configuration #} 15 | router bgp {{ bgp.as_number }} 16 | address-family ipv4 mvpn 17 | {% for neighbor in bgp.neighbors %} 18 | {% if bgp.neighbors[neighbor].ipv4_mvpn == 'yes'%} 19 | 20 | {{ prefix }} neighbor {{neighbor }} activate 21 | 22 | {# configuration of RR-client if needed #} 23 | {% if bgp.neighbors[neighbor].rrc is defined and bgp.neighbors[neighbor].rrc == 'yes' %} 24 | {{ prefix }} neighbor {{ neighbor }} inherit peer-policy SPINE-EVPN-PEER-POLICY 25 | {% else %} 26 | {{ prefix }} neighbor {{ neighbor }} inherit peer-policy LEAF-EVPN-PEER-POLICY 27 | {% endif %} 28 | 29 | {% endif %} 30 | {% endfor %} 31 | 32 | -------------------------------------------------------------------------------- /docs/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* Colors definition*/ 2 | 3 | .black { 4 | color: black; 5 | } 6 | 7 | .gray { 8 | color: gray; 9 | } 10 | 11 | .grey { 12 | color: gray; 13 | } 14 | 15 | .silver { 16 | color: silver; 17 | } 18 | 19 | .white { 20 | color: white; 21 | } 22 | 23 | .maroon { 24 | color: maroon; 25 | } 26 | 27 | .red { 28 | color: red; 29 | } 30 | 31 | .magenta { 32 | color: magenta; 33 | } 34 | 35 | .fuchsia { 36 | color: fuchsia; 37 | } 38 | 39 | .pink { 40 | color: pink; 41 | } 42 | 43 | .orange { 44 | color: orange; 45 | } 46 | 47 | .yellow { 48 | color: yellow; 49 | } 50 | 51 | .lime { 52 | color: lime; 53 | } 54 | 55 | .green { 56 | color: green; 57 | } 58 | 59 | .olive { 60 | color: olive; 61 | } 62 | 63 | .teal { 64 | color: teal; 65 | } 66 | 67 | .cyan { 68 | color: cyan; 69 | } 70 | 71 | .aqua { 72 | color: aqua; 73 | } 74 | 75 | .blue { 76 | color: blue; 77 | } 78 | 79 | .navy { 80 | color: navy; 81 | } 82 | 83 | .purple { 84 | color: purple; 85 | } 86 | 87 | /* The table width fix to avoid scrollbar */ 88 | .wy-table-responsive table td { 89 | white-space: normal; 90 | } -------------------------------------------------------------------------------- /docs/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | The main goal of this project is the automation of Campus EVPN Deployment based on Catalyst 9000. 5 | 6 | Custom Jinja templates and Python modules are used to build an initial config and modify the network configuration. 7 | 8 | Project has a modular structure which gives an ability to introduce new features/services gradually step-by-step. 9 | 10 | * `DAG (Distributed Anycast Gateway) `_ 11 | 12 | * `L2 Overlay (L2VNI) `_ 13 | 14 | * `L3 Overlay (L3VNI) `_ 15 | 16 | Prerequisites: 17 | ************** 18 | 19 | To run Cisco cat9k EVPN ansible playbook, you will require: 20 | 21 | **Hardware**: 22 | 23 | * A linux server (Fedroa, Ubuntu, RedHat, etc) 24 | * Cat9k Switches supporting EVPN (from x release) 25 | 26 | **Network-Expertise**: 27 | 28 | * Basic network knowledge (network design, bring up of cat9k switches) 29 | * Basic understanding of YAML 30 | * Basic understanding of Python 31 | * Basic linux command line use 32 | 33 | -------------------------------------------------------------------------------- /templates/isis_underlay.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures underlay ISIS process and interfaces. 3 | Default ISIS network type in underlay is P2P for speeding up the convergence time. 4 | #} 5 | 6 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 7 | 8 | {# check if ISIS process is defined#} 9 | {% if isis is defined %} 10 | 11 | {% set process_id = isis.process_id | default ('UNDERLAY') %} 12 | 13 | {# configure ISIS process #} 14 | router isis {{process_id}} 15 | 16 | {# configure ISIS NET #} 17 | net {{isis.net}} 18 | 19 | {% for p_int in isis.passive_interfaces %} 20 | passive-interface {{p_int}} 21 | {% endfor %} 22 | {# default parameters #} 23 | is-type level-2-only 24 | advertise passive-only 25 | metric-style wide 26 | log-adjacency-changes 27 | {% endif %} 28 | 29 | {# configure ISIS under interface and set ISIS interface P2P if not loopback #} 30 | {% for interface in interfaces%} 31 | ! 32 | interface {{ interface }} 33 | {% if interfaces[interface].loopback == 'no'%} 34 | ip router isis {{process_id}} 35 | isis network point-to-point 36 | {% endif %} 37 | {% endfor %} -------------------------------------------------------------------------------- /templates/bgp_l2vpn_ipv46_per_vrf.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring, deleting or modifing ipv4/ipv6 AF under vrf under BGP. 3 | If action is not set in the configurations, it is "add" by default. 4 | #} 5 | 6 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 7 | 8 | {# Configure BGP AF for VRF #} 9 | {% if vrfs is defined -%} 10 | ! 11 | router bgp {{ bgp.as_number }} 12 | {% for vrf in vrfs -%} 13 | {% if ( (vrf_cli is defined and vrf in vrf_cli) or (vrf_cli is not defined) ) %} 14 | {% for af in vrfs[vrf].afs -%} 15 | ! 16 | address-family {{ af }} vrf {{ vrf }} 17 | advertise l2vpn evpn 18 | redistribute connected 19 | redistribute static 20 | 21 | {# Check if something else needed to be redistributed except static or connected #} 22 | {% if bgp.afs[af].vrf[vrf].redistribute is defined %} 23 | {% for redist in bgp.afs[af].vrf[vrf].redistribute %} 24 | redistribute {{redist}} 25 | {% endfor%} 26 | {% endif %} 27 | 28 | {% endfor%} 29 | {% endif %} 30 | {% endfor %} 31 | {% endif %} 32 | -------------------------------------------------------------------------------- /dag/group_vars/ipv6_create_vars.yml: -------------------------------------------------------------------------------- 1 | # ipv6_incremental: 2 | # ================== 3 | #Please find below the examples of declaring the respective vrf's under dag where it is used as input file to playbook_overlay_incremental_ipv6_generate.py and playbook_overlay_incremental_ipv6_commit.py 4 | # 1) with single vrf under dag 5 | # .......................................... 6 | # . dag: . 7 | # . - green ------------> add ipv6 to the green vrf . . 8 | # .......................................... 9 | # 2) with two vrf's under dag's 10 | # .......................................... 11 | # . dag: . 12 | # . - green ------------> adds ipv6 to the blue and green vrfs . . 13 | # . - blue . . 14 | # .......................................... 15 | # 3) with all vrf's under dag's 16 | # .......................................... 17 | # . dag: . 18 | # . - all -------------> adds ipv6 to all the vrfs which are not provisoned . . 19 | # .......................................... 20 | 21 | dag: 22 | - all 23 | -------------------------------------------------------------------------------- /dag/group_vars/delete_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_overlay_delete_preview.yml and playbook_overlay_delete_commit.yml 2 | # 3 | # List the DAG/s to be deleted under 'dag' keyword 4 | # The keyword 'update_access' when set to true removes the related VLANs from the interfaces 5 | # 6 | # 7 | # ______________________________________________ 8 | # __________ EXAMPLE SCENARIOS _________________ 9 | # ______________________________________________ 10 | # 11 | # Example 1: Deletes blue and green DAGs and removes the respective VLANs from the access interfaces 12 | # 13 | # dag: 14 | # - blue <--------- deletes blue and green DAGs 15 | # - green 16 | # 17 | # update_access: true <--------- removes the respective VLANs from the access interfaces 18 | # 19 | # 20 | # Example 2: Deletes all the DAGs and makes NO changes to the access interfaces 21 | # 22 | # dag: 23 | # - all <--------- deletes all the DAGs 24 | # 25 | # update_access: false <--------- makes NO changes to the access interfaces 26 | # 27 | # ______________________________________________ 28 | # ______________________________________________ 29 | # ______________________________________________ 30 | 31 | 32 | dag: 33 | - blue 34 | 35 | update_access: true 36 | -------------------------------------------------------------------------------- /l2vni/group_vars/delete_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_overlay_delete_preview.yml and playbook_overlay_delete_commit.yml 2 | # 3 | # List the vlans to be deleted under 'vlans' keyword 4 | # The keyword 'update_access' when set to true removes the related VLANs from the interfaces 5 | # 6 | # 7 | # ______________________________________________ 8 | # __________ EXAMPLE SCENARIOS _________________ 9 | # ______________________________________________ 10 | # 11 | # Example 1: Deletes 101 and 102 vlans and removes the respective VLANs from the access interfaces 12 | # 13 | # vlans: 14 | # - 101 <--------- deletes 101 and 102 VLANs 15 | # - 102 16 | # 17 | # update_access: true <--------- removes the respective VLANs from the access interfaces 18 | # 19 | # 20 | # Example 2: Deletes all the VLANs and makes NO changes to the access interfaces 21 | # 22 | # vlans: 23 | # - all <--------- deletes all the VLANs 24 | # 25 | # update_access: false <--------- makes NO changes to the access interfaces 26 | # 27 | # ______________________________________________ 28 | # ______________________________________________ 29 | # ______________________________________________ 30 | 31 | 32 | vlans: 33 | - 104 34 | 35 | update_access: true 36 | -------------------------------------------------------------------------------- /l2vni/host_vars/node_vars/Leaf-01.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Leaf-01' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.3' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'no' 16 | 17 | Loopback1: 18 | name: 'NVE Loopback' 19 | ip_address: '172.16.254.3' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | GigabitEthernet1/0/1: 25 | name: 'Backbone interface to Spine-01' 26 | ip_address: '172.16.13.3' 27 | subnet_mask: '255.255.255.0' 28 | loopback: 'no' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/2: 32 | name: 'Backbone interface to Spine-02' 33 | ip_address: '172.16.23.3' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | ospf: 39 | router_id: '172.16.255.3' 40 | 41 | pim: 42 | rp_address: '172.16.255.255' 43 | 44 | bgp: 45 | as_number: '65001' 46 | router_id: 'Loopback0' 47 | neighbors: 48 | '172.16.255.1': 49 | peer_as_number: '65001' 50 | source_interface: 'Loopback0' 51 | 52 | '172.16.255.2': 53 | peer_as_number: '65001' 54 | source_interface: 'Loopback0' 55 | 56 | -------------------------------------------------------------------------------- /l2vni/host_vars/node_vars/Leaf-02.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Leaf-02' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.4' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'yes' 16 | 17 | Loopback1: 18 | name: 'NVE Loopback' 19 | ip_address: '172.16.254.4' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | GigabitEthernet1/0/1: 25 | name: 'Backbone interface to Spine-01' 26 | ip_address: '172.16.14.4' 27 | subnet_mask: '255.255.255.0' 28 | loopback: 'no' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/2: 32 | name: 'Backbone interface to Spine-02' 33 | ip_address: '172.16.24.4' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | ospf: 39 | router_id: '172.16.255.4' 40 | 41 | pim: 42 | rp_address: '172.16.255.255' 43 | 44 | bgp: 45 | as_number: '65001' 46 | router_id: 'Loopback0' 47 | neighbors: 48 | '172.16.255.1': 49 | peer_as_number: '65001' 50 | source_interface: 'Loopback0' 51 | 52 | '172.16.255.2': 53 | peer_as_number: '65001' 54 | source_interface: 'Loopback0' 55 | 56 | -------------------------------------------------------------------------------- /templates/leaf_show_command.j2: -------------------------------------------------------------------------------- 1 | show run nve 2 | show nve peers 3 | show l2vpn evpn peers vxlan 4 | show bgp l2vpn evpn summary 5 | show bgp l2vpn evpn 6 | 7 | {% if vlans is defined -%} 8 | {% for ivlan,idata in vlans.items() %} 9 | {% if vlans[ivlan].vlan_type == 'access' %} 10 | show l2vpn evpn evi {{idata.evi}} detail 11 | show l2vpn evpn mac evi {{idata.evi}} 12 | show l2vpn evpn mac ip evi {{idata.evi}} 13 | show l2fib bridge-domain {{ivlan}} detail 14 | {% endif %} 15 | {% endfor %} 16 | {% endif %} 17 | 18 | {%- if vrfs is defined -%} 19 | {% for vrf in vrfs -%} 20 | {% for af in vrfs[vrf].afs -%} 21 | {% if af == 'ipv4' %} 22 | {% set ipv4_multicast = vrfs[vrf].ipv4_multicast | default ('false') %} 23 | {%- if ipv4_multicast != 'false' -%} 24 | show bgp ipv4 mvpn all summ 25 | show ip pim vrf {{vrf}} rp mapping 26 | show ip route vrf {{vrf}} {{ vrfs[vrf].rp_address }} 27 | show ip igmp vrf {{vrf}} groups 28 | show ip mroute vrf {{vrf}} 29 | show ip mfib vrf * 30 | show bgp ipv4 mvpn all 31 | show ip mroute 32 | show ip mfib 33 | {% endif %} 34 | {% endif %} 35 | {% endfor %} 36 | {% endfor %} 37 | {% endif %} 38 | 39 | 40 | -------------------------------------------------------------------------------- /subtasks/subtask_acc_intf_commit.yml: -------------------------------------------------------------------------------- 1 | 2 | - name: Set inteface detail 3 | when: " intf_item | type_debug != 'dict' " 4 | set_fact: 5 | intf_name: "{{ intf_item }}" 6 | vlan_id: "{{ vlan_id_common }}" 7 | 8 | - name: Collect custom VLAN/s per interface 9 | when: " intf_item | type_debug == 'dict' " 10 | block: 11 | - set_fact: 12 | intf_name: "{{ item }}" 13 | with_items: "{{ intf_item.keys() }}" 14 | 15 | - name: Get custom VLAN/s 16 | set_fact: 17 | vlan_id: "{{ intf_item[intf_name][info_dict[intf_mode]['key_name']] }}" 18 | 19 | - name: Trunk interface configuration 20 | when: intf_mode == 'trunks' 21 | cisco.ios.ios_l2_interfaces: 22 | config: 23 | - name: "{{ intf_name }}" 24 | mode: trunk 25 | trunk: 26 | allowed_vlans: "{{ vlan_id }}" 27 | state: merged 28 | 29 | - name: Access interface configuration 30 | when: intf_mode == 'access' 31 | cisco.ios.ios_l2_interfaces: 32 | config: 33 | - name: "{{ intf_name }}" 34 | mode: access 35 | access: 36 | vlan: "{{ vlan_id }}" 37 | state: merged 38 | 39 | - name: Apply no shutdown on interfaces 40 | cisco.ios.ios_config: 41 | lines: 42 | - no shutdown 43 | parents: interface {{ intf_name }} 44 | -------------------------------------------------------------------------------- /templates/bgp_mvpn_af_trm.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring MVPN AF for IPv4 and activates neighbors under AF 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {% set action = action | default ('add') %} 8 | {% if action == 'add' %} 9 | {% set prefix = '' %} 10 | {% endif %} 11 | 12 | {# Entering bgp process configuration #} 13 | 14 | ! 15 | router bgp {{ bgp.as_number }} 16 | 17 | {% if action == 'delete' %} 18 | {% if delete_ipv4_mvpn == true %} 19 | ! 20 | no address-family ipv4 mvpn 21 | {% endif %} 22 | {% if delete_ipv6_mvpn == true %} 23 | ! 24 | no address-family ipv6 mvpn 25 | {% endif %} 26 | 27 | {% else %} 28 | {% for mvpn in mvpns %} 29 | ! 30 | address-family {{ mvpn }} mvpn 31 | {% for neighbor in bgp[mvpn+'_mvpn_neighbors'] %} 32 | {{ prefix }} neighbor {{neighbor }} activate 33 | {# configuration of RR-client if needed #} 34 | {% if bgp.rrc is defined and bgp.rrc == 'true' %} 35 | {{ prefix }} neighbor {{ neighbor }} inherit peer-policy SPINE-EVPN-PEER-POLICY 36 | {% else %} 37 | {{ prefix }} neighbor {{ neighbor }} inherit peer-policy LEAF-EVPN-PEER-POLICY 38 | {% endif %} 39 | {% endfor %} 40 | {% endfor %} 41 | {% endif %} 42 | -------------------------------------------------------------------------------- /l3vni/group_vars/overlay_db.yml: -------------------------------------------------------------------------------- 1 | #l3vni overlay_db.yml 2 | l2vpn_global: 3 | router_id: 'Loopback1' 4 | 5 | vrfs: 6 | green: 7 | ipv6_unicast: 'enable' 8 | rd: '1:1' 9 | afs: 10 | ipv4: 11 | rt_import: 12 | - '1:1' 13 | - '1:1 stitching' 14 | rt_export: 15 | - '1:1' 16 | - '1:1 stitching' 17 | 18 | ipv6: 19 | rt_import: 20 | - '1:1' 21 | - '1:1 stitching' 22 | rt_export: 23 | - '1:1' 24 | - '1:1 stitching' 25 | 26 | blue: 27 | rd: '2:2' 28 | afs: 29 | ipv4: 30 | rt_import: 31 | - '2:2' 32 | - '2:2 stitching' 33 | rt_export: 34 | - '2:2' 35 | - '2:2 stitching' 36 | ipv6: 37 | rt_import: 38 | - '2:2' 39 | - '2:2 stitching' 40 | rt_export: 41 | - '2:2' 42 | 43 | vlans: 44 | 45 | 901: 46 | vlan_type: 'core' 47 | description: 'Core_VLAN_VRF_green' 48 | vni: '50901' 49 | vrf: 'green' 50 | 51 | 902: 52 | vlan_type: 'core' 53 | description: 'Core_VLAN_VRF_blue' 54 | vni: '50902' 55 | vrf: 'blue' 56 | 57 | svis: 58 | 59 | 901: 60 | svi_type: 'core' 61 | vrf: 'green' 62 | src_intf: 'Loopback1' 63 | ipv6_enable: 'yes' 64 | 65 | 902: 66 | svi_type: 'core' 67 | vrf: 'blue' 68 | src_intf: 'Loopback1' 69 | ipv6_enable: 'yes' 70 | 71 | nve_interfaces: 72 | 1: 73 | source_interface: 'Loopback1' 74 | -------------------------------------------------------------------------------- /dag/group_vars/trm_create_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_trm_overlay_incremental_preview.yml and playbook_trm_overlay_incremental_commit.yml 2 | # 3 | # List the DAG/s where TRM needs to be enabled under 'dag' keyword 4 | # 5 | # Note: Enter the TRM information for listed DAG/s in trm_overlay_db.yml 6 | # 7 | # DAGs where TRM is already enabled are skipped even if they are listed under dag 8 | # To reconfigure DAGs for TRM, either run playbook_trm_overlay_delete_commit.yml (OR) 9 | # set 'check_vrfs_4_trm' key in 'playbook_trm_overlay_incremental_preview/commit.yml' to 'false' 10 | # However, It is advised to use playbook_trm_overlay_delete_commit.yml for reconfiguring 11 | # 12 | # ______________________________________________ 13 | # __________ EXAMPLE SCENARIOS _________________ 14 | # ______________________________________________ 15 | # 16 | # 17 | # Example 1: Enables TRM for DAG 'blue' and 'green' DAGs 18 | # 19 | # dag: 20 | # - blue <--------- Get TRM data for the DAG from trm_overlay_db.yml 21 | # - green 22 | # 23 | # ------------ 24 | # 25 | # Example 2: Enables TRM for all DAGs 26 | # 27 | # dag: 28 | # - all <--------- Enables TRM on all DAG/s mentioned in trm_overlay_db.yml except the ones where TRM is already configured 29 | # 30 | # ______________________________________________ 31 | # ______________________________________________ 32 | # ______________________________________________ 33 | 34 | 35 | dag: 36 | - blue 37 | -------------------------------------------------------------------------------- /templates/ospf_overlay.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module configures overlay OSPF process and interfaces 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {# Configuring ospf processes and interfaces in overlay#} 8 | {% if ospf_overlay is defined -%} 9 | {% for vrf in ospf_overlay.vrf -%} 10 | {% for id in ospf_overlay.vrf[vrf].id -%} 11 | ! 12 | router ospf {{ id }} vrf {{ vrf }} 13 | 14 | {# Check if redistribution is configured #} 15 | {% set redistribute = ospf_overlay.vrf[vrf].id[id].redistribute | default ('not_defined') %} 16 | {% if redistribute != 'not_defined' %} 17 | redistribute {{redistribute}} 18 | {% endif %} 19 | 20 | {# Configuring interfaces. Must be last section -> leaving router ospf <> configuration#} 21 | {% if ospf_overlay.vrf[vrf].id[id].interfaces is defined %} 22 | {% for intf in ospf_overlay.vrf[vrf].id[id].interfaces %} 23 | ! 24 | interface {{intf}} 25 | {% set intf_type = ospf_overlay.vrf[vrf].id[id].interfaces[intf].type | default ('broadcast') %} 26 | {% set intf_area = ospf_overlay.vrf[vrf].id[id].interfaces[intf].area | default ('0') %} 27 | ip ospf network {{intf_type}} 28 | ip ospf {{id}} area {{intf_area}} 29 | {% endfor %} 30 | {% endif %} 31 | {% endfor %} 32 | {% endfor %} 33 | {% endif %} 34 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_delete_ipv6_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write l3vni delete ipv6 info to files in host_vars/ipv6_delete_vars folder 3 | import_playbook: playbook_overlay_delete_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | vars: 9 | svi_ipv6_dict: 10 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 11 | tasks: 12 | - name: Check whether host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 13 | local_action: stat path=./host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 14 | register: file_state 15 | become: no 16 | 17 | - name: Executing tasks if host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 18 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 19 | block: 20 | - name: Load variables from files under host_vars/ipv6_delete_vars folder 21 | include_vars: host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 22 | 23 | - name: SVI configuration 24 | when: inventory_hostname in groups['leaf'] 25 | cli_config: 26 | config: " {{ lookup('template', item.key) }} " 27 | diff_match: none 28 | register: result 29 | with_dict: "{{ svi_ipv6_dict }}" 30 | 31 | - name: Pause for 60 seconds for convergence 32 | wait_for: 33 | delay: 60 34 | timeout: 0 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Cisco Systems, Inc. and/or its affiliates 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /dag/playbook_overlay_delete_ipv6_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write DAG incremental info to files in host_vars/inc_vars folder 3 | import_playbook: playbook_overlay_delete_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | # vars_files: 9 | # - "group_vars/overlay_db.yml" 10 | vars: 11 | svi_ipv6_dict: 12 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 13 | tasks: 14 | - name: Check whether host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 15 | local_action: stat path=./host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 16 | register: file_state 17 | become: no 18 | 19 | - name: Executing tasks if host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 20 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 21 | block: 22 | - name: Load variables from files under host_vars/ipv6_inc_vars folder 23 | include_vars: host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 24 | 25 | - name: SVI configuration 26 | when: inventory_hostname in groups['leaf'] 27 | cli_config: 28 | config: " {{ lookup('template', item.key) }} " 29 | diff_match: none 30 | register: result 31 | with_dict: "{{ svi_ipv6_dict }}" 32 | 33 | - name: Pause for 60 seconds for convergence 34 | wait_for: 35 | delay: 60 36 | timeout: 0 37 | -------------------------------------------------------------------------------- /dag/playbook_overlay_incremental_ipv6_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write DAG incremental info to files in host_vars/inc_vars folder 3 | import_playbook: playbook_overlay_incremental_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | # vars_files: 9 | # - "group_vars/overlay_db.yml" 10 | vars: 11 | svi_ipv6_dict: 12 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 13 | tasks: 14 | - name: Check whether host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 15 | local_action: stat path=./host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 16 | register: file_state 17 | become: no 18 | 19 | - name: Executing tasks if host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 20 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 21 | block: 22 | - name: Load variables from files under host_vars/ipv6_inc_vars folder 23 | include_vars: host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 24 | 25 | - name: SVI configuration 26 | when: inventory_hostname in groups['leaf'] 27 | cli_config: 28 | config: " {{ lookup('template', item.key) }} " 29 | diff_match: none 30 | register: result 31 | with_dict: "{{ svi_ipv6_dict }}" 32 | 33 | - name: Pause for 20 seconds for convergence 34 | wait_for: 35 | delay: 20 36 | timeout: 0 37 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_incremental_ipv6_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write l3vni ipv6 incremental info to files in host_vars/ipv6_inc_vars folder 3 | import_playbook: playbook_overlay_incremental_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | # vars_files: 9 | # - "group_vars/overlay_db.yml" 10 | vars: 11 | svi_ipv6_dict: 12 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 13 | tasks: 14 | - name: Check whether host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 15 | local_action: stat path=./host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 16 | register: file_state 17 | become: no 18 | 19 | - name: Executing tasks if host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 20 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 21 | block: 22 | - name: Load variables from files under host_vars/ipv6_inc_vars folder 23 | include_vars: host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 24 | 25 | - name: SVI configuration 26 | when: inventory_hostname in groups['leaf'] 27 | cli_config: 28 | config: " {{ lookup('template', item.key) }} " 29 | diff_match: none 30 | register: result 31 | with_dict: "{{ svi_ipv6_dict }}" 32 | 33 | - name: Pause for 20 seconds for convergence 34 | wait_for: 35 | delay: 20 36 | timeout: 0 37 | -------------------------------------------------------------------------------- /l2vni/group_vars/create_vars.yml: -------------------------------------------------------------------------------- 1 | # This is the input file for playbook_overlay_incremental_preview.yml and playbook_overlay_incremental_commit.yml 2 | # 3 | # If there are multiple vlans in the overlay_db.yml and if you want to provision only selective vlans then declare the respective vlans as shown below 4 | # 5 | # List the vlans to be added under 'vlans' keyword 6 | # 7 | # 8 | # ______________________________________________ 9 | # __________ EXAMPLE SCENARIO : 1 _________________ 10 | # ______________________________________________ 11 | # 12 | # Example: Adds 101 and 102 vlans present in overlay_db.yml 13 | # 14 | # vlans: 15 | # - 101 <--------- adds 101 and 102 vlans 16 | # - 102 17 | # 18 | # ______________________________________________ 19 | # ______________________________________________ 20 | # ______________________________________________ 21 | # 22 | # If there are multiple vlans in the overlay_db.yml and if you want to provision all the vlans then declare the "all" keyword under "vlans" as shown below 23 | # 24 | # List the vlans to be added under 'vlans' keyword 25 | # 26 | # 27 | # ______________________________________________ 28 | # __________ EXAMPLE SCENARIO : 2 _________________ 29 | # ______________________________________________ 30 | # 31 | # Example: Adds all the vlans present in overlay_db.yml 32 | # 33 | # vlans: 34 | # - all <--------- adds all the vlans which are not provisioned 35 | # 36 | # ______________________________________________ 37 | # ______________________________________________ 38 | # ______________________________________________ 39 | 40 | vlans: 41 | - all 42 | 43 | -------------------------------------------------------------------------------- /l2vni/host_vars/node_vars/Spine-01.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Spine-01' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv4_multi: 'yes' 6 | 7 | interfaces: 8 | 9 | Loopback0: 10 | name: 'Routing Loopback' 11 | ip_address: '172.16.255.1' 12 | subnet_mask: '255.255.255.255' 13 | loopback: 'yes' 14 | pim_enable: 'no' 15 | 16 | Loopback1: 17 | name: 'MSDP Loopback' 18 | ip_address: '172.16.254.1' 19 | subnet_mask: '255.255.255.255' 20 | loopback: 'yes' 21 | pim_enable: 'yes' 22 | 23 | Loopback2: 24 | name: 'AnycastRP Loopback' 25 | ip_address: '172.16.255.255' 26 | subnet_mask: '255.255.255.255' 27 | loopback: 'yes' 28 | pim_enable: 'yes' 29 | 30 | GigabitEthernet1/0/1: 31 | name: 'Backbone interface to Leaf-01' 32 | ip_address: '172.16.13.1' 33 | subnet_mask: '255.255.255.0' 34 | loopback: 'no' 35 | pim_enable: 'yes' 36 | 37 | GigabitEthernet1/0/2: 38 | name: 'Backbone interface to Leaf-02' 39 | ip_address: '172.16.14.1' 40 | subnet_mask: '255.255.255.0' 41 | loopback: 'no' 42 | pim_enable: 'yes' 43 | 44 | ospf: 45 | router_id: '172.16.255.1' 46 | 47 | pim: 48 | rp_address: '172.16.255.255' 49 | 50 | msdp: 51 | '1': 52 | peer_ip: '172.16.254.2' 53 | source_interface: 'Loopback1' 54 | remote_as: '65001' 55 | 56 | bgp: 57 | as_number: '65001' 58 | router_id: 'Loopback0' 59 | neighbors: 60 | '172.16.255.2': 61 | peer_as_number: '65001' 62 | source_interface: 'Loopback0' 63 | rrc: 'yes' 64 | 65 | '172.16.255.3': 66 | peer_as_number: '65001' 67 | source_interface: 'Loopback0' 68 | rrc: 'yes' 69 | 70 | '172.16.255.4': 71 | peer_as_number: '65001' 72 | source_interface: 'Loopback0' 73 | rrc: 'yes' -------------------------------------------------------------------------------- /l3vni/host_vars/node_vars/Spine-01.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Spine-01' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv4_multi: 'yes' 6 | 7 | interfaces: 8 | 9 | Loopback0: 10 | name: 'Routing Loopback' 11 | ip_address: '172.16.255.1' 12 | subnet_mask: '255.255.255.255' 13 | loopback: 'yes' 14 | pim_enable: 'no' 15 | 16 | Loopback1: 17 | name: 'MSDP Loopback' 18 | ip_address: '172.16.254.1' 19 | subnet_mask: '255.255.255.255' 20 | loopback: 'yes' 21 | pim_enable: 'yes' 22 | 23 | Loopback2: 24 | name: 'AnycastRP Loopback' 25 | ip_address: '172.16.255.255' 26 | subnet_mask: '255.255.255.255' 27 | loopback: 'yes' 28 | pim_enable: 'yes' 29 | 30 | GigabitEthernet1/0/1: 31 | name: 'Backbone interface to Leaf-01' 32 | ip_address: '172.16.13.1' 33 | subnet_mask: '255.255.255.0' 34 | loopback: 'no' 35 | pim_enable: 'yes' 36 | 37 | GigabitEthernet1/0/2: 38 | name: 'Backbone interface to Leaf-02' 39 | ip_address: '172.16.14.1' 40 | subnet_mask: '255.255.255.0' 41 | loopback: 'no' 42 | pim_enable: 'yes' 43 | 44 | ospf: 45 | router_id: '172.16.255.1' 46 | 47 | pim: 48 | rp_address: '172.16.255.255' 49 | 50 | msdp: 51 | '1': 52 | peer_ip: '172.16.254.2' 53 | source_interface: 'Loopback1' 54 | remote_as: '65001' 55 | 56 | bgp: 57 | as_number: '65001' 58 | router_id: 'Loopback0' 59 | neighbors: 60 | '172.16.255.2': 61 | peer_as_number: '65001' 62 | source_interface: 'Loopback0' 63 | rrc: 'yes' 64 | 65 | '172.16.255.3': 66 | peer_as_number: '65001' 67 | source_interface: 'Loopback0' 68 | rrc: 'yes' 69 | 70 | '172.16.255.4': 71 | peer_as_number: '65001' 72 | source_interface: 'Loopback0' 73 | rrc: 'yes' -------------------------------------------------------------------------------- /l2vni/host_vars/node_vars/Spine-02.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Spine-02' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv4_multi: 'yes' 6 | 7 | interfaces: 8 | 9 | Loopback0: 10 | name: 'Routing Loopback' 11 | ip_address: '172.16.255.2' 12 | subnet_mask: '255.255.255.255' 13 | loopback: 'yes' 14 | pim_enable: 'no' 15 | 16 | Loopback1: 17 | name: 'MSDP Loopback' 18 | ip_address: '172.16.254.2' 19 | subnet_mask: '255.255.255.255' 20 | loopback: 'yes' 21 | pim_enable: 'yes' 22 | 23 | Loopback2: 24 | name: 'AnycastRP Loopback' 25 | ip_address: '172.16.255.255' 26 | subnet_mask: '255.255.255.255' 27 | loopback: 'yes' 28 | pim_enable: 'yes' 29 | 30 | GigabitEthernet1/0/1: 31 | name: 'Backbone interface to Leaf-01' 32 | ip_address: '172.16.23.2' 33 | subnet_mask: '255.255.255.0' 34 | loopback: 'no' 35 | pim_enable: 'yes' 36 | 37 | GigabitEthernet1/0/2: 38 | name: 'Backbone interface to Leaf-02' 39 | ip_address: '172.16.24.2' 40 | subnet_mask: '255.255.255.0' 41 | loopback: 'no' 42 | pim_enable: 'yes' 43 | 44 | ospf: 45 | router_id: '172.16.255.2' 46 | 47 | pim: 48 | rp_address: '172.16.255.255' 49 | 50 | msdp: 51 | '1': 52 | peer_ip: '172.16.254.1' 53 | source_interface: 'Loopback1' 54 | remote_as: '65001' 55 | 56 | bgp: 57 | as_number: '65001' 58 | router_id: 'Loopback0' 59 | neighbors: 60 | '172.16.255.1': 61 | peer_as_number: '65001' 62 | source_interface: 'Loopback0' 63 | rrc: 'yes' 64 | 65 | '172.16.255.3': 66 | peer_as_number: '65001' 67 | source_interface: 'Loopback0' 68 | rrc: 'yes' 69 | 70 | '172.16.255.4': 71 | peer_as_number: '65001' 72 | source_interface: 'Loopback0' 73 | rrc: 'yes' -------------------------------------------------------------------------------- /l3vni/host_vars/node_vars/Spine-02.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Spine-02' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv4_multi: 'yes' 6 | 7 | interfaces: 8 | 9 | Loopback0: 10 | name: 'Routing Loopback' 11 | ip_address: '172.16.255.2' 12 | subnet_mask: '255.255.255.255' 13 | loopback: 'yes' 14 | pim_enable: 'no' 15 | 16 | Loopback1: 17 | name: 'MSDP Loopback' 18 | ip_address: '172.16.254.2' 19 | subnet_mask: '255.255.255.255' 20 | loopback: 'yes' 21 | pim_enable: 'yes' 22 | 23 | Loopback2: 24 | name: 'AnycastRP Loopback' 25 | ip_address: '172.16.255.255' 26 | subnet_mask: '255.255.255.255' 27 | loopback: 'yes' 28 | pim_enable: 'yes' 29 | 30 | GigabitEthernet1/0/1: 31 | name: 'Backbone interface to Leaf-01' 32 | ip_address: '172.16.23.2' 33 | subnet_mask: '255.255.255.0' 34 | loopback: 'no' 35 | pim_enable: 'yes' 36 | 37 | GigabitEthernet1/0/2: 38 | name: 'Backbone interface to Leaf-02' 39 | ip_address: '172.16.24.2' 40 | subnet_mask: '255.255.255.0' 41 | loopback: 'no' 42 | pim_enable: 'yes' 43 | 44 | ospf: 45 | router_id: '172.16.255.2' 46 | 47 | pim: 48 | rp_address: '172.16.255.255' 49 | 50 | msdp: 51 | '1': 52 | peer_ip: '172.16.254.1' 53 | source_interface: 'Loopback1' 54 | remote_as: '65001' 55 | 56 | bgp: 57 | as_number: '65001' 58 | router_id: 'Loopback0' 59 | neighbors: 60 | '172.16.255.1': 61 | peer_as_number: '65001' 62 | source_interface: 'Loopback0' 63 | rrc: 'yes' 64 | 65 | '172.16.255.3': 66 | peer_as_number: '65001' 67 | source_interface: 'Loopback0' 68 | rrc: 'yes' 69 | 70 | '172.16.255.4': 71 | peer_as_number: '65001' 72 | source_interface: 'Loopback0' 73 | rrc: 'yes' -------------------------------------------------------------------------------- /dag/host_vars/node_vars/Leaf-02.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Leaf-02' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.4' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'yes' 16 | 17 | Loopback1: 18 | name: 'NVE Loopback' 19 | ip_address: '172.16.254.4' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | GigabitEthernet1/0/1: 25 | name: 'Backbone interface to Spine-01' 26 | ip_address: '172.16.14.4' 27 | subnet_mask: '255.255.255.0' 28 | loopback: 'no' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/2: 32 | name: 'Backbone interface to Spine-02' 33 | ip_address: '172.16.24.4' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | # ospf: 39 | # router_id: '172.16.255.4' 40 | 41 | isis: 42 | process_id: 'UNDERLAY' 43 | net: 49.0001.1720.1625.5004.00 44 | passive_interfaces: 45 | - Loopback0 46 | - Loopback1 47 | 48 | pim: 49 | rp_address: '172.16.255.255' 50 | 51 | bgp: 52 | as_number: '65001' 53 | router_id: 'Loopback0' 54 | neighbors: 55 | '172.16.255.1': 56 | peer_as_number: '65001' 57 | source_interface: 'Loopback0' 58 | 59 | '172.16.255.2': 60 | peer_as_number: '65001' 61 | source_interface: 'Loopback0' 62 | 63 | #layer 3 interface 64 | overlay_interfaces: 65 | Loopback11: 66 | name: 'Vrf Loopback ' 67 | ip_address: '10.2.11.11' 68 | subnet_mask: '255.255.255.0' 69 | loopback: 'yes' 70 | vrf: 'green' 71 | 72 | Loopback12: 73 | name: 'Vrf Loopback ' 74 | ip_address: '10.2.12.12' 75 | subnet_mask: '255.255.255.0' 76 | loopback: 'yes' 77 | vrf: 'blue' 78 | 79 | -------------------------------------------------------------------------------- /dag/host_vars/node_vars/Leaf-01.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Leaf-01' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.3' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'no' 16 | 17 | Loopback1: 18 | name: 'NVE Loopback' 19 | ip_address: '172.16.254.3' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | GigabitEthernet1/0/1: 25 | name: 'Backbone interface to Spine-01' 26 | ip_address: '172.16.13.3' 27 | subnet_mask: '255.255.255.0' 28 | loopback: 'no' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/2: 32 | name: 'Backbone interface to Spine-02' 33 | ip_address: '172.16.23.3' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | # ospf: 39 | # router_id: '172.16.255.3' 40 | 41 | isis: 42 | process_id: 'UNDERLAY' 43 | net: 49.0001.1720.1625.5003.00 44 | passive_interfaces: 45 | - Loopback0 46 | - Loopback1 47 | 48 | pim: 49 | rp_address: '172.16.255.255' 50 | 51 | bgp: 52 | as_number: '65001' 53 | router_id: 'Loopback0' 54 | #source_interface: 'Loopback1' 55 | neighbors: 56 | '172.16.255.1': 57 | #peer_as_number: '65001' 58 | #source_interface: 'Loopback0' 59 | 60 | '172.16.255.2': 61 | #peer_as_number: '65004' 62 | #source_interface: 'Loopback0' 63 | 64 | #layer 3 interface 65 | overlay_interfaces: 66 | Loopback11: 67 | name: 'Vrf Loopback ' 68 | ip_address: '10.1.11.11' 69 | subnet_mask: '255.255.255.0' 70 | loopback: 'yes' 71 | vrf: 'green' 72 | 73 | Loopback12: 74 | name: 'Vrf Loopback ' 75 | ip_address: '10.1.12.12' 76 | subnet_mask: '255.255.255.0' 77 | loopback: 'yes' 78 | vrf: 'blue' 79 | 80 | -------------------------------------------------------------------------------- /dag/host_vars/node_vars/Spine-01.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Spine-01' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.1' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'no' 16 | 17 | Loopback1: 18 | name: 'MSDP Loopback' 19 | ip_address: '172.16.254.1' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | Loopback2: 25 | name: 'AnycastRP Loopback' 26 | ip_address: '172.16.255.255' 27 | subnet_mask: '255.255.255.255' 28 | loopback: 'yes' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/1: 32 | name: 'Backbone interface to Leaf-01' 33 | ip_address: '172.16.13.1' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | GigabitEthernet1/0/2: 39 | name: 'Backbone interface to Leaf-02' 40 | ip_address: '172.16.14.1' 41 | subnet_mask: '255.255.255.0' 42 | loopback: 'no' 43 | pim_enable: 'yes' 44 | 45 | # ospf: 46 | # router_id: '172.16.255.1' 47 | 48 | isis: 49 | process_id: 'UNDERLAY' 50 | net: 49.0001.1720.1625.5001.00 51 | passive_interfaces: 52 | - Loopback0 53 | - Loopback1 54 | 55 | pim: 56 | rp_address: '172.16.255.255' 57 | 58 | msdp: 59 | '1': 60 | peer_ip: '172.16.254.2' 61 | source_interface: 'Loopback1' 62 | remote_as: '65001' 63 | 64 | bgp: 65 | as_number: '65001' 66 | router_id: 'Loopback0' 67 | neighbors: 68 | '172.16.255.2': 69 | peer_as_number: '65001' 70 | source_interface: 'Loopback0' 71 | rrc: 'yes' 72 | 73 | '172.16.255.3': 74 | peer_as_number: '65001' 75 | source_interface: 'Loopback0' 76 | rrc: 'yes' 77 | 78 | '172.16.255.4': 79 | peer_as_number: '65001' 80 | source_interface: 'Loopback0' 81 | rrc: 'yes' 82 | -------------------------------------------------------------------------------- /dag/host_vars/node_vars/Spine-02.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Spine-02' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.2' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'no' 16 | 17 | Loopback1: 18 | name: 'MSDP Loopback' 19 | ip_address: '172.16.254.2' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | Loopback2: 25 | name: 'AnycastRP Loopback' 26 | ip_address: '172.16.255.255' 27 | subnet_mask: '255.255.255.255' 28 | loopback: 'yes' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/1: 32 | name: 'Backbone interface to Leaf-01' 33 | ip_address: '172.16.23.2' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | GigabitEthernet1/0/2: 39 | name: 'Backbone interface to Leaf-02' 40 | ip_address: '172.16.24.2' 41 | subnet_mask: '255.255.255.0' 42 | loopback: 'no' 43 | pim_enable: 'yes' 44 | 45 | # ospf: 46 | # router_id: '172.16.255.2' 47 | 48 | isis: 49 | process_id: 'UNDERLAY' 50 | net: 49.0001.1720.1625.5002.00 51 | passive_interfaces: 52 | - Loopback0 53 | - Loopback1 54 | 55 | pim: 56 | rp_address: '172.16.255.255' 57 | 58 | msdp: 59 | '1': 60 | peer_ip: '172.16.254.1' 61 | source_interface: 'Loopback1' 62 | remote_as: '65001' 63 | 64 | bgp: 65 | as_number: '65001' 66 | router_id: 'Loopback0' 67 | neighbors: 68 | '172.16.255.1': 69 | peer_as_number: '65001' 70 | source_interface: 'Loopback0' 71 | rrc: 'yes' 72 | 73 | '172.16.255.3': 74 | peer_as_number: '65001' 75 | source_interface: 'Loopback0' 76 | rrc: 'yes' 77 | 78 | '172.16.255.4': 79 | peer_as_number: '65001' 80 | source_interface: 'Loopback0' 81 | rrc: 'yes' 82 | -------------------------------------------------------------------------------- /l3vni/host_vars/node_vars/Leaf-01.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Leaf-01' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.3' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'no' 16 | 17 | Loopback1: 18 | name: 'NVE Loopback' 19 | ip_address: '172.16.254.3' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | GigabitEthernet1/0/1: 25 | name: 'Backbone interface to Spine-01' 26 | ip_address: '172.16.13.3' 27 | subnet_mask: '255.255.255.0' 28 | loopback: 'no' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/2: 32 | name: 'Backbone interface to Spine-02' 33 | ip_address: '172.16.23.3' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | ospf: 39 | router_id: '172.16.255.3' 40 | 41 | pim: 42 | rp_address: '172.16.255.255' 43 | 44 | bgp: 45 | as_number: '65001' 46 | router_id: 'Loopback0' 47 | neighbors: 48 | '172.16.255.1': 49 | peer_as_number: '65001' 50 | source_interface: 'Loopback0' 51 | 52 | '172.16.255.2': 53 | peer_as_number: '65001' 54 | source_interface: 'Loopback0' 55 | 56 | #layer 3 interface 57 | overlay_interfaces: 58 | Loopback11: 59 | name: 'Vrf Loopback ' 60 | ip_address: '10.1.11.11' 61 | subnet_mask: '255.255.255.0' 62 | loopback: 'yes' 63 | vrf: 'green' 64 | 65 | Loopback12: 66 | name: 'Vrf Loopback ' 67 | ip_address: '10.1.12.12' 68 | subnet_mask: '255.255.255.0' 69 | loopback: 'yes' 70 | vrf: 'blue' 71 | 72 | #layer 2 interfaces 73 | access_interfaces: 74 | trunks: 75 | GigabitEthernet1/0/7: 76 | action: init 77 | vlans: 78 | - 101-102 79 | #- 102 80 | - 201 81 | - 202 82 | access: 83 | GigabitEthernet1/0/8: 84 | action: init 85 | vlans: 86 | - 101 87 | 88 | -------------------------------------------------------------------------------- /l3vni/host_vars/node_vars/Leaf-02.yml: -------------------------------------------------------------------------------- 1 | hostname: 'Leaf-02' 2 | 3 | routing: 4 | ipv4_uni: 'yes' 5 | ipv6_uni: 'yes' 6 | ipv4_multi: 'yes' 7 | 8 | interfaces: 9 | 10 | Loopback0: 11 | name: 'Routing Loopback' 12 | ip_address: '172.16.255.4' 13 | subnet_mask: '255.255.255.255' 14 | loopback: 'yes' 15 | pim_enable: 'yes' 16 | 17 | Loopback1: 18 | name: 'NVE Loopback' 19 | ip_address: '172.16.254.4' 20 | subnet_mask: '255.255.255.255' 21 | loopback: 'yes' 22 | pim_enable: 'yes' 23 | 24 | GigabitEthernet1/0/1: 25 | name: 'Backbone interface to Spine-01' 26 | ip_address: '172.16.14.4' 27 | subnet_mask: '255.255.255.0' 28 | loopback: 'no' 29 | pim_enable: 'yes' 30 | 31 | GigabitEthernet1/0/2: 32 | name: 'Backbone interface to Spine-02' 33 | ip_address: '172.16.24.4' 34 | subnet_mask: '255.255.255.0' 35 | loopback: 'no' 36 | pim_enable: 'yes' 37 | 38 | ospf: 39 | router_id: '172.16.255.4' 40 | 41 | pim: 42 | rp_address: '172.16.255.255' 43 | 44 | bgp: 45 | as_number: '65001' 46 | router_id: 'Loopback0' 47 | neighbors: 48 | '172.16.255.1': 49 | peer_as_number: '65001' 50 | source_interface: 'Loopback0' 51 | 52 | '172.16.255.2': 53 | peer_as_number: '65001' 54 | source_interface: 'Loopback0' 55 | 56 | #layer 3 interface 57 | overlay_interfaces: 58 | Loopback11: 59 | name: 'Vrf Loopback ' 60 | ip_address: '10.2.11.11' 61 | subnet_mask: '255.255.255.0' 62 | loopback: 'yes' 63 | vrf: 'green' 64 | 65 | Loopback12: 66 | name: 'Vrf Loopback ' 67 | ip_address: '10.2.12.12' 68 | subnet_mask: '255.255.255.0' 69 | loopback: 'yes' 70 | vrf: 'blue' 71 | 72 | #layer 2 interfaces 73 | access_interfaces: 74 | trunks: 75 | GigabitEthernet1/0/7: 76 | action: init 77 | vlans: 78 | - 101-102 79 | # - 102 80 | - 201 81 | - 202 82 | access: 83 | GigabitEthernet1/0/8: 84 | action: init 85 | vlans: 86 | - 101 87 | 88 | -------------------------------------------------------------------------------- /templates/bgp_l2vpn_evpn_af.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring EVPN AF and activates neighbors under AF 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {# Entering bgp process configuration #} 8 | ! 9 | router bgp {{ bgp.as_number }} 10 | 11 | {# activating EVPN AF #} 12 | ! 13 | address-family l2vpn evpn 14 | 15 | {# in case of eBGP peering, default auto-RT based on AS should be rewrittem #} 16 | {% if bgp.rewrite_evpn_rt_asn is defined and bgp.rewrite_evpn_rt_asn == 'yes'%} 17 | rewrite-evpn-rt-asn 18 | {% endif %} 19 | 20 | {# configuring neighbors under EVPN AF #} 21 | {% for neighbor in bgp.neighbors -%} 22 | neighbor {{neighbor }} activate 23 | 24 | {# configuration of RR-client if needed #} 25 | {% if bgp.neighbors[neighbor].rrc is defined and bgp.neighbors[neighbor].rrc == 'yes' %} 26 | neighbor {{ neighbor }} inherit peer-policy SPINE-EVPN-PEER-POLICY 27 | {% else %} 28 | neighbor {{ neighbor }} inherit peer-policy LEAF-EVPN-PEER-POLICY 29 | {% endif %} 30 | 31 | {# configuration of allowas-in if needed #} 32 | {% if bgp.neighbors[neighbor].allows_as_in is defined and bgp.neighbors[neighbor].allows_as_in == 'yes' %} 33 | neighbor {{ neighbor }} allowas-in 34 | {% endif %} 35 | 36 | {# configuration of route-map in incoming direction if needed #} 37 | {% if bgp.neighbors[neighbor].route_map.in is defined %} 38 | neighbor {{ neighbor }} route-map {{ bgp.neighbors[neighbor].route_map.in }} in 39 | {% endif %} 40 | 41 | {# configuration of route-map in outgoing direction if needed #} 42 | {% if bgp.neighbors[neighbor].route_map.out is defined %} 43 | neighbor {{ neighbor }} route-map {{ bgp.neighbors[neighbor].route_map.out }} out 44 | {% endif %} 45 | {% endfor %} 46 | -------------------------------------------------------------------------------- /subtasks/subtask_acc_intf_preview.yml: -------------------------------------------------------------------------------- 1 | - name: Set inteface detail 2 | when: " intf_item | type_debug != 'dict' " 3 | set_fact: 4 | intf_name: "{{ intf_item }}" 5 | vlan_id: "{{ vlan_id_common }}" 6 | 7 | - name: Collect custom VLAN/s per interface 8 | when: " intf_item | type_debug == 'dict' " 9 | block: 10 | - set_fact: 11 | intf_name: "{{ item }}" 12 | with_items: "{{ intf_item.keys() }}" 13 | 14 | - name: Get custom VLAN/s 15 | set_fact: 16 | vlan_id: "{{ intf_item[intf_name][info_dict[intf_mode]['key_name']] }}" 17 | 18 | - name: Trunk interface configuration 19 | when: intf_mode == 'trunks' 20 | block: 21 | - name: Call ios_l2_interfaces module 22 | cisco.ios.ios_l2_interfaces: 23 | config: 24 | - name: "{{ intf_name }}" 25 | mode: trunk 26 | trunk: 27 | allowed_vlans: "{{ vlan_id }}" 28 | state: rendered 29 | register: trunk_unit 30 | 31 | - set_fact: 32 | intf_conf_unit: "{{ trunk_unit }}" 33 | 34 | - name: Access interface configuration 35 | when: intf_mode == 'access' 36 | block: 37 | - name: Call ios_l2_interfaces module 38 | cisco.ios.ios_l2_interfaces: 39 | config: 40 | - name: "{{ intf_name }}" 41 | mode: access 42 | access: 43 | vlan: "{{ vlan_id }}" 44 | state: rendered 45 | register: access_unit 46 | 47 | - set_fact: 48 | intf_conf_unit: "{{ access_unit }}" 49 | 50 | - name: Rendering output for playbook mode incrememtal 51 | when: playbook_mode == 'inc' 52 | set_fact: 53 | intf_conf_unit: "{{ intf_conf_unit | regex_replace('trunk allowed vlan', 'trunk allowed vlan add') }}" 54 | 55 | - name: Rendering output - no shutdown 56 | set_fact: 57 | intf_conf_unit: "{{ [ '!'] + intf_conf_unit.rendered + [ '!\ninterface ' +intf_name + '\nno shutdown'] }}" 58 | 59 | - name: Appending output 60 | set_fact: 61 | intf_conf: "{{ intf_conf_unit + intf_conf }}" 62 | -------------------------------------------------------------------------------- /subtasks/subtask_acc_intf_trunk.yml: -------------------------------------------------------------------------------- 1 | - name: Set interface type 2 | set_fact: 3 | intf_mode: "trunks" 4 | 5 | - name: Get VLAN/s list from file in host_vars/access_intf 6 | when: '"trunk_vlan_list" in get_Leaf_vars["access_interfaces"]' 7 | set_fact: 8 | vlan_id_common: "{{ get_Leaf_vars.access_interfaces.trunk_vlan_list }}" 9 | 10 | - name: Get VLAN/s list from group_vars/overlay_db.yml or host_vars/inc_vars/.yml file 11 | when: '"trunk_vlan_list" not in get_Leaf_vars["access_interfaces"]' 12 | block: 13 | - name: Assign VLANs from host_vars/inc_vars/.yml 14 | when: playbook_mode == 'inc' 15 | block: 16 | - name: Load variables from access interface input file if present 17 | block: 18 | - name: Check whether host_vars/inc_vars/{{ inventory_hostname }}.yml" is present 19 | local_action: stat path="{{ input_vars_path }}host_vars/inc_vars/{{ inventory_hostname }}.yml" 20 | register: file_state 21 | become: no 22 | 23 | - name: Load variables from host_vars/inc_vars/{{ inventory_hostname }}.yml if present 24 | include_vars: 25 | file: "{{ input_vars_path }}host_vars/inc_vars/{{ inventory_hostname }}.yml" 26 | name: inc_vars_data 27 | when: file_state.stat.exists 28 | 29 | - set_fact: 30 | vlan_id_common: "{{ inc_vars_data['access_inft_cli'] }}" 31 | when: file_state.stat.exists 32 | 33 | - name: Assign VLANs from group_vars/overlay_db.yml 34 | when: playbook_mode == 'add' 35 | block: 36 | - name: Filter 'access' type VLAN/s 37 | when: counter is not defined 38 | include_tasks: ../subtasks/subtask_get_access_vlan.yml 39 | 40 | - set_fact: 41 | vlan_id_common: "{{ vlan_id_all }}" 42 | 43 | - name: Loop through interfaces 44 | when: vlan_id_common is defined 45 | include_tasks: "{{ execute_task }}" 46 | loop: "{{ get_Leaf_vars['access_interfaces']['trunks'] }}" 47 | loop_control: 48 | loop_var: intf_item 49 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_incremental_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/inc_vars dir 8 | find: 9 | path: "host_vars/inc_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from create_vars.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/create_vars.yml 24 | name: user_input 25 | 26 | - name: Collect vars from overlay_db.yml 27 | run_once: true 28 | include_vars: 29 | file: group_vars/overlay_db.yml 30 | name: to_compare 31 | 32 | - name: Collect vars from hostvars leafs 33 | run_once: true 34 | include_vars: 35 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 36 | name: hostvars_leaf_data 37 | 38 | - name: Collect "show run nve" 39 | cli_command: 40 | command: show run nve 41 | register: command_output 42 | 43 | - name: Process data 44 | create_incremental_l3vni_yml: 45 | userinput: "{{ user_input }}" 46 | overlay_intf: "{{ hostvars_leaf_data }}" 47 | hostvars: "{{ command_output.stdout_lines }}" 48 | tocompare: "{{ to_compare }}" 49 | register: module_output 50 | 51 | - name: Copying configurations to the repective files ( .yml ) under host_vars/inc_vars folder 52 | when: module_output['yaml'] != '{}\n' 53 | copy: 54 | dest: host_vars/inc_vars/{{ inventory_hostname }}.yml 55 | content: "{{ module_output['yaml'] }}" 56 | -------------------------------------------------------------------------------- /dag/playbook_overlay_delete_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | update_acc_intf: false 8 | 9 | tasks: 10 | - name: Load variables from group_vars/delete_vars.yml file 11 | run_once: true 12 | include_vars: 13 | file: group_vars/delete_vars.yml 14 | name: to_del 15 | 16 | - name: Collect "show run nve" 17 | cli_command: 18 | command: show run nve 19 | register: nve_output 20 | 21 | - name: Add "show run nve" output to dictionary 22 | set_fact: 23 | command_output: "{{ { 'run_nve' : nve_output.stdout_lines} }}" 24 | 25 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 26 | when: to_del['update_access'] is defined 27 | set_fact: 28 | update_acc_intf: "{{ to_del['update_access'] }}" 29 | 30 | - name: Collect and add interface information to dictionary 31 | when: update_acc_intf == true 32 | block: 33 | - name: Collect "show run | section ^interface" 34 | cli_command: 35 | command: show run | section ^interface 36 | register: interface_output 37 | 38 | - name: Add "show run | section ^interface" output to dictionary 39 | set_fact: 40 | command_output: "{{ command_output | combine ( {'intf_sec' : interface_output.stdout_lines} ) }}" 41 | 42 | - name: Process collected CLIs' output 43 | parser_to_delete_yaml: 44 | hostvars: "{{ command_output }}" 45 | toDel: "{{ to_del }}" 46 | register: module_output 47 | 48 | - name: Copying configurations to the repective files ( .yml ) under host_vars/delete_vars folder 49 | when: module_output['yaml'] != '{}\n' 50 | copy: 51 | dest: host_vars/delete_vars/{{ inventory_hostname }}.yml 52 | content: "{{ module_output['yaml'] }}" 53 | -------------------------------------------------------------------------------- /l2vni/playbook_overlay_delete_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | update_acc_intf: false 8 | 9 | tasks: 10 | - name: Load variables from group_vars/delete_vars.yml file 11 | run_once: true 12 | include_vars: 13 | file: group_vars/delete_vars.yml 14 | name: to_del 15 | 16 | - name: Collect "show run nve" 17 | cli_command: 18 | command: show run nve 19 | register: nve_output 20 | 21 | - name: Add "show run nve" output to dictionary 22 | set_fact: 23 | command_output: "{{ { 'run_nve' : nve_output.stdout_lines} }}" 24 | 25 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 26 | when: to_del['update_access'] is defined 27 | set_fact: 28 | update_acc_intf: "{{ to_del['update_access'] }}" 29 | 30 | - name: Collect and add interface information to dictionary 31 | when: update_acc_intf == true 32 | block: 33 | - name: Collect "show run | section ^interface" 34 | cli_command: 35 | command: show run | section ^interface 36 | register: interface_output 37 | 38 | - name: Add "show run | section ^interface" output to dictionary 39 | set_fact: 40 | command_output: "{{ command_output | combine ( {'intf_sec' : interface_output.stdout_lines} ) }}" 41 | 42 | - name: Process collected CLIs' output 43 | parser_to_delete_yaml: 44 | hostvars: "{{ command_output }}" 45 | toDel: "{{ to_del }}" 46 | register: module_output 47 | 48 | - name: Copying configurations to the repective files ( .yml ) under host_vars/delete_vars folder 49 | when: module_output['yaml'] != '{}\n' 50 | copy: 51 | dest: host_vars/delete_vars/{{ inventory_hostname }}.yml 52 | content: "{{ module_output['yaml'] }}" 53 | -------------------------------------------------------------------------------- /dag/playbook_overlay_delete_ipv6_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/inc_vars dir 8 | find: 9 | path: "host_vars/ipv6_delete_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from overlay_db.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/overlay_db.yml 24 | name: to_compare 25 | 26 | - name: Collect vars from ipv6_delete_vars.yml 27 | run_once: true 28 | include_vars: 29 | file: group_vars/ipv6_delete_vars.yml 30 | name: user_input 31 | 32 | - name: Collect "show run nve" 33 | cli_command: 34 | command: show run nve 35 | register: command_output 36 | 37 | - name: Collect vars from hostvars leafs 38 | run_once: true 39 | include_vars: 40 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 41 | name: hostvars_leaf_data 42 | 43 | - name: Process data 44 | delete_ipv6: 45 | hostvars: "{{ command_output.stdout_lines }}" 46 | leaf_data: "{{ hostvars_leaf_data }}" 47 | overlay_db: "{{ to_compare }}" 48 | userinput: "{{ user_input }}" 49 | register: module_output 50 | 51 | - name: Copying configurations to the repective files ( .yml ) under host_vars/ipv6_delete_vars folder 52 | when: module_output['yaml'] != '{}\n' 53 | copy: 54 | dest: host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 55 | content: "{{ module_output['yaml'] }}" 56 | -------------------------------------------------------------------------------- /dag/playbook_overlay_incremental_ipv6_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/inc_vars dir 8 | find: 9 | path: "host_vars/ipv6_inc_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from overlay_db.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/overlay_db.yml 24 | name: to_compare 25 | 26 | - name: Collect vars from incremental_vars.yml 27 | run_once: true 28 | include_vars: 29 | file: group_vars/ipv6_create_vars.yml 30 | name: user_input 31 | 32 | - name: Collect "show run nve" 33 | cli_command: 34 | command: show run nve 35 | register: command_output 36 | 37 | - name: Collect vars from hostvars leafs 38 | run_once: true 39 | include_vars: 40 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 41 | name: hostvars_leaf_data 42 | 43 | - name: Process data 44 | create_incremental_ipv6: 45 | hostvars: "{{ command_output.stdout_lines }}" 46 | leaf_data: "{{ hostvars_leaf_data }}" 47 | overlay_db: "{{ to_compare }}" 48 | userinput: "{{ user_input }}" 49 | register: module_output 50 | 51 | - name: Copying configurations to the repective files ( .yml ) under host_vars/ipv6_inc_vars folder 52 | when: module_output['yaml'] != '{}\n' 53 | copy: 54 | dest: host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 55 | content: "{{ module_output['yaml'] }}" 56 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_delete_ipv6_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/ipv6_delete_vars dir 8 | find: 9 | path: "host_vars/ipv6_delete_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from overlay_db.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/overlay_db.yml 24 | name: to_compare 25 | 26 | - name: Collect vars from ipv6_delete_vars.yml 27 | run_once: true 28 | include_vars: 29 | file: group_vars/ipv6_delete_vars.yml 30 | name: user_input 31 | 32 | - name: Collect "show run nve" 33 | cli_command: 34 | command: show run nve 35 | register: command_output 36 | 37 | - name: Collect vars from hostvars leafs 38 | run_once: true 39 | include_vars: 40 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 41 | name: hostvars_leaf_data 42 | 43 | - name: Process data 44 | delete_ipv6_l3vni: 45 | hostvars: "{{ command_output.stdout_lines }}" 46 | leaf_data: "{{ hostvars_leaf_data }}" 47 | overlay_db: "{{ to_compare }}" 48 | userinput: "{{ user_input }}" 49 | register: module_output 50 | 51 | - name: Copying configurations to the repective files ( .yml ) under host_vars/ipv6_delete_vars folder 52 | when: module_output['yaml'] != '{}\n' 53 | copy: 54 | dest: host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 55 | content: "{{ module_output['yaml'] }}" 56 | -------------------------------------------------------------------------------- /dag/playbook_access_add_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | playbook_mode: add 8 | info_dict: 9 | access: 10 | key_name: 'access_vlan' 11 | trunks: 12 | key_name: 'trunk_vlan_list' 13 | 14 | tasks: 15 | - name: Check playbook mode 16 | run_once: true 17 | when: incremental is defined 18 | set_fact: 19 | playbook_mode: inc 20 | 21 | - name: set access interface input folder path if not already set 22 | when: input_vars_path is not defined 23 | set_fact: 24 | input_vars_path: "./" 25 | 26 | - name: Load variables from access interface input file if present 27 | block: 28 | - name: Check whether host_vars/access_intf/.yml is present 29 | local_action: stat path="{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 30 | register: file_state 31 | become: no 32 | 33 | - name: Load variables from host_vars/access_intf/.yml if present 34 | include_vars: 35 | file: "{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 36 | name: get_Leaf_vars 37 | when: file_state.stat.exists 38 | 39 | # Following tasks are skippped if host_vars/access_intf/.yml is not present 40 | 41 | - name: Process interfaces 42 | when: (get_Leaf_vars is defined) and (get_Leaf_vars.access_interfaces is defined) 43 | block: 44 | - set_fact: 45 | execute_task: "../subtasks/subtask_acc_intf_commit.yml" 46 | 47 | - name: Process trunk interfaces 48 | when: get_Leaf_vars.access_interfaces.trunks is defined 49 | include_tasks: ../subtasks/subtask_acc_intf_trunk.yml 50 | 51 | - name: Process access interfaces 52 | when: get_Leaf_vars.access_interfaces.access is defined 53 | include_tasks: ../subtasks/subtask_acc_intf_access.yml 54 | -------------------------------------------------------------------------------- /l2vni/playbook_access_add_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | playbook_mode: add 8 | info_dict: 9 | access: 10 | key_name: 'access_vlan' 11 | trunks: 12 | key_name: 'trunk_vlan_list' 13 | 14 | tasks: 15 | - name: Check playbook mode 16 | run_once: true 17 | when: incremental is defined 18 | set_fact: 19 | playbook_mode: inc 20 | 21 | - name: set access interface input folder path if not already set 22 | when: input_vars_path is not defined 23 | set_fact: 24 | input_vars_path: "./" 25 | 26 | - name: Load variables from access interface input file if present 27 | block: 28 | - name: Check whether host_vars/access_intf/.yml is present 29 | local_action: stat path="{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 30 | register: file_state 31 | become: no 32 | 33 | - name: Load variables from host_vars/access_intf/.yml if present 34 | include_vars: 35 | file: "{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 36 | name: get_Leaf_vars 37 | when: file_state.stat.exists 38 | 39 | # Following tasks are skippped if host_vars/access_intf/.yml is not present 40 | 41 | - name: Process interfaces 42 | when: (get_Leaf_vars is defined) and (get_Leaf_vars.access_interfaces is defined) 43 | block: 44 | - set_fact: 45 | execute_task: "../subtasks/subtask_acc_intf_commit.yml" 46 | 47 | - name: Process trunk interfaces 48 | when: get_Leaf_vars.access_interfaces.trunks is defined 49 | include_tasks: ../subtasks/subtask_acc_intf_trunk.yml 50 | 51 | - name: Process access interfaces 52 | when: get_Leaf_vars.access_interfaces.access is defined 53 | include_tasks: ../subtasks/subtask_acc_intf_access.yml 54 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_incremental_ipv6_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/ipv6_inc_vars dir 8 | find: 9 | path: "host_vars/ipv6_inc_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from overlay_db.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/overlay_db.yml 24 | name: to_compare 25 | 26 | - name: Collect vars from ipv6_create_vars.yml 27 | run_once: true 28 | include_vars: 29 | file: group_vars/ipv6_create_vars.yml 30 | name: user_input 31 | 32 | - name: Collect "show run nve" 33 | cli_command: 34 | command: show run nve 35 | register: command_output 36 | 37 | - name: Collect vars from hostvars leafs 38 | run_once: true 39 | include_vars: 40 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 41 | name: hostvars_leaf_data 42 | 43 | - name: Process data 44 | create_incremental_ipv6_l3vni: 45 | hostvars: "{{ command_output.stdout_lines }}" 46 | leaf_data: "{{ hostvars_leaf_data }}" 47 | overlay_db: "{{ to_compare }}" 48 | userinput: "{{ user_input }}" 49 | register: module_output 50 | 51 | - name: Copying configurations to the repective files ( .yml ) under host_vars/ipv6_inc_vars folder 52 | when: module_output['yaml'] != '{}\n' 53 | copy: 54 | dest: host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 55 | content: "{{ module_output['yaml'] }}" 56 | -------------------------------------------------------------------------------- /templates/nve_create.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring, deleting or modifing nvi config. 3 | If action is not set in the configurations, it is "add" by default. 4 | 5 | Mutable paramiters: 6 | - member vni // mandatory 7 | 8 | Immutable paramiters: 9 | - source interface // mandatory 10 | - host-reachability protocol // mandatory 11 | #} 12 | 13 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 14 | 15 | {% set vni_list = [] %} 16 | 17 | {% if vlan_cli is defined %} 18 | {% for vlan in vlan_cli %} 19 | {% set vni_list = vni_list.append(vlans[vlan].vni|int) %} 20 | {% endfor %} 21 | {% endif %} 22 | 23 | {% if nve_interfaces is defined %} 24 | 25 | {% for interface in nve_interfaces %} 26 | ! 27 | interface nve{{ interface }} 28 | no ip address 29 | source-interface {{ nve_interfaces[interface].source_interface }} 30 | host-reachability protocol bgp 31 | 32 | {% for ivlan,idata in vlans.items() %} 33 | {% if ((vlan_cli is defined and ivlan in vlan_cli) or (vlan_cli is not defined)) %} 34 | 35 | {# Checking an action for vlan - action_stitching #} 36 | {% set action = vlans[ivlan].action | default ('add') %} 37 | 38 | {% if action == 'delete' %} 39 | {% set prefix = 'no' %} 40 | {% else %} 41 | {% set prefix = '' %} 42 | {% endif %} 43 | 44 | {% if idata.vlan_type == 'access' %} 45 | {% if idata.evi is defined %} 46 | {% if idata.replication_type == 'static' %} 47 | {{prefix}} member vni {{ idata.vni }} mcast-group {{idata.replication_mcast}} 48 | {% elif ((idata.replication_type == 'ingress') or (idata.replication_type == 'ingress-replication')) %} 49 | {{prefix}} member vni {{ idata.vni }} ingress-replication 50 | {% endif %} 51 | {% endif %} 52 | {% elif idata.vlan_type == 'core' %} 53 | {{prefix}} member vni {{ idata.vni }} vrf {{ idata.vrf }} 54 | {% endif %} 55 | 56 | {% endif %} 57 | {% endfor %} 58 | {% endfor %} 59 | {% endif %} 60 | -------------------------------------------------------------------------------- /l2vni/playbook_overlay_delete_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Write l2vni delete info to files in host_vars/delete_vars folder 4 | import_playbook: playbook_overlay_delete_generate.yml 5 | 6 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 7 | hosts: leaf 8 | gather_facts: no 9 | vars: 10 | update_acc_intf: false 11 | block_dict: 12 | '../templates/nve_create.j2': 'nve block' 13 | '../templates/vlan_create.j2': 'vlan block' 14 | '../templates/l2vpn_evpn_evi_create.j2': 'l2vpn evpn evi block' 15 | 16 | tasks: 17 | 18 | - name: Load variables from group_vars/delete_vars.yml file 19 | run_once: true 20 | include_vars: group_vars/delete_vars.yml 21 | 22 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 23 | run_once: true 24 | when: update_access is defined 25 | set_fact: 26 | update_acc_intf: "{{ update_access }}" 27 | 28 | - name: Check whether host_vars/delete_vars/.yml is present 29 | local_action: stat path=./host_vars/delete_vars/{{ inventory_hostname }}.yml 30 | register: file_state 31 | become: no 32 | 33 | - name: Executing tasks if host_vars/delete_vars/.yml is present 34 | when: file_state.stat.exists 35 | block: 36 | - name: Load variables from files under host_vars/delete_vars folder 37 | include_vars: host_vars/delete_vars/{{inventory_hostname}}.yml 38 | 39 | - name: Applying configurations 40 | cli_config: 41 | config: "{{ lookup('template', item.key) }}" 42 | diff_match: none 43 | register: result 44 | with_dict: "{{ block_dict }}" 45 | 46 | - name: Applying configurations on access interfaces 47 | when: update_acc_intf == true 48 | cli_config: 49 | config: "{{ lookup('template', '../templates/access_interfaces.j2' ) }}" 50 | diff_match: none 51 | register: result 52 | 53 | - name: Pause for 30 seconds for convergence 54 | wait_for: 55 | delay: 30 56 | timeout: 0 57 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'Cat9k EVPN Ansible' 21 | copyright = '2022, Cat9k EVPN Team' 22 | author = 'Cat9k EVPN Team' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '1.0' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | #'sphinx_rtd_theme', 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 44 | 45 | 46 | # -- Options for HTML output ------------------------------------------------- 47 | 48 | # The theme to use for HTML and HTML Help pages. See the documentation for 49 | # a list of builtin themes. 50 | # 51 | import sphinx_rtd_theme 52 | 53 | html_theme = "sphinx_rtd_theme" 54 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 55 | 56 | # Add any paths that contain custom static files (such as style sheets) here, 57 | # relative to this directory. They are copied after the builtin static files, 58 | # so a file named "default.css" will overwrite the builtin "default.css". 59 | html_static_path = ['_static'] 60 | 61 | html_css_files = ['css/custom.css'] 62 | 63 | rst_prolog = """ 64 | .. include:: 65 | """ 66 | -------------------------------------------------------------------------------- /dag/playbook_overlay_delete_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Write DAG delete info to files in host_vars/delete_vars folder 4 | import_playbook: playbook_overlay_delete_generate.yml 5 | 6 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 7 | hosts: leaf 8 | gather_facts: no 9 | vars: 10 | update_acc_intf: false 11 | block_dict: 12 | '../templates/svi_create.j2': 'svi block' 13 | '../templates/nve_create.j2': 'nve block' 14 | '../templates/vlan_create.j2': 'vlan block' 15 | '../templates/l2vpn_evpn_evi_create.j2': 'l2vpn evpn evi block' 16 | '../templates/vrf_definition.j2': 'vrf block' 17 | 18 | tasks: 19 | 20 | - name: Load variables from group_vars/delete_vars.yml file 21 | run_once: true 22 | include_vars: group_vars/delete_vars.yml 23 | 24 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 25 | run_once: true 26 | when: update_access is defined 27 | set_fact: 28 | update_acc_intf: "{{ update_access }}" 29 | 30 | - name: Check whether host_vars/delete_vars/.yml is present 31 | local_action: stat path=./host_vars/delete_vars/{{ inventory_hostname }}.yml 32 | register: file_state 33 | become: no 34 | 35 | - name: Executing tasks if host_vars/delete_vars/.yml is present 36 | when: file_state.stat.exists 37 | block: 38 | - name: Load variables from files under host_vars/delete_vars folder 39 | include_vars: host_vars/delete_vars/{{inventory_hostname}}.yml 40 | 41 | - name: Applying configurations 42 | cli_config: 43 | config: "{{ lookup('template', item.key) }}" 44 | diff_match: none 45 | register: result 46 | with_dict: "{{ block_dict }}" 47 | 48 | - name: Applying configurations on access interfaces 49 | when: update_acc_intf == true 50 | cli_config: 51 | config: "{{ lookup('template', '../templates/access_interfaces.j2' ) }}" 52 | diff_match: none 53 | register: result 54 | 55 | - name: Pause for 30 seconds for convergence 56 | wait_for: 57 | delay: 30 58 | timeout: 0 59 | -------------------------------------------------------------------------------- /templates/bgp_global.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring global BGP process and neighbor parameters 3 | #} 4 | 5 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 6 | 7 | {# initial BGP process configuration #} 8 | ! 9 | router bgp {{ bgp.as_number }} 10 | 11 | {# default bgp parameters configuration #} 12 | bgp log-neighbor-changes 13 | bgp router-id interface {{ bgp.router_id }} 14 | no bgp default ipv4-unicast 15 | 16 | {# if multiple-as are used in fabric, default route-target filter should be disabled #} 17 | {% if bgp.route_target_filter is defined and bgp.route_target_filter == 'disable' %} 18 | no bgp default route-target filter 19 | {% endif %} 20 | 21 | {# set global BGP Src Intf. If it is not set, then Loopback0 by default #} 22 | {% set g_bgp_src_intf = bgp.source_interface | default ('Loopback0')%} 23 | 24 | {# defining BGP peer session template #} 25 | template peer-session EVPN-PEER-SESSION 26 | remote-as {{ bgp.as_number }} 27 | update-source {{ g_bgp_src_intf }} 28 | 29 | {# defining BGP peer policy template #} 30 | template peer-policy LEAF-EVPN-PEER-POLICY 31 | send-community both 32 | 33 | {# defining BGP RR peer policy template #} 34 | template peer-policy SPINE-EVPN-PEER-POLICY 35 | send-community both 36 | route-reflector-client 37 | 38 | {# BGP neighbors configuration #} 39 | {% for neighbor in bgp.neighbors%} 40 | {# define bgp peer AS. By default - the same AS #} 41 | {% set bgp_neighbor_as = bgp.neighbors[neighbor].peer_as_number|default(bgp.as_number) %} 42 | 43 | {# define bgp peer source interface. By default - global SRC INTF. If it is not set - Loopback0 #} 44 | {% set bgp_neighbor_src_intf = bgp.neighbors[neighbor].source_interface|default(g_bgp_src_intf) %} 45 | 46 | {# if it is eBGP neighborship, next list of commands is used #} 47 | {% if bgp.as_number != bgp_neighbor_as %} 48 | neighbor {{ neighbor }} remote-as {{ bgp_neighbor_as }} 49 | neighbor {{ neighbor }} update-source {{ bgp_neighbor_src_intf }} 50 | neighbor {{ neighbor }} ebgp-multihop 255 51 | 52 | {# if iBGP neighborship, then inherit peer-session template #} 53 | {% else %} 54 | neighbor {{neighbor }} inherit peer-session EVPN-PEER-SESSION 55 | {% endif %} 56 | {% endfor %} 57 | -------------------------------------------------------------------------------- /l3vni/playbook_underlay_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | # Set db file path and load underlay vars 9 | - name: Load and process underlay input file 10 | run_once: true 11 | block: 12 | - name: set underlay vars input file path if not already set 13 | when: input_vars_path is not defined 14 | set_fact: 15 | input_vars_path: "./" 16 | 17 | - name: Load vars from node_vars/.yml input file 18 | include_vars: 19 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 20 | 21 | - name: Configure hostnames 22 | cli_config: 23 | config: "{{ lookup('template', '../templates/hostname.j2') }}" 24 | diff_match: none 25 | register: result 26 | 27 | - name: Enable Unicast/Multicast routing 28 | cli_config: 29 | config: "{{ lookup('template', '../templates/global_routing.j2') }}" 30 | diff_match: none 31 | register: result 32 | 33 | - name: Configure ip addresses 34 | cli_config: 35 | config: "{{ lookup('template', '../templates/underlay_interfaces.j2') }}" 36 | diff_match: none 37 | register: result 38 | 39 | - name: Configure OSPF 40 | cli_config: 41 | config: "{{ lookup('template', '../templates/ospf_interfaces.j2') }}" 42 | diff_match: none 43 | register: result 44 | 45 | - name: Configure PIM on interfaces 46 | cli_config: 47 | config: "{{ lookup('template', '../templates/pim_interfaces.j2') }}" 48 | diff_match: none 49 | register: result 50 | 51 | - name: Configure RP 52 | cli_config: 53 | config: "{{ lookup('template', '../templates/pim_rp.j2') }}" 54 | diff_match: none 55 | register: result 56 | 57 | - name: Configure MSDP peering 58 | when: inventory_hostname in groups['spine'] 59 | cli_config: 60 | config: "{{ lookup('template', '../templates/msdp_peering.j2') }}" 61 | diff_match: none 62 | register: result 63 | -------------------------------------------------------------------------------- /l2vni/playbook_underlay_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | vars: 7 | underlay_block: "" 8 | underlay_dict: 9 | '../templates/hostname.j2': 'hostname block' 10 | '../templates/global_routing.j2': 'global routing block' 11 | '../templates/underlay_interfaces.j2': 'underlay interface block' 12 | '../templates/ospf_interfaces.j2': 'ospf interface block' 13 | '../templates/pim_interfaces.j2': 'pim interface block' 14 | '../templates/pim_rp.j2': 'pim rp block' 15 | 16 | tasks: 17 | # Set db file path and load underlay vars 18 | - name: Load and process underlay input file 19 | run_once: true 20 | block: 21 | - name: set underlay vars input file path if not already set 22 | when: input_vars_path is not defined 23 | set_fact: 24 | input_vars_path: "./" 25 | 26 | - name: Load vars from node_vars/.yml input file 27 | include_vars: 28 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 29 | 30 | - name: Read template files 31 | set_fact: 32 | underlay_block: "{{ underlay_block }} ! {{ underlay_dict[item.key] }} {{ lookup('template', item.key ) }}" 33 | with_dict: "{{ underlay_dict }}" 34 | 35 | - name: Read MSDP peering template file 36 | when: inventory_hostname in groups['spine'] 37 | set_fact: 38 | underlay_block: "{{ underlay_block }} ! MSDP peering block {{ lookup('template', '../templates/msdp_peering.j2' ) }}" 39 | 40 | - name: Render template files output 41 | set_fact: 42 | rendered: "{{ underlay_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 43 | 44 | - name: Copy configurations to the respective file ( -underlay.txt ) under 'preview_files' folder 45 | copy: 46 | dest: preview_files/{{inventory_hostname}}-underlay.txt 47 | content: "{{ rendered }}" 48 | 49 | - name: Display output file info 50 | hosts: localhost 51 | gather_facts: no 52 | run_once: True 53 | 54 | tasks: 55 | - debug: 56 | msg: Please refer to -underlay.txt in 'preview_files' folder 57 | -------------------------------------------------------------------------------- /l3vni/playbook_underlay_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | vars: 7 | underlay_block: "" 8 | underlay_dict: 9 | '../templates/hostname.j2': 'hostname block' 10 | '../templates/global_routing.j2': 'global routing block' 11 | '../templates/underlay_interfaces.j2': 'underlay interface block' 12 | '../templates/ospf_interfaces.j2': 'ospf interface block' 13 | '../templates/pim_interfaces.j2': 'pim interface block' 14 | '../templates/pim_rp.j2': 'pim rp block' 15 | 16 | tasks: 17 | # Set db file path and load underlay vars 18 | - name: Load and process underlay input file 19 | run_once: true 20 | block: 21 | - name: set underlay vars input file path if not already set 22 | when: input_vars_path is not defined 23 | set_fact: 24 | input_vars_path: "./" 25 | 26 | - name: Load vars from node_vars/.yml input file 27 | include_vars: 28 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 29 | 30 | - name: Read template files 31 | set_fact: 32 | underlay_block: "{{ underlay_block }} ! {{ underlay_dict[item.key] }} {{ lookup('template', item.key ) }}" 33 | with_dict: "{{ underlay_dict }}" 34 | 35 | - name: Read MSDP peering template file 36 | when: inventory_hostname in groups['spine'] 37 | set_fact: 38 | underlay_block: "{{ underlay_block }} ! MSDP peering block {{ lookup('template', '../templates/msdp_peering.j2' ) }}" 39 | 40 | - name: Render template files output 41 | set_fact: 42 | rendered: "{{ underlay_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 43 | 44 | - name: Copy configurations to the respective file ( -underlay.txt ) under 'preview_files' folder 45 | copy: 46 | dest: preview_files/{{inventory_hostname}}-underlay.txt 47 | content: "{{ rendered }}" 48 | 49 | - name: Display output file info 50 | hosts: localhost 51 | gather_facts: no 52 | run_once: True 53 | 54 | tasks: 55 | - debug: 56 | msg: Please refer to -underlay.txt in 'preview_files' folder 57 | -------------------------------------------------------------------------------- /l2vni/playbook_underlay_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | # Set db file path and load underlay vars 9 | - name: Load and process underlay input file 10 | run_once: true 11 | block: 12 | - name: set underlay vars input file path if not already set 13 | when: input_vars_path is not defined 14 | set_fact: 15 | input_vars_path: "./" 16 | 17 | - name: Load vars from node_vars/.yml input file 18 | include_vars: 19 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 20 | 21 | - name: Configuring hostnames 22 | cli_config: 23 | config: "{{ lookup('template', '../templates/hostname.j2') }}" 24 | diff_match: none 25 | register: result 26 | 27 | - name: Enabling Unicast/Multicast routing 28 | cli_config: 29 | config: "{{ lookup('template', '../templates/global_routing.j2') }}" 30 | diff_match: none 31 | register: result 32 | 33 | - name: Configuring ip addresses 34 | cli_config: 35 | config: "{{ lookup('template', '../templates/underlay_interfaces.j2') }}" 36 | diff_match: none 37 | register: result 38 | 39 | - name: Configuring OSPF 40 | cli_config: 41 | config: "{{ lookup('template', '../templates/ospf_interfaces.j2') }}" 42 | diff_match: none 43 | register: result 44 | 45 | - name: Configuring PIM on interfaces 46 | cli_config: 47 | config: "{{ lookup('template', '../templates/pim_interfaces.j2') }}" 48 | diff_match: none 49 | register: result 50 | 51 | - name: Configuring RP 52 | cli_config: 53 | config: "{{ lookup('template', '../templates/pim_rp.j2') }}" 54 | diff_match: none 55 | register: result 56 | 57 | - name: Configuring MSDP peering 58 | when: inventory_hostname in groups['spine'] 59 | cli_config: 60 | config: "{{ lookup('template', '../templates/msdp_peering.j2') }}" 61 | diff_match: none 62 | register: result 63 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_delete_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | update_acc_intf: false 8 | 9 | tasks: 10 | - name: Load variables from group_vars/delete_vars.yml file 11 | run_once: true 12 | include_vars: 13 | file: group_vars/delete_vars.yml 14 | name: to_del 15 | 16 | - name: Load variables from group_vars/delete_vars.yml file and appending to dag keyword 17 | when: to_del.l3vni is defined 18 | block : 19 | - set_fact: 20 | dag: "{{ to_del.l3vni }}" 21 | - set_fact: 22 | to_del: "{{ {'dag': dag } }}" 23 | 24 | - name: Collect "show run nve" 25 | cli_command: 26 | command: show run nve 27 | register: nve_output 28 | 29 | - name: Add "show run nve" output to dictionary 30 | set_fact: 31 | command_output: "{{ { 'run_nve' : nve_output.stdout_lines} }}" 32 | 33 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 34 | when: to_del['update_access'] is defined 35 | set_fact: 36 | update_acc_intf: "{{ to_del['update_access'] }}" 37 | 38 | - name: Collect and add interface information to dictionary 39 | when: update_acc_intf == true 40 | block: 41 | - name: Collect "show run | section ^interface" 42 | cli_command: 43 | command: show run | section ^interface 44 | register: interface_output 45 | 46 | - name: Add "show run | section ^interface" output to dictionary 47 | set_fact: 48 | command_output: "{{ command_output | combine ( {'intf_sec' : interface_output.stdout_lines} ) }}" 49 | 50 | - name: Process collected CLIs' output 51 | parser_to_delete_yaml: 52 | hostvars: "{{ command_output }}" 53 | toDel: "{{ to_del }}" 54 | register: module_output 55 | 56 | - name: Copying configurations to the repective files ( .yml ) under host_vars/delete_vars folder 57 | when: module_output['yaml'] != '{}\n' 58 | copy: 59 | dest: host_vars/delete_vars/{{ inventory_hostname }}.yml 60 | content: "{{ module_output['yaml'] }}" 61 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_delete_ipv6_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write l3vni ipv6 delete info to files in host_vars/ipv6_delete_vars folder 3 | import_playbook: playbook_overlay_delete_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | vars: 9 | svi_ipv6_block : " " 10 | svi_ipv6_dict: 11 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 12 | tasks: 13 | - name: Check whether host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 14 | local_action: stat path=./host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 15 | register: file_state 16 | become: no 17 | 18 | - name: Executing tasks if host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 19 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 20 | block: 21 | - name: Load variables from files under host_vars/ipv6_delete_vars folder 22 | include_vars: host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 23 | 24 | - name: SVI configuration 25 | when: inventory_hostname in groups['leaf'] 26 | set_fact: 27 | svi_ipv6_block: "{{ svi_ipv6_block }} ! {{ svi_ipv6_dict[item.key] }} {{ lookup('template', item.key ) }}" 28 | with_dict: "{{ svi_ipv6_dict }}" 29 | 30 | 31 | - name: Render template files output 32 | set_fact: 33 | rendered: "{{ svi_ipv6_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 34 | 35 | - name: Copy configurations to respective file ( -ipv6_delete.txt ) under 'preview_files' folder 36 | copy: 37 | dest: preview_files/{{inventory_hostname}}-ipv6_delete.txt 38 | content: "{{ rendered }}" 39 | 40 | - name: Clearing configurations in preview_files/-ipv6_delete.txt if device config is skipped 41 | when: not file_state.stat.exists 42 | copy: 43 | dest: preview_files/{{inventory_hostname}}-ipv6_delete.txt 44 | content: "" 45 | 46 | - name: Display output file info 47 | hosts: localhost 48 | gather_facts: no 49 | run_once: True 50 | 51 | tasks: 52 | - debug: 53 | msg: Please refer to -ipv6_delete.txt in 'preview_files' folder 54 | -------------------------------------------------------------------------------- /dag/playbook_underlay_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | vars: 7 | underlay_block: "" 8 | underlay_dict: 9 | '../templates/hostname.j2': 'hostname block' 10 | '../templates/global_routing.j2': 'global routing block' 11 | '../templates/underlay_interfaces.j2': 'underlay interface block' 12 | '../templates/ospf_interfaces.j2': 'ospf interface block' 13 | '../templates/pim_interfaces.j2': 'pim interface block' 14 | '../templates/pim_rp.j2': 'pim rp block' 15 | '../templates/isis_underlay.j2': 'pim rp block' 16 | 17 | tasks: 18 | 19 | # Set db file path and load underlay vars 20 | - name: Load and process underlay input file 21 | run_once: true 22 | block: 23 | - name: set underlay vars input file path if not already set 24 | when: input_vars_path is not defined 25 | set_fact: 26 | input_vars_path: "./" 27 | 28 | - name: Load vars from node_vars/.yml input file 29 | include_vars: 30 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 31 | 32 | - name: Read template files 33 | set_fact: 34 | underlay_block: "{{ underlay_block }} ! {{ underlay_dict[item.key] }} {{ lookup('template', item.key ) }}" 35 | with_dict: "{{ underlay_dict }}" 36 | 37 | - name: Read MSDP peering template file 38 | when: inventory_hostname in groups['spine'] 39 | set_fact: 40 | underlay_block: "{{ underlay_block }} ! MSDP peering block {{ lookup('template', '../templates/msdp_peering.j2' ) }}" 41 | 42 | - name: Render template files output 43 | set_fact: 44 | rendered: "{{ underlay_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n\n! ') }}" 45 | 46 | - name: Copy configurations to the respective file ( -underlay.txt ) under 'preview_files' folder 47 | copy: 48 | dest: preview_files/{{inventory_hostname}}-underlay.txt 49 | content: "{{ rendered }}" 50 | 51 | - name: Display output file info 52 | hosts: localhost 53 | gather_facts: no 54 | run_once: True 55 | 56 | tasks: 57 | - debug: 58 | msg: Please refer to -underlay.txt in 'preview_files' folder 59 | -------------------------------------------------------------------------------- /dag/playbook_overlay_incremental_ipv6_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write DAG incremental info to files in host_vars/inc_vars folder 3 | import_playbook: playbook_overlay_incremental_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | # vars_files: 9 | # - "group_vars/overlay_db.yml" 10 | vars: 11 | svi_ipv6_block : " " 12 | svi_ipv6_dict: 13 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 14 | tasks: 15 | - name: Check whether host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 16 | local_action: stat path=./host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 17 | register: file_state 18 | become: no 19 | 20 | - name: Executing tasks if host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 21 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 22 | block: 23 | - name: Load variables from files under host_vars/ipv6_inc_vars folder 24 | include_vars: host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 25 | 26 | - name: SVI configuration 27 | when: inventory_hostname in groups['leaf'] 28 | set_fact: 29 | svi_ipv6_block: "{{ svi_ipv6_block }} ! {{ svi_ipv6_dict[item.key] }} {{ lookup('template', item.key ) }}" 30 | with_dict: "{{ svi_ipv6_dict }}" 31 | 32 | 33 | - name: Render template files output 34 | set_fact: 35 | rendered: "{{ svi_ipv6_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 36 | 37 | - name: Copy configurations to respective file ( -overlay.txt ) under 'preview_files' folder 38 | copy: 39 | dest: preview_files/{{inventory_hostname}}-ipv6_inc.txt 40 | content: "{{ rendered }}" 41 | 42 | - name: Clearing configurations in preview_files/-inc.txt if device config is skipped 43 | when: not file_state.stat.exists 44 | copy: 45 | dest: preview_files/{{inventory_hostname}}-ipv6_inc.txt 46 | content: "" 47 | 48 | - name: Display output file info 49 | hosts: localhost 50 | gather_facts: no 51 | run_once: True 52 | 53 | tasks: 54 | - debug: 55 | msg: Please refer to -ipv6_inc.txt in 'preview_files' folder 56 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_delete_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Write DAG delete info to files in host_vars/delete_vars folder 4 | import_playbook: playbook_overlay_delete_generate.yml 5 | 6 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 7 | hosts: leaf 8 | gather_facts: no 9 | vars: 10 | update_acc_intf: false 11 | block_dict: 12 | '../templates/svi_create.j2': 'svi block' 13 | '../templates/nve_create.j2': 'nve block' 14 | '../templates/vlan_create.j2': 'vlan block' 15 | '../templates/vrf_definition.j2': 'vrf block' 16 | 17 | tasks: 18 | 19 | - name: Load variables from group_vars/delete_vars.yml file 20 | run_once: true 21 | include_vars: group_vars/delete_vars.yml 22 | 23 | - name: Load variables from group_vars/delete_vars.yml file and appending to dag keyword 24 | when: l3vni is defined 25 | set_fact: 26 | dag: "{{ l3vni }}" 27 | 28 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 29 | run_once: true 30 | when: update_access is defined 31 | set_fact: 32 | update_acc_intf: "{{ update_access }}" 33 | 34 | - name: Check whether host_vars/delete_vars/.yml is present 35 | local_action: stat path=./host_vars/delete_vars/{{ inventory_hostname }}.yml 36 | register: file_state 37 | become: no 38 | 39 | - name: Executing tasks if host_vars/delete_vars/.yml is present 40 | when: file_state.stat.exists 41 | block: 42 | - name: Load variables from files under host_vars/delete_vars folder 43 | include_vars: host_vars/delete_vars/{{inventory_hostname}}.yml 44 | 45 | - name: Applying configurations 46 | cli_config: 47 | config: "{{ lookup('template', item.key) }}" 48 | diff_match: none 49 | register: result 50 | with_dict: "{{ block_dict }}" 51 | 52 | - name: Applying configurations on access interfaces 53 | when: update_acc_intf == true 54 | cli_config: 55 | config: "{{ lookup('template', '../templates/access_interfaces.j2' ) }}" 56 | diff_match: none 57 | register: result 58 | 59 | - name: Pause for 30 seconds for convergence 60 | wait_for: 61 | delay: 30 62 | timeout: 0 63 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_incremental_ipv6_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write l3vni ipv6 incremental info to files in host_vars/ipv6_inc_vars folder 3 | import_playbook: playbook_overlay_incremental_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | # vars_files: 9 | # - "group_vars/overlay_db.yml" 10 | vars: 11 | svi_ipv6_block : " " 12 | svi_ipv6_dict: 13 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 14 | tasks: 15 | - name: Check whether host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 16 | local_action: stat path=./host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 17 | register: file_state 18 | become: no 19 | 20 | - name: Executing tasks if host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml is present 21 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 22 | block: 23 | - name: Load variables from files under host_vars/ipv6_inc_vars folder 24 | include_vars: host_vars/ipv6_inc_vars/{{ inventory_hostname }}.yml 25 | 26 | - name: SVI configuration 27 | when: inventory_hostname in groups['leaf'] 28 | set_fact: 29 | svi_ipv6_block: "{{ svi_ipv6_block }} ! {{ svi_ipv6_dict[item.key] }} {{ lookup('template', item.key ) }}" 30 | with_dict: "{{ svi_ipv6_dict }}" 31 | 32 | 33 | - name: Render template files output 34 | set_fact: 35 | rendered: "{{ svi_ipv6_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 36 | 37 | - name: Copy configurations to respective file ( -overlay.txt ) under 'preview_files' folder 38 | copy: 39 | dest: preview_files/{{inventory_hostname}}-ipv6_inc.txt 40 | content: "{{ rendered }}" 41 | 42 | - name: Clearing configurations in preview_files/-ipv6_inc.txt if device config is skipped 43 | when: not file_state.stat.exists 44 | copy: 45 | dest: preview_files/{{inventory_hostname}}-ipv6_inc.txt 46 | content: "" 47 | 48 | - name: Display output file info 49 | hosts: localhost 50 | gather_facts: no 51 | run_once: True 52 | 53 | tasks: 54 | - debug: 55 | msg: Please refer to -ipv6_inc.txt in 'preview_files' folder 56 | -------------------------------------------------------------------------------- /dag/playbook_overlay_delete_ipv6_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write DAG incremental info to files in host_vars/inc_vars folder 3 | import_playbook: playbook_overlay_delete_ipv6_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | # vars_files: 9 | # - "group_vars/overlay_db.yml" 10 | vars: 11 | svi_ipv6_block : " " 12 | svi_ipv6_dict: 13 | '../templates/ipv6_incremental.j2': 'svi ipv6 block' 14 | tasks: 15 | - name: Check whether host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 16 | local_action: stat path=./host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 17 | register: file_state 18 | become: no 19 | 20 | - name: Executing tasks if host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml is present 21 | when: file_state.stat.exists and inventory_hostname in groups['leaf'] 22 | block: 23 | - name: Load variables from files under host_vars/ipv6_delete_vars folder 24 | include_vars: host_vars/ipv6_delete_vars/{{ inventory_hostname }}.yml 25 | 26 | - name: SVI configuration 27 | when: inventory_hostname in groups['leaf'] 28 | set_fact: 29 | svi_ipv6_block: "{{ svi_ipv6_block }} ! {{ svi_ipv6_dict[item.key] }} {{ lookup('template', item.key ) }}" 30 | with_dict: "{{ svi_ipv6_dict }}" 31 | 32 | 33 | - name: Render template files output 34 | set_fact: 35 | rendered: "{{ svi_ipv6_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 36 | 37 | - name: Copy configurations to respective file ( -ipv6_delete.txt ) under 'preview_files' folder 38 | copy: 39 | dest: preview_files/{{inventory_hostname}}-ipv6_delete.txt 40 | content: "{{ rendered }}" 41 | 42 | - name: Clearing configurations in preview_files/-ipv6_delete.txt if device config is skipped 43 | when: not file_state.stat.exists 44 | copy: 45 | dest: preview_files/{{inventory_hostname}}-ipv6_delete.txt 46 | content: "" 47 | 48 | - name: Display output file info 49 | hosts: localhost 50 | gather_facts: no 51 | run_once: True 52 | 53 | tasks: 54 | - debug: 55 | msg: Please refer to -ipv6_delete.txt in 'preview_files' folder 56 | -------------------------------------------------------------------------------- /l2vni/playbook_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: collect outputs after config 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | 9 | - name: construct show commands from host var file for leafs 10 | when: inventory_hostname in groups['leaf'] 11 | copy: 12 | dest: "output/{{inventory_hostname}}-show_commands.txt" 13 | content: "{{ lookup('template', '../templates/leaf_show_command.j2') }}" 14 | 15 | - name: cat show commands file of leaf 16 | when: inventory_hostname in groups['leaf'] 17 | shell: cat "output/{{inventory_hostname}}-show_commands.txt" 18 | register: leaf_command_buf 19 | 20 | - name: run show command on Leafs 21 | when: inventory_hostname in groups['leaf'] 22 | ios_command: 23 | commands: 24 | "{{ leaf_command_buf.stdout_lines }}" 25 | 26 | register: leaf_output 27 | 28 | - name: save outputs to output file for Leafs 29 | when: inventory_hostname in groups['leaf'] 30 | copy: 31 | dest: "output/{{inventory_hostname}}-show_output.txt" 32 | content: | 33 | "{{leaf_command_buf.stdout_lines | zip(leaf_output.stdout_lines) | to_nice_yaml}}" 34 | 35 | - name: construct show commands from host var file for Spines 36 | when: inventory_hostname in groups['spine'] 37 | copy: 38 | dest: "output/{{inventory_hostname}}-show_commands.txt" 39 | content: "{{ lookup('template', '../templates/spine_show_command.j2') }}" 40 | 41 | - name: cat show commands file of Spines 42 | when: inventory_hostname in groups['spine'] 43 | shell: cat "output/{{inventory_hostname}}-show_commands.txt" 44 | register: spine_command_buf 45 | 46 | - name: run show command on Spines 47 | when: inventory_hostname in groups['spine'] 48 | ios_command: 49 | commands: 50 | "{{ spine_command_buf.stdout_lines }}" 51 | 52 | register: spine_output 53 | 54 | - name: save outputs to output file for Spines 55 | when: inventory_hostname in groups['spine'] 56 | copy: 57 | dest: "output/{{inventory_hostname}}-show_output.txt" 58 | content: | 59 | "{{spine_command_buf.stdout_lines | zip(spine_output.stdout_lines) | to_nice_yaml}}" 60 | 61 | 62 | -------------------------------------------------------------------------------- /l3vni/playbook_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: collect outputs after config 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | 9 | - name: construct show commands from host var file for leafs 10 | when: inventory_hostname in groups['leaf'] 11 | copy: 12 | dest: "output/{{inventory_hostname}}-show_commands.txt" 13 | content: "{{ lookup('template', '../templates/leaf_show_command.j2') }}" 14 | 15 | - name: cat show commands file of leaf 16 | when: inventory_hostname in groups['leaf'] 17 | shell: cat "output/{{inventory_hostname}}-show_commands.txt" 18 | register: leaf_command_buf 19 | 20 | - name: run show command on Leafs 21 | when: inventory_hostname in groups['leaf'] 22 | ios_command: 23 | commands: 24 | "{{ leaf_command_buf.stdout_lines }}" 25 | 26 | register: leaf_output 27 | 28 | - name: save outputs to output file for Leafs 29 | when: inventory_hostname in groups['leaf'] 30 | copy: 31 | dest: "output/{{inventory_hostname}}-show_output.txt" 32 | content: | 33 | "{{leaf_command_buf.stdout_lines | zip(leaf_output.stdout_lines) | to_nice_yaml}}" 34 | 35 | - name: construct show commands from host var file for Spines 36 | when: inventory_hostname in groups['spine'] 37 | copy: 38 | dest: "output/{{inventory_hostname}}-show_commands.txt" 39 | content: "{{ lookup('template', '../templates/spine_show_command.j2') }}" 40 | 41 | - name: cat show commands file of Spines 42 | when: inventory_hostname in groups['spine'] 43 | shell: cat "output/{{inventory_hostname}}-show_commands.txt" 44 | register: spine_command_buf 45 | 46 | - name: run show command on Spines 47 | when: inventory_hostname in groups['spine'] 48 | ios_command: 49 | commands: 50 | "{{ spine_command_buf.stdout_lines }}" 51 | 52 | register: spine_output 53 | 54 | - name: save outputs to output file for Spines 55 | when: inventory_hostname in groups['spine'] 56 | copy: 57 | dest: "output/{{inventory_hostname}}-show_output.txt" 58 | content: | 59 | "{{spine_command_buf.stdout_lines | zip(spine_output.stdout_lines) | to_nice_yaml}}" 60 | 61 | 62 | -------------------------------------------------------------------------------- /dag/playbook_underlay_commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | tasks: 8 | 9 | # Set db file path and load underlay vars 10 | - name: Load and process underlay input file 11 | run_once: true 12 | block: 13 | - name: set underlay vars input file path if not already set 14 | when: input_vars_path is not defined 15 | set_fact: 16 | input_vars_path: "./" 17 | 18 | - name: Load vars from node_vars/.yml input file 19 | include_vars: 20 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 21 | 22 | - name: Configure hostnames 23 | cli_config: 24 | config: "{{ lookup('template', '../templates/hostname.j2') }}" 25 | diff_match: none 26 | register: result 27 | 28 | - name: Enable Unicast/Multicast routing 29 | cli_config: 30 | config: "{{ lookup('template', '../templates/global_routing.j2') }}" 31 | diff_match: none 32 | register: result 33 | 34 | - name: Configure ip addresses 35 | cli_config: 36 | config: "{{ lookup('template', '../templates/underlay_interfaces.j2') }}" 37 | diff_match: none 38 | register: result 39 | 40 | - name: Configure OSPF 41 | cli_config: 42 | config: "{{ lookup('template', '../templates/ospf_interfaces.j2') }}" 43 | diff_match: none 44 | register: result 45 | 46 | - name: Configure ISIS 47 | cli_config: 48 | config: "{{ lookup('template', '../templates/isis_underlay.j2') }}" 49 | diff_match: none 50 | register: result 51 | 52 | - name: Configure PIM on interfaces 53 | cli_config: 54 | config: "{{ lookup('template', '../templates/pim_interfaces.j2') }}" 55 | diff_match: none 56 | register: result 57 | 58 | - name: Configure RP 59 | cli_config: 60 | config: "{{ lookup('template', '../templates/pim_rp.j2') }}" 61 | diff_match: none 62 | register: result 63 | 64 | - name: Configure MSDP peering 65 | when: inventory_hostname in groups['spine'] 66 | cli_config: 67 | config: "{{ lookup('template', '../templates/msdp_peering.j2') }}" 68 | diff_match: none 69 | register: result 70 | -------------------------------------------------------------------------------- /dag/playbook_overlay_incremental_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/inc_vars dir 8 | find: 9 | path: "host_vars/inc_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from incremental_vars.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/create_vars.yml 24 | name: user_input 25 | 26 | - name: Collect vars from host_vars/access_intf 27 | include_vars: 28 | file: host_vars/access_intf/{{ inventory_hostname }}.yml 29 | name: access_intf_input 30 | 31 | - name: Collect vars from overlay_db.yml 32 | run_once: true 33 | include_vars: 34 | file: group_vars/overlay_db.yml 35 | name: to_compare 36 | 37 | - name: Collect vars from hostvars leafs 38 | run_once: true 39 | include_vars: 40 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 41 | name: hostvars_leaf_data 42 | 43 | - name: Collect "show run nve" 44 | cli_command: 45 | command: show run nve 46 | register: command_output 47 | 48 | - name: Collect "show run | sec ^int" 49 | cli_command: 50 | command: show run | sec ^int 51 | register: sec_command_output 52 | 53 | - name: Process data 54 | create_incremental_yml: 55 | userinput: "{{ user_input }}" 56 | access_input: "{{ access_intf_input }}" 57 | overlay_intf: "{{ hostvars_leaf_data }}" 58 | hostvars: "{{ command_output.stdout_lines }}" 59 | tocompare: "{{ to_compare }}" 60 | sec_output: "{{ sec_command_output.stdout_lines }}" 61 | register: module_output 62 | 63 | - name: Copying configurations to the repective files ( .yml ) under host_vars/inc_vars folder 64 | when: module_output['yaml'] != '{}\n' 65 | copy: 66 | dest: host_vars/inc_vars/{{ inventory_hostname }}.yml 67 | content: "{{ module_output['yaml'] }}" 68 | -------------------------------------------------------------------------------- /l2vni/playbook_overlay_incremental_generate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | tasks: 7 | - name: Locate files in host_vars/inc_vars dir 8 | find: 9 | path: "host_vars/inc_vars" 10 | register: cleanup 11 | ignore_errors: true 12 | 13 | - name: Remove files from previous run 14 | file: 15 | path: "{{ item.path }}" 16 | state: absent 17 | with_items: "{{ cleanup.files }}" 18 | ignore_errors: true 19 | 20 | - name: Collect vars from incremental_vars.yml 21 | run_once: true 22 | include_vars: 23 | file: group_vars/create_vars.yml 24 | name: user_input 25 | 26 | - name: Collect vars from host_vars/access_intf 27 | include_vars: 28 | file: host_vars/access_intf/{{ inventory_hostname }}.yml 29 | name: access_intf_input 30 | 31 | - name: Collect vars from overlay_db.yml 32 | run_once: true 33 | include_vars: 34 | file: group_vars/overlay_db.yml 35 | name: to_compare 36 | 37 | - name: Collect vars from hostvars leafs 38 | run_once: true 39 | include_vars: 40 | file: host_vars/node_vars/{{ inventory_hostname }}.yml 41 | name: hostvars_leaf_data 42 | 43 | - name: Collect "show run nve" 44 | cli_command: 45 | command: show run nve 46 | register: command_output 47 | 48 | - name: Collect "show run | sec ^int" 49 | cli_command: 50 | command: show run | sec ^int 51 | register: sec_command_output 52 | 53 | - name: Process data 54 | create_incremental_l2vni_yml: 55 | userinput: "{{ user_input }}" 56 | access_input: "{{ access_intf_input }}" 57 | overlay_intf: "{{ hostvars_leaf_data }}" 58 | hostvars: "{{ command_output.stdout_lines }}" 59 | tocompare: "{{ to_compare }}" 60 | sec_output: "{{ sec_command_output.stdout_lines }}" 61 | register: module_output 62 | 63 | - name: Copying configurations to the repective files ( .yml ) under host_vars/inc_vars folder 64 | when: module_output['yaml'] != '{}\n' 65 | copy: 66 | dest: host_vars/inc_vars/{{ inventory_hostname }}.yml 67 | content: "{{ module_output['yaml'] }}" 68 | -------------------------------------------------------------------------------- /l2vni/playbook_overlay_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | vars_files: 7 | - "group_vars/overlay_db.yml" 8 | vars: 9 | overlay_block: "" 10 | leaf_ol_dict: 11 | '../templates/vlan_create.j2': 'vlan block' 12 | '../templates/l2vpn_evpn_global.j2': 'l2vpn evpn global block' 13 | '../templates/l2vpn_evpn_evi_create.j2': 'l2vpn evpn evi block' 14 | '../templates/evi_vni_vlan_stiching.j2': 'evi vni vlan stiching block' 15 | '../templates/nve_create.j2': 'nve block' 16 | 17 | common_ol_dict: 18 | '../templates/bgp_global.j2': 'bgp global block' 19 | '../templates/bgp_l2vpn_evpn_af.j2': 'bgp l2vpn evpn af block' 20 | 21 | tasks: 22 | # Set db file path and load overlay_db 23 | - name: Load and process overlay input file 24 | run_once: true 25 | block: 26 | - name: set overlay db input file path if not already set 27 | when: input_vars_path is not defined 28 | set_fact: 29 | input_vars_path: "./" 30 | 31 | - name: Load vars from overlay input file 32 | include_vars: 33 | file: "{{ input_vars_path }}group_vars/overlay_db.yml" 34 | 35 | - name: Load vars from node_vars/.yml input file 36 | include_vars: 37 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 38 | 39 | - name: Read template files 40 | when: inventory_hostname in groups['leaf'] 41 | set_fact: 42 | overlay_block: "{{ overlay_block }} ! {{ leaf_ol_dict[item.key] }} {{ lookup('template', item.key ) }}" 43 | with_dict: "{{ leaf_ol_dict }}" 44 | 45 | - name: Read template files - BGP related 46 | set_fact: 47 | overlay_block: "{{ overlay_block }} ! {{ common_ol_dict[item.key] }} {{ lookup('template', item.key ) }}" 48 | with_dict: "{{ common_ol_dict }}" 49 | 50 | - name: Render template files output 51 | set_fact: 52 | rendered: "{{ overlay_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 53 | 54 | - name: Copy configurations to respective file ( -overlay.txt ) under 'preview_files' folder 55 | copy: 56 | dest: preview_files/{{inventory_hostname}}-overlay.txt 57 | content: "{{ rendered }}" 58 | 59 | - name: Display output file info 60 | hosts: localhost 61 | gather_facts: no 62 | run_once: True 63 | 64 | tasks: 65 | - debug: 66 | msg: Please refer to -overlay.txt in 'preview_files' folder 67 | -------------------------------------------------------------------------------- /l2vni/playbook_overlay_delete_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write l2vni delete info to files in host_vars/delete_vars folder 3 | import_playbook: playbook_overlay_delete_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | vars: 9 | whole_block: "" 10 | update_acc_intf: false 11 | block_dict: 12 | '../templates/nve_create.j2': 'nve block' 13 | '../templates/vlan_create.j2': 'vlan block' 14 | '../templates/l2vpn_evpn_evi_create.j2': 'l2vpn evpn evi block' 15 | 16 | tasks: 17 | 18 | - name: Load variables from group_vars/delete_vars.yml file 19 | run_once: true 20 | include_vars: group_vars/delete_vars.yml 21 | 22 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 23 | run_once: true 24 | when: update_access is defined 25 | set_fact: 26 | update_acc_intf: "{{ update_access }}" 27 | 28 | - name: Check whether host_vars/delete_vars/.yml is present 29 | local_action: stat path=./host_vars/delete_vars/{{ inventory_hostname }}.yml 30 | register: file_state 31 | become: no 32 | 33 | - name: Executing tasks if host_vars/delete_vars/.yml is present 34 | when: file_state.stat.exists 35 | block: 36 | - name: Load variables from files under host_vars/delete_vars folder 37 | include_vars: host_vars/delete_vars/{{ inventory_hostname }}.yml 38 | 39 | - name: Read template files 40 | set_fact: 41 | whole_block: "{{ whole_block }} ! {{ block_dict[item.key] }} {{ lookup('template', item.key) }}" 42 | with_dict: "{{ block_dict }}" 43 | 44 | - name: Read access interface template files 45 | when: update_acc_intf == true 46 | set_fact: 47 | whole_block: "{{ whole_block }} ! access interfaces block {{ lookup('template', '../templates/access_interfaces.j2' ) }}" 48 | 49 | - name: Render template files output 50 | set_fact: 51 | rendered: "{{ whole_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 52 | 53 | - name: Copy configurations to the respective file ( -delete.txt ) under 'preview_files' folder 54 | copy: 55 | dest: preview_files/{{inventory_hostname}}-delete.txt 56 | content: "{{ rendered }}" 57 | 58 | - name: Display output file info 59 | hosts: localhost 60 | gather_facts: no 61 | run_once: True 62 | 63 | tasks: 64 | - debug: 65 | msg: Please refer to -delete.txt in 'preview_files' folder 66 | -------------------------------------------------------------------------------- /templates/svi_create.j2: -------------------------------------------------------------------------------- 1 | {# 2 | This module is configuring, deleting or modifing SVI. 3 | If action is not set in the configurations, it is "add" by default. 4 | #} 5 | 6 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 7 | 8 | {% if svis is defined %} 9 | {% for svi in svis %} 10 | {% if ( (svi_cli is defined and svi in svi_cli) or (svi_cli is not defined) ) %} 11 | {% set action = svis[svi].action | default ('add') %} 12 | {% if action == 'delete' %} 13 | ! 14 | no interface Vlan{{svi}} 15 | {% elif action == 'add' %} 16 | {% if svis[svi].svi_type == 'access' %} 17 | ! 18 | interface Vlan{{svi}} 19 | vrf forwarding {{ svis[svi].vrf }} 20 | ip address {{svis[svi].ipv4}} 21 | {% if svis[svi].ipv6 is defined %} 22 | {% for ipv6_addr in svis[svi].ipv6 %} 23 | ipv6 address {{ipv6_addr}} 24 | {% endfor %} 25 | {% endif %} 26 | no shut 27 | {% elif svis[svi].svi_type == 'core' %} 28 | ! 29 | interface Vlan{{svi}} 30 | vrf forwarding {{ svis[svi].vrf }} 31 | ip unnumbered {{ svis[svi].src_intf }} 32 | {% if svis[svi].ipv6_enable is defined %} 33 | ipv6 enable 34 | {% endif %} 35 | no autostate 36 | no shut 37 | {% elif svis[svi].svi_type == 'non_vxlan' %} 38 | ! 39 | interface Vlan{{svi}} 40 | vrf forwarding {{ svis[svi].vrf }} 41 | ip address {{svis[svi].ipv4}} 42 | {% if svis[svi].ipv6 is defined %} 43 | {% for ipv6_addr in svis[svi].ipv6 %} 44 | ipv6 address {{ipv6_addr}} 45 | {% endfor %} 46 | {% endif %} 47 | no shut 48 | {% endif %} 49 | 50 | {# Check if PIM configuration is needed on the SVI #} 51 | {% set pim_enable = svis[svi].pim_enable | default('no') %} 52 | {% if pim_enable == 'yes' %} 53 | ip pim sparse-mode 54 | {% elif pim_enable == 'no' %} 55 | no ip pim sparse-mode 56 | {% endif %} 57 | 58 | {# Check if static MAC configuration is needed on the SVI #} 59 | {% if svis[svi].mac is defined and l2vpn_global.default_gw == "no" %} 60 | mac-address {{ svis[svi].mac }} 61 | {% endif %} 62 | 63 | {% endif %} 64 | {% endif %} 65 | {% endfor %} 66 | {% endif %} 67 | -------------------------------------------------------------------------------- /dag/library/dhcp_src_addr_info.py: -------------------------------------------------------------------------------- 1 | # Get the DAG related informations from 'show run nve' and 'show run | section ^interface' parsed CLI output 2 | 3 | from __future__ import (absolute_import, division, print_function) 4 | __metaclass__ = type 5 | 6 | from ansible.module_utils.basic import AnsibleModule 7 | 8 | DOCUMENTATION = r''' 9 | --- 10 | module: dhcp_src_addr_info 11 | 12 | short_description: This module contains functions used in preprocessing, for DHCP related playbooks 13 | ''' 14 | 15 | def dhcp_vrf_addr_test(dhcp_dict): 16 | 17 | ret_dict = {} 18 | ret_dict['global_src_err'] = False 19 | ret_dict['no_src_vrf'] = [] 20 | 21 | for vrf in dhcp_dict['vrfs']: 22 | if 'helper_vrf' not in dhcp_dict['vrfs'][vrf]: 23 | if 'relay_src_intf' not in dhcp_dict['vrfs'][vrf]: 24 | ret_dict['no_src_vrf'].append(vrf) 25 | else: 26 | if dhcp_dict['vrfs'][vrf]['helper_vrf'] == 'global': 27 | if 'relay_src_intf' not in dhcp_dict['vrfs'][vrf]: 28 | ret_dict['global_src_err'] = vrf 29 | break 30 | else: 31 | if 'relay_src_intf' not in dhcp_dict['vrfs'][vrf]: 32 | ret_dict['no_src_vrf'].append(dhcp_dict['vrfs'][vrf]['helper_vrf']) 33 | 34 | ret_dict['no_src_vrf'] = list(dict.fromkeys(ret_dict['no_src_vrf'])) 35 | 36 | return ret_dict 37 | 38 | def dhcp_vrf_ovrly_intf(ovrly_intf_info): 39 | 40 | no_src_vrf = ovrly_intf_info['no_src_vrf'] 41 | 42 | if 'all' in no_src_vrf: no_src_vrf = ovrly_intf_info['all_vrfs'] 43 | 44 | ret_dict = {} 45 | ret_dict['overlay_intf'] = {} 46 | ret_dict['intf_err'] = False 47 | overlay_intf = ovrly_intf_info['overlay_intf'] 48 | 49 | if no_src_vrf != []: 50 | for vrf in no_src_vrf: 51 | for intf in overlay_intf: 52 | if overlay_intf[intf]['vrf'] == vrf: 53 | ret_dict['overlay_intf'][vrf] = intf 54 | break 55 | if vrf not in ret_dict['overlay_intf']: 56 | ret_dict['intf_err'] = vrf 57 | break 58 | 59 | return ret_dict 60 | 61 | def run_module(): 62 | module = AnsibleModule( 63 | argument_spec=dict( 64 | dhcp_info=dict(required=False,type='dict'), 65 | ovrly_intf_info=dict(required=False,type='dict') 66 | ), 67 | supports_check_mode=True 68 | ) 69 | 70 | result = {} 71 | if module.params['dhcp_info']: 72 | dhcp_info = module.params['dhcp_info'] 73 | result['dhcp_ret_dict'] = dhcp_vrf_addr_test(dhcp_info) 74 | 75 | if module.params['ovrly_intf_info']: 76 | ovrly_intf_info = module.params['ovrly_intf_info'] 77 | result['dhcp_ret_dict'] = dhcp_vrf_ovrly_intf(ovrly_intf_info) 78 | 79 | module.exit_json(**result) 80 | 81 | 82 | def main(): 83 | run_module() 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /dag/playbook_overlay_delete_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write DAG delete info to files in host_vars/delete_vars folder 3 | import_playbook: playbook_overlay_delete_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | vars: 9 | whole_block: "" 10 | update_acc_intf: false 11 | block_dict: 12 | '../templates/svi_create.j2': 'svi block' 13 | '../templates/nve_create.j2': 'nve block' 14 | '../templates/vlan_create.j2': 'vlan block' 15 | '../templates/l2vpn_evpn_evi_create.j2': 'l2vpn evpn evi block' 16 | '../templates/vrf_definition.j2': 'vrf block' 17 | 18 | tasks: 19 | 20 | - name: Load variables from group_vars/delete_vars.yml file 21 | run_once: true 22 | include_vars: group_vars/delete_vars.yml 23 | 24 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 25 | run_once: true 26 | when: update_access is defined 27 | set_fact: 28 | update_acc_intf: "{{ update_access }}" 29 | 30 | - name: Check whether host_vars/delete_vars/.yml is present 31 | local_action: stat path=./host_vars/delete_vars/{{ inventory_hostname }}.yml 32 | register: file_state 33 | become: no 34 | 35 | - name: Executing tasks if host_vars/delete_vars/.yml is present 36 | when: file_state.stat.exists 37 | block: 38 | - name: Load variables from files under host_vars/delete_vars folder 39 | include_vars: host_vars/delete_vars/{{ inventory_hostname }}.yml 40 | 41 | - name: Read template files 42 | set_fact: 43 | whole_block: "{{ whole_block }} ! {{ block_dict[item.key] }} {{ lookup('template', item.key) }}" 44 | with_dict: "{{ block_dict }}" 45 | 46 | - name: Read access interface template files 47 | when: update_acc_intf == true 48 | set_fact: 49 | whole_block: "{{ whole_block }} ! access interfaces block {{ lookup('template', '../templates/access_interfaces.j2' ) }}" 50 | 51 | - name: Render template files output 52 | set_fact: 53 | rendered: "{{ whole_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 54 | 55 | - name: Copy configurations to the respective file ( -delete.txt ) under 'preview_files' folder 56 | copy: 57 | dest: preview_files/{{inventory_hostname}}-delete.txt 58 | content: "{{ rendered }}" 59 | 60 | - name: Display output file info 61 | hosts: localhost 62 | gather_facts: no 63 | run_once: True 64 | 65 | tasks: 66 | - debug: 67 | msg: Please refer to -delete.txt in 'preview_files' folder 68 | -------------------------------------------------------------------------------- /dag/playbook_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: collect outputs after config 4 | hosts: all 5 | gather_facts: no 6 | connection: network_cli 7 | vars_files: 8 | - "group_vars/overlay_db.yml" 9 | tasks: 10 | 11 | - name: construct show commands from host var file for leafs 12 | when: inventory_hostname in groups['leaf'] 13 | copy: 14 | dest: "output/{{inventory_hostname}}-show_commands.txt" 15 | content: "{{ lookup('template', '../templates/leaf_show_command.j2') }}" 16 | 17 | - name: cat show commands file of leaf 18 | when: inventory_hostname in groups['leaf'] 19 | shell: cat "output/{{inventory_hostname}}-show_commands.txt" 20 | register: leaf_command_buf 21 | 22 | - name: run show command on Leafs 23 | when: inventory_hostname in groups['leaf'] 24 | ios_command: 25 | commands: 26 | "{{ leaf_command_buf.stdout_lines }}" 27 | 28 | register: leaf_output 29 | 30 | - name: save outputs to output file for Leafs 31 | when: inventory_hostname in groups['leaf'] 32 | copy: 33 | dest: "output/{{inventory_hostname}}-show_output.txt" 34 | content: | 35 | "{{leaf_command_buf.stdout_lines | zip(leaf_output.stdout_lines) | to_nice_yaml}}" 36 | 37 | - name: construct show commands from host var file for Spines 38 | when: inventory_hostname in groups['spine'] 39 | copy: 40 | dest: "output/{{inventory_hostname}}-show_commands.txt" 41 | content: "{{ lookup('template', '../templates/spine_show_command.j2') }}" 42 | 43 | - name: cat show commands file of Spines 44 | when: inventory_hostname in groups['spine'] 45 | shell: cat "output/{{inventory_hostname}}-show_commands.txt" 46 | register: spine_command_buf 47 | 48 | - name: run show command on Spines 49 | when: inventory_hostname in groups['spine'] 50 | ios_command: 51 | commands: 52 | "{{ spine_command_buf.stdout_lines }}" 53 | 54 | register: spine_output 55 | 56 | - name: save outputs to output file for Spines 57 | when: inventory_hostname in groups['spine'] 58 | copy: 59 | dest: "output/{{inventory_hostname}}-show_output.txt" 60 | content: | 61 | "{{spine_command_buf.stdout_lines | zip(spine_output.stdout_lines) | to_nice_yaml}}" 62 | 63 | - name: Display output file info 64 | run_once: true 65 | debug: 66 | msg: Please refer to -show_commands.txt in 'output' folder 67 | 68 | - name: Display output file info 69 | run_once: true 70 | debug: 71 | msg: Please refer to -show_output.txt in 'output' folder 72 | 73 | 74 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_delete_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Write l3vni delete info to files in host_vars/delete_vars folder 3 | import_playbook: playbook_overlay_delete_generate.yml 4 | 5 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 6 | hosts: leaf 7 | gather_facts: no 8 | vars: 9 | whole_block: "" 10 | update_acc_intf: false 11 | block_dict: 12 | '../templates/svi_create.j2': 'svi block' 13 | '../templates/nve_create.j2': 'nve block' 14 | '../templates/vlan_create.j2': 'vlan block' 15 | '../templates/vrf_definition.j2': 'vrf block' 16 | 17 | tasks: 18 | 19 | - name: Load variables from group_vars/delete_vars.yml file 20 | run_once: true 21 | include_vars: group_vars/delete_vars.yml 22 | 23 | - name: Load variables from group_vars/delete_vars.yml file and appending to dag keyword 24 | when: l3vni is defined 25 | set_fact: 26 | dag: "{{ l3vni }}" 27 | 28 | - name: Get "update_access" value if present in group_vars/delete_vars.yml 29 | run_once: true 30 | when: update_access is defined 31 | set_fact: 32 | update_acc_intf: "{{ update_access }}" 33 | 34 | - name: Check whether host_vars/delete_vars/.yml is present 35 | local_action: stat path=./host_vars/delete_vars/{{ inventory_hostname }}.yml 36 | register: file_state 37 | become: no 38 | 39 | - name: Executing tasks if host_vars/delete_vars/.yml is present 40 | when: file_state.stat.exists 41 | block: 42 | - name: Load variables from files under host_vars/delete_vars folder 43 | include_vars: host_vars/delete_vars/{{ inventory_hostname }}.yml 44 | 45 | - name: Read template files 46 | set_fact: 47 | whole_block: "{{ whole_block }} ! {{ block_dict[item.key] }} {{ lookup('template', item.key) }}" 48 | with_dict: "{{ block_dict }}" 49 | 50 | - name: Read access interface template files 51 | when: update_acc_intf == true 52 | set_fact: 53 | whole_block: "{{ whole_block }} ! access interfaces block {{ lookup('template', '../templates/access_interfaces.j2' ) }}" 54 | 55 | - name: Render template files output 56 | set_fact: 57 | rendered: "{{ whole_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 58 | 59 | - name: Copy configurations to the respective file ( -delete.txt ) under 'preview_files' folder 60 | copy: 61 | dest: preview_files/{{inventory_hostname}}-delete.txt 62 | content: "{{ rendered }}" 63 | 64 | - name: Display output file info 65 | hosts: localhost 66 | gather_facts: no 67 | run_once: True 68 | 69 | tasks: 70 | - debug: 71 | msg: Please refer to -delete.txt in 'preview_files' folder 72 | -------------------------------------------------------------------------------- /l2vni/playbook_access_add_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | playbook_mode: add 8 | info_dict: 9 | access: 10 | key_name: 'access_vlan' 11 | trunks: 12 | key_name: 'trunk_vlan_list' 13 | 14 | tasks: 15 | - name: Check playbook mode 16 | run_once: true 17 | when: incremental is defined 18 | set_fact: 19 | playbook_mode: inc 20 | 21 | - name: Clear variable from previous run 22 | set_fact: 23 | intf_conf: [] 24 | 25 | - name: set access interface input folder path if not already set 26 | when: input_vars_path is not defined 27 | set_fact: 28 | input_vars_path: "./" 29 | 30 | - name: Load variables from access interface input file if present 31 | block: 32 | - name: Check whether host_vars/access_intf/.yml is present 33 | local_action: stat path="{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 34 | register: file_state 35 | become: no 36 | 37 | - name: Load variables from host_vars/access_intf/.yml if present 38 | include_vars: 39 | file: "{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 40 | name: get_Leaf_vars 41 | when: file_state.stat.exists 42 | 43 | # Following tasks are skippped if host_vars/access_intf/.yml is not present 44 | 45 | - name: Process interfaces 46 | when: (get_Leaf_vars is defined) and (get_Leaf_vars.access_interfaces is defined) 47 | block: 48 | - set_fact: 49 | execute_task: "../subtasks/subtask_acc_intf_preview.yml" 50 | 51 | - name: Process trunk interfaces 52 | when: get_Leaf_vars.access_interfaces.trunks is defined 53 | include_tasks: ../subtasks/subtask_acc_intf_trunk.yml 54 | 55 | - name: Process access interfaces 56 | when: get_Leaf_vars.access_interfaces.access is defined 57 | include_tasks: ../subtasks/subtask_acc_intf_access.yml 58 | 59 | - name: Copy configurations to respective file ( -{add/inc}-intf.txt ) under 'preview_files' folder 60 | copy: 61 | dest: preview_files/{{inventory_hostname}}-{{ playbook_mode }}-intf.txt 62 | content: "\n! access interface block \n{{ intf_conf | join('\n') }}" 63 | 64 | - name: Display output file info 65 | hosts: localhost 66 | gather_facts: no 67 | run_once: True 68 | vars: 69 | playbook_mode: add 70 | 71 | tasks: 72 | - name: Check playbook mode 73 | when: incremental is defined 74 | set_fact: 75 | playbook_mode: inc 76 | 77 | - debug: 78 | msg: Please refer to -{{ playbook_mode }}-intf.txt in 'preview_files' folder 79 | -------------------------------------------------------------------------------- /dag/playbook_access_add_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L2/L3 underlay w/ Spine 4 | hosts: leaf 5 | gather_facts: no 6 | vars: 7 | playbook_mode: add 8 | info_dict: 9 | access: 10 | key_name: 'access_vlan' 11 | trunks: 12 | key_name: 'trunk_vlan_list' 13 | 14 | tasks: 15 | - name: Check playbook mode 16 | run_once: true 17 | when: incremental is defined 18 | set_fact: 19 | playbook_mode: inc 20 | 21 | - name: Clear variable from previous run 22 | set_fact: 23 | intf_conf: [] 24 | 25 | - name: set access interface input folder path if not already set 26 | when: input_vars_path is not defined 27 | set_fact: 28 | input_vars_path: "./" 29 | 30 | - name: Load variables from access interface input file if present 31 | block: 32 | - name: Check whether host_vars/access_intf/.yml is present 33 | local_action: stat path="{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 34 | register: file_state 35 | become: no 36 | 37 | - name: Load variables from host_vars/access_intf/.yml if present 38 | include_vars: 39 | file: "{{ input_vars_path }}host_vars/access_intf/{{ inventory_hostname }}.yml" 40 | name: get_Leaf_vars 41 | when: file_state.stat.exists 42 | 43 | # Following tasks are skippped if host_vars/access_intf/.yml is not present 44 | 45 | - name: Process interfaces 46 | when: (get_Leaf_vars is defined) and (get_Leaf_vars.access_interfaces is defined) 47 | block: 48 | - set_fact: 49 | execute_task: "../subtasks/subtask_acc_intf_preview.yml" 50 | 51 | - name: Process trunk interfaces 52 | when: get_Leaf_vars.access_interfaces.trunks is defined 53 | include_tasks: ../subtasks/subtask_acc_intf_trunk.yml 54 | 55 | - name: Process access interfaces 56 | when: get_Leaf_vars.access_interfaces.access is defined 57 | include_tasks: ../subtasks/subtask_acc_intf_access.yml 58 | 59 | - name: Copy configurations to respective file ( -{add/inc}-intf.txt ) under 'preview_files' folder 60 | copy: 61 | dest: preview_files/{{inventory_hostname}}-{{ playbook_mode }}-intf.txt 62 | content: "\n! access interface block \n{{ intf_conf | join('\n') }}" 63 | 64 | - name: Display output file info 65 | hosts: localhost 66 | gather_facts: no 67 | run_once: True 68 | vars: 69 | playbook_mode: add 70 | 71 | tasks: 72 | - name: Check playbook mode 73 | when: incremental is defined 74 | set_fact: 75 | playbook_mode: inc 76 | 77 | - debug: 78 | msg: Please refer to -{{ playbook_mode }}-intf.txt in 'preview_files' folder 79 | -------------------------------------------------------------------------------- /templates/ipv6_incremental.j2: -------------------------------------------------------------------------------- 1 | {# 2 | 3 | This module incrementally adding/removing ipv6 configuration to the fabric. 4 | 5 | #} 6 | 7 | #jinja2: lstrip_blocks: "True", trim_blocks: "True" 8 | 9 | {% set action = ipv6_action | default ('add') %} 10 | {% if action == 'delete' %} 11 | {% set prefix = 'no' %} 12 | {% elif action == 'add' %} 13 | {% set prefix = '' %} 14 | {% endif %} 15 | 16 | {# Global ipv6 unicast routing enable/disable #} 17 | {% if ipv6_routing is defined %} 18 | ipv6 unicast-routing 19 | {% endif %} 20 | 21 | {# vrf configuration #} 22 | 23 | {% if vrfs is defined %} 24 | {% for vrf in vrfs %} 25 | 26 | vrf definition {{ vrf }} 27 | 28 | {# Checking an action for the AF #} 29 | {% if vrfs[vrf].afs is defined %} 30 | {% for af in vrfs[vrf].afs %} 31 | {% set action = vrfs[vrf].afs[af].action | default ('add') %} 32 | {% if action == 'delete' %} 33 | no address-family {{ af }} 34 | {% elif action == 'add' %} 35 | address-family {{ af }} 36 | {# RT import section #} 37 | {% for rti in vrfs[vrf].afs[af].rt_import %} 38 | {{ prefix }} route-target import {{ rti }} 39 | {% endfor %} 40 | 41 | {# RT export section #} 42 | {% for rte in vrfs[vrf].afs[af].rt_export %} 43 | route-target export {{ rte}} 44 | {% endfor %} 45 | {% endif %} 46 | {% endfor %} 47 | {% endif %} 48 | {% endfor %} 49 | {% endif %} 50 | 51 | {# SVI configuration #} 52 | {% if svis is defined %} 53 | {% for svi in svis %} 54 | 55 | interface Vlan{{svi}} 56 | 57 | {% if svis[svi].ipv6 is defined %} 58 | {% for ipv6_addr in svis[svi].ipv6 %} 59 | {{prefix}} ipv6 address {{ipv6_addr}} 60 | {{ prefix }} ipv6 enable 61 | {% endfor %} 62 | {% elif svis[svi].ipv6_enable is defined %} 63 | {{ prefix }} ipv6 enable 64 | {% endif %} 65 | 66 | {% endfor %} 67 | {% endif %} 68 | 69 | {# bgp configuration #} 70 | 71 | {% if vrfs is defined -%} 72 | router bgp {{ bgp.as_number }} 73 | {% for vrf in vrfs -%} 74 | {% for af in vrfs[vrf].afs -%} 75 | {% if prefix == 'no'%} 76 | {{ prefix }} address-family {{ af }} vrf {{ vrf }} 77 | {% else %} 78 | address-family {{ af }} vrf {{ vrf }} 79 | advertise l2vpn evpn 80 | redistribute connected 81 | redistribute static 82 | {# Check if something else needed to be redistributed except static or connected #} 83 | {% if bgp.afs[af].vrf[vrf].redistribute is defined %} 84 | {% for redist in bgp.afs[af].vrf[vrf].redistribute %} 85 | redistribute {{redist}} 86 | {% endfor%} 87 | {% endif %} 88 | {% endif %} 89 | {% endfor%} 90 | {% endfor %} 91 | {% endif %} 92 | -------------------------------------------------------------------------------- /dag/group_vars/overlay_db.yml: -------------------------------------------------------------------------------- 1 | l2vpn_global: 2 | replication_type: 'static' 3 | router_id: 'Loopback1' 4 | default_gw: 'yes' 5 | 6 | vrfs: 7 | green: 8 | description: 'green VRF defn' 9 | rd: '1:1' 10 | afs: 11 | ipv4: 12 | rt_import: 13 | - '1:1' 14 | - '1:1 stitching' 15 | rt_export: 16 | - '1:1' 17 | - '1:1 stitching' 18 | 19 | ipv6: 20 | rt_import: 21 | - '1:1' 22 | - '1:1 stitching' 23 | rt_export: 24 | - '1:1' 25 | - '1:1 stitching' 26 | 27 | blue: 28 | description: 'blue VRF defn' 29 | rd: '2:2' 30 | afs: 31 | ipv4: 32 | rt_import: 33 | - '2:2' 34 | - '2:2 stitching' 35 | rt_export: 36 | - '2:2' 37 | - '2:2 stitching' 38 | ipv6: 39 | rt_import: 40 | - '2:2' 41 | - '2:2 stitching' 42 | rt_export: 43 | - '2:2' 44 | 45 | vlans: 46 | #vrf green vlans 47 | 101: 48 | vlan_type: 'access' 49 | description: 'Access_VLAN_101' 50 | vni: '10101' 51 | evi: '101' 52 | type: 'vlan-based' 53 | encapsulation: 'vxlan' 54 | replication_type: 'static' 55 | replication_mcast: '225.0.0.101' 56 | 57 | 102: 58 | vlan_type: 'access' 59 | description: 'Access_VLAN_102' 60 | vni: '10102' 61 | evi: '102' 62 | type: 'vlan-based' 63 | encapsulation: 'vxlan' 64 | replication_type: 'ingress' 65 | 66 | 901: 67 | vlan_type: 'core' 68 | description: 'Core_VLAN_VRF_green' 69 | vni: '50901' 70 | vrf: 'green' 71 | 72 | #vrf blue vlans 73 | 201: 74 | vlan_type: 'access' 75 | description: 'Access_VLAN_201' 76 | vni: '10201' 77 | evi: '201' 78 | type: 'vlan-based' 79 | encapsulation: 'vxlan' 80 | replication_type: 'static' 81 | replication_mcast: '225.0.0.101' 82 | 83 | 202: 84 | vlan_type: 'access' 85 | description: 'Access_VLAN_202' 86 | vni: '10202' 87 | evi: '202' 88 | type: 'vlan-based' 89 | encapsulation: 'vxlan' 90 | replication_type: 'ingress' 91 | 92 | 902: 93 | vlan_type: 'core' 94 | description: 'Core_VLAN_VRF_blue' 95 | vni: '50902' 96 | vrf: 'blue' 97 | 98 | svis: 99 | #vrf green svi's 100 | 101: 101 | svi_type: 'access' 102 | vrf: 'green' 103 | ipv4: '10.1.101.1 255.255.255.0' 104 | ipv6: 105 | - '2001:101::1/64' 106 | mac: 'dead.beef.abcd' 107 | 108 | 102: 109 | svi_type: 'access' 110 | vrf: 'green' 111 | ipv4: '10.1.102.1 255.255.255.0' 112 | ipv6: 113 | - '2001:102::1/64' 114 | mac: 'dead.beef.abcd' 115 | 116 | 901: 117 | svi_type: 'core' 118 | vrf: 'green' 119 | src_intf: 'Loopback1' 120 | ipv6_enable: 'yes' 121 | 122 | #vrf blue svi's 123 | 201: 124 | svi_type: 'access' 125 | vrf: 'blue' 126 | ipv4: '10.1.201.1 255.255.255.0' 127 | ipv6: 128 | - '2001:201::1/64' 129 | 130 | 202: 131 | svi_type: 'access' 132 | vrf: 'blue' 133 | ipv4: '10.1.202.1 255.255.255.0' 134 | ipv6: 135 | - '2001:202::1/64' 136 | 137 | 902: 138 | svi_type: 'core' 139 | vrf: 'blue' 140 | src_intf: 'Loopback1' 141 | ipv6_enable: 'yes' 142 | 143 | nve_interfaces: 144 | 1: 145 | source_interface: 'Loopback1' -------------------------------------------------------------------------------- /l3vni/playbook_overlay_preview.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Automated VXLAN deployment with BGP EVPN L3 underlay w/ Spine 4 | hosts: all 5 | gather_facts: no 6 | vars_files: 7 | - "group_vars/overlay_db.yml" 8 | vars: 9 | overlay_block: "" 10 | leaf_ol_dict: 11 | '../templates/vrf_definition.j2': 'vrf definition block' 12 | '../templates/bgp_l2vpn_ipv46_per_vrf.j2': 'bgp per vrf block' 13 | '../templates/vlan_create.j2': 'vlan block' 14 | '../templates/svi_create.j2': 'svi block' 15 | '../templates/l2vpn_evpn_global.j2': 'l2vpn evpn global block' 16 | '../templates/evi_vni_vlan_stiching.j2': 'evi vni vlan stiching block' 17 | '../templates/overlay_interfaces.j2': 'overlay interface block' 18 | '../templates/nve_create.j2': 'nve block' 19 | 20 | common_ol_dict: 21 | '../templates/bgp_global.j2': 'bgp global block' 22 | '../templates/bgp_l2vpn_evpn_af.j2': 'bgp l2vpn evpn af block' 23 | 24 | tasks: 25 | # Set db file path and load overlay_db 26 | - name: Load and process overlay input file 27 | run_once: true 28 | block: 29 | - name: set overlay db input file path if not already set 30 | when: input_vars_path is not defined 31 | set_fact: 32 | input_vars_path: "./" 33 | 34 | - name: Load vars from overlay input file 35 | include_vars: 36 | file: "{{ input_vars_path }}group_vars/overlay_db.yml" 37 | 38 | - name: Check for IPv6 config under VRFs 39 | set_fact: 40 | ipv6_unicast: 'enable' 41 | when: '"ipv6" in item.value.afs and ipv6_unicast is not defined' 42 | with_dict: "{{ vrfs }}" 43 | 44 | - name: Load vars from node_vars/.yml input file 45 | include_vars: 46 | file: "{{ input_vars_path }}host_vars/node_vars/{{ inventory_hostname }}.yml" 47 | 48 | - name: Read template files 49 | when: inventory_hostname in groups['leaf'] 50 | set_fact: 51 | overlay_block: "{{ overlay_block }} ! {{ leaf_ol_dict[item.key] }} {{ lookup('template', item.key ) }}" 52 | with_dict: "{{ leaf_ol_dict }}" 53 | 54 | - name: Read template files - BGP related 55 | set_fact: 56 | overlay_block: "{{ overlay_block }} ! {{ common_ol_dict[item.key] }} {{ lookup('template', item.key ) }}" 57 | with_dict: "{{ common_ol_dict }}" 58 | 59 | - name: Render template files output 60 | set_fact: 61 | rendered: "{{ overlay_block | regex_replace('#jinja2: lstrip_blocks: \"True\", trim_blocks: \"True\"', '') | regex_replace('\\n(\\s+)','\n') | regex_replace('! ','\n! ') }}" 62 | 63 | - name: Copy configurations to respective file ( -overlay.txt ) under 'preview_files' folder 64 | copy: 65 | dest: preview_files/{{inventory_hostname}}-overlay.txt 66 | content: "{{ rendered }}" 67 | 68 | - name: Display output file info 69 | hosts: localhost 70 | gather_facts: no 71 | run_once: True 72 | 73 | tasks: 74 | - debug: 75 | msg: Please refer to -overlay.txt in 'preview_files' folder 76 | -------------------------------------------------------------------------------- /dag/playbook_overlay_precheck.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: check for underlay loopback ip reachability 4 | hosts: leaf 5 | gather_facts: no 6 | connection: local 7 | tasks: 8 | # Collecting the show_version data 9 | - name: Collect "show version" 10 | cli_command: 11 | command: show version 12 | register: leaf_command_output 13 | 14 | # Getting the loopback from "group_vars/overlay_db.yml under nve_interfaces" 15 | - name: run the new module for version 16 | device_reachability: 17 | version: "{{ leaf_command_output.stdout_lines }}" 18 | fileName: "group_vars/overlay_db.yml" 19 | register: result 20 | 21 | # collecting the "show run interface loopback" output 22 | - name: Collect "show run interface loopback" 23 | cli_command: 24 | command: show run interface {{result['yaml_loopback_check']}} 25 | register: command_output 26 | 27 | # Get the loopback ip from "show run interface loopback output from the leafs" 28 | - name: get the loopback ip 29 | set_fact: loopback_ip="{{command_output.stdout | regex_findall('ip\s+address\s+((?:[0-9]{1,3}\.){3}[0-9]{1,3})',multiline=True)}}" 30 | 31 | # writing the loopback ip's results to output/loopback.txt 32 | - name: Write results to local output file 33 | copy: 34 | dest: output/loopback.txt 35 | content: | 36 | {% for host in groups['leaf'] %} 37 | {{hostvars[host].loopback_ip}} 38 | {% endfor %} 39 | 40 | # save the loopback ip to item_output 41 | - name: save the loopback ip output to file 42 | debug: var=item 43 | with_file: 44 | - "output/loopback.txt" 45 | register: item_output 46 | 47 | # Getting the loopback ip's from the output/loopback.txt file 48 | - name: getting the loopback ip's from the loopback.txt file 49 | device_reachability: 50 | version: "{{ leaf_command_output.stdout_lines }}" 51 | fileName: "group_vars/overlay_db.yml" 52 | loopback: "{{item_output}}" 53 | register: result 54 | 55 | # execute the ping command 56 | - name: execute ping command 57 | cli_command: 58 | command: ping {{item}} 59 | loop: "{{result['loopback_ip']}}" 60 | register: command_output_loopback 61 | 62 | # run the module with version,filename,loopback,ping_output and hostname 63 | - name: run the new module for final_result 64 | device_reachability: 65 | version: "{{ leaf_command_output.stdout_lines }}" 66 | fileName: "group_vars/overlay_db.yml" 67 | loopback: "{{item_output}}" 68 | ping_output: "{{ command_output_loopback}}" 69 | host_name: "{{ inventory_hostname }}" 70 | register: final_result 71 | 72 | # printing the final result 73 | - name: Print result 74 | debug: 75 | msg: "{{ final_result}}'" 76 | -------------------------------------------------------------------------------- /l2vni/playbook_overlay_precheck.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: check for underlay loopback ip reachability 4 | hosts: leaf 5 | gather_facts: no 6 | connection: local 7 | tasks: 8 | # Collecting the show_version data 9 | - name: Collect "show version" 10 | cli_command: 11 | command: show version 12 | register: leaf_command_output 13 | 14 | # Getting the loopback from "group_vars/overlay_db.yml under nve_interfaces" 15 | - name: run the new module for version 16 | device_reachability: 17 | version: "{{ leaf_command_output.stdout_lines }}" 18 | fileName: "group_vars/overlay_db.yml" 19 | register: result 20 | 21 | # collecting the "show run interface loopback" output 22 | - name: Collect "show run interface loopback" 23 | cli_command: 24 | command: show run interface {{result['yaml_loopback_check']}} 25 | register: command_output 26 | 27 | # Get the loopback ip from "show run interface loopback output from the leafs" 28 | - name: get the loopback ip 29 | set_fact: loopback_ip="{{command_output.stdout | regex_findall('ip\s+address\s+((?:[0-9]{1,3}\.){3}[0-9]{1,3})',multiline=True)}}" 30 | 31 | # writing the loopback ip's results to output/loopback.txt 32 | - name: Write results to local output file 33 | copy: 34 | dest: output/loopback.txt 35 | content: | 36 | {% for host in groups['leaf'] %} 37 | {{hostvars[host].loopback_ip}} 38 | {% endfor %} 39 | 40 | # save the loopback ip to item_output 41 | - name: save the loopback ip output to file 42 | debug: var=item 43 | with_file: 44 | - "output/loopback.txt" 45 | register: item_output 46 | 47 | # Getting the loopback ip's from the output/loopback.txt file 48 | - name: getting the loopback ip's from the loopback.txt file 49 | device_reachability: 50 | version: "{{ leaf_command_output.stdout_lines }}" 51 | fileName: "group_vars/overlay_db.yml" 52 | loopback: "{{item_output}}" 53 | register: result 54 | 55 | # execute the ping command 56 | - name: execute ping command 57 | cli_command: 58 | command: ping {{item}} 59 | loop: "{{result['loopback_ip']}}" 60 | register: command_output_loopback 61 | 62 | # run the module with version,filename,loopback,ping_output and hostname 63 | - name: run the new module for final_result 64 | device_reachability: 65 | version: "{{ leaf_command_output.stdout_lines }}" 66 | fileName: "group_vars/overlay_db.yml" 67 | loopback: "{{item_output}}" 68 | ping_output: "{{ command_output_loopback}}" 69 | host_name: "{{ inventory_hostname }}" 70 | register: final_result 71 | 72 | # printing the final result 73 | - name: Print result 74 | debug: 75 | msg: "{{ final_result}}'" 76 | -------------------------------------------------------------------------------- /l3vni/playbook_overlay_precheck.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: check for underlay loopback ip reachability 4 | hosts: leaf 5 | gather_facts: no 6 | connection: local 7 | tasks: 8 | # Collecting the show_version data 9 | - name: Collect "show version" 10 | cli_command: 11 | command: show version 12 | register: leaf_command_output 13 | 14 | # Getting the loopback from "group_vars/overlay_db.yml under nve_interfaces" 15 | - name: run the new module for version 16 | device_reachability: 17 | version: "{{ leaf_command_output.stdout_lines }}" 18 | fileName: "group_vars/overlay_db.yml" 19 | register: result 20 | 21 | # collecting the "show run interface loopback" output 22 | - name: Collect "show run interface loopback" 23 | cli_command: 24 | command: show run interface {{result['yaml_loopback_check']}} 25 | register: command_output 26 | 27 | # Get the loopback ip from "show run interface loopback output from the leafs" 28 | - name: get the loopback ip 29 | set_fact: loopback_ip="{{command_output.stdout | regex_findall('ip\s+address\s+((?:[0-9]{1,3}\.){3}[0-9]{1,3})',multiline=True)}}" 30 | 31 | # writing the loopback ip's results to output/loopback.txt 32 | - name: Write results to local output file 33 | copy: 34 | dest: output/loopback.txt 35 | content: | 36 | {% for host in groups['leaf'] %} 37 | {{hostvars[host].loopback_ip}} 38 | {% endfor %} 39 | 40 | # save the loopback ip to item_output 41 | - name: save the loopback ip output to file 42 | debug: var=item 43 | with_file: 44 | - "output/loopback.txt" 45 | register: item_output 46 | 47 | # Getting the loopback ip's from the output/loopback.txt file 48 | - name: getting the loopback ip's from the loopback.txt file 49 | device_reachability: 50 | version: "{{ leaf_command_output.stdout_lines }}" 51 | fileName: "group_vars/overlay_db.yml" 52 | loopback: "{{item_output}}" 53 | register: result 54 | 55 | # execute the ping command 56 | - name: execute ping command 57 | cli_command: 58 | command: ping {{item}} 59 | loop: "{{result['loopback_ip']}}" 60 | register: command_output_loopback 61 | 62 | # run the module with version,filename,loopback,ping_output and hostname 63 | - name: run the new module for final_result 64 | device_reachability: 65 | version: "{{ leaf_command_output.stdout_lines }}" 66 | fileName: "group_vars/overlay_db.yml" 67 | loopback: "{{item_output}}" 68 | ping_output: "{{ command_output_loopback}}" 69 | host_name: "{{ inventory_hostname }}" 70 | register: final_result 71 | 72 | # printing the final result 73 | - name: Print result 74 | debug: 75 | msg: "{{ final_result}}'" 76 | --------------------------------------------------------------------------------