diff --git a/.github/generate_release.py b/.github/generate_release.py index 201ebc73290..47ad4cd1556 100644 --- a/.github/generate_release.py +++ b/.github/generate_release.py @@ -14,6 +14,7 @@ SCOPES = [ "build_output_folders", "cvp_configlet_upload", + "cloudvision", "dhcp_provisioner", "eos_cli_config_gen", "eos_config_deploy_cvp", diff --git a/.github/workflows/pull-request-management.yml b/.github/workflows/pull-request-management.yml index 56638d3c146..306f5848985 100644 --- a/.github/workflows/pull-request-management.yml +++ b/.github/workflows/pull-request-management.yml @@ -49,6 +49,10 @@ jobs: - 'ansible_collections/arista/avd/molecule/cvp_configlet_upload/*' - 'ansible_collections/arista/avd/molecule/cvp_configlet_upload/**/*' - '.github/workflows/pull-request-management.yml' + - 'ansible_collections/arista/avd/roles/cloudvision/*' + - 'ansible_collections/arista/avd/roles/cloudvision/**/*' + - 'ansible_collections/arista/avd/molecule/cloudvision/*' + - 'ansible_collections/arista/avd/molecule/cloudvision/**/*' dhcp: - 'ansible_collections/arista/avd/roles/dhcp_provisioner/*' - 'ansible_collections/arista/avd/roles/dhcp_provisioner/**/*' @@ -305,6 +309,42 @@ jobs: galaxy_server: 'https://old-galaxy.ansible.com' needs: [ pre_commit ] if: needs.file-changes.outputs.cloudvision == 'true' + steps: + - name: 'Set environment variables' + run: | + echo "PY_COLORS=1" >> $GITHUB_ENV + echo "ANSIBLE_FORCE_COLOR=1" >> $GITHUB_ENV + - name: 'Set galaxy server' + if: ${{ matrix.galaxy_server }} + run: | + echo "ANSIBLE_GALAXY_SERVER=${{ matrix.galaxy_server }}" >> $GITHUB_ENV + - uses: actions/checkout@v3 + - name: Run molecule action + uses: arista-netdevops-community/action-molecule-avd@v1.6 + with: + molecule_parentdir: 'ansible_collections/arista/avd' + molecule_command: 'test' + molecule_args: '--scenario-name ${{ matrix.avd_scenario }}' + pip_file: ansible_collections/arista/avd/requirements.txt + galaxy_file: "ansible_collections/arista/avd/collections.yml" + ansible: ${{ matrix.ansible_version }} + check_git: true + check_git_enforced: true + + # ----------------------------------- # + # CloudVision TAGS MOLECULE + # ----------------------------------- # + molecule_cloudvision_tags: + name: Validate CloudVision Role for Tags + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + avd_scenario: [ 'cloudvision_tags' ] + ansible_version: ['ansible-core<2.16.0 --upgrade'] + galaxy_server: ['https://old-galaxy.ansible.com'] + needs: [ pre_commit ] + if: needs.file-changes.outputs.eos_design == 'true' || needs.file-changes.outputs.cloudvision == 'true' steps: - name: 'Set environment variables' run: | diff --git a/.github/workflows/pull-request-triage.yml b/.github/workflows/pull-request-triage.yml index e22bb6ec6dc..fa3919ce5ec 100644 --- a/.github/workflows/pull-request-triage.yml +++ b/.github/workflows/pull-request-triage.yml @@ -60,6 +60,7 @@ jobs: scopes: | build_output_folders cvp_configlet_upload + cloudvision dhcp_provisioner eos_cli_config_gen eos_config_deploy_cvp diff --git a/ansible_collections/arista/avd/README.md b/ansible_collections/arista/avd/README.md index 7fc3172b57f..b394e5d6261 100644 --- a/ansible_collections/arista/avd/README.md +++ b/ansible_collections/arista/avd/README.md @@ -38,6 +38,7 @@ This repository provides content for Arista's **arista.avd** collection. The fol - [**arista.avd.eos_config_deploy_cvp**](roles/eos_config_deploy_cvp/README.md) - Deploys intended configuration via CloudVision. - [**arista.avd.eos_config_deploy_eapi**](roles/eos_config_deploy_eapi/README.md) - Deploys intended configuration via eAPI. - [**arista.avd.cvp_configlet_upload**](roles/cvp_configlet_upload/README.md) - Uploads configlets from a local folder to CloudVision Server. +- [**arista.avd.cloudvision**](roles/cloudvision/README.md) - Applies tag changes to CloudVision. - [**arista.avd.eos_validate_state**](roles/eos_validate_state/README.md) - Validate operational states of Arista EOS devices. - [**arista.avd.eos_snapshot**](roles/eos_snapshot/README.md) - Collect commands on EOS devices and generate reports. - [**arista.avd.dhcp_provisioner**](roles/dhcp_provisioner/README.md) - Configure an ISC-DHCP server to provide ZTP services and CloudVision registration. diff --git a/ansible_collections/arista/avd/molecule/MOLECULE_SCENARIOS.txt b/ansible_collections/arista/avd/molecule/MOLECULE_SCENARIOS.txt index 30fab04e270..74311b916af 100644 --- a/ansible_collections/arista/avd/molecule/MOLECULE_SCENARIOS.txt +++ b/ansible_collections/arista/avd/molecule/MOLECULE_SCENARIOS.txt @@ -1,3 +1,4 @@ +cloudvision_tags eos_cli_config_gen eos_cli_config_gen_deprecated_vars eos_cli_config_gen_negative_unit_tests diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/converge.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/converge.yml new file mode 100644 index 00000000000..bb18c1cb7de --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/converge.yml @@ -0,0 +1,20 @@ +--- +- name: Converge + hosts: FABRIC + gather_facts: false + connection: local + vars: + root_dir: '{{ playbook_dir }}' + cvp_address: "{{ lookup('ansible.builtin.env', 'CVP_ADDRESS', default='None') }}" + tasks: + - name: Generate intended variables + delegate_to: 127.0.0.1 + ansible.builtin.import_role: + name: arista.avd.eos_designs + + - name: Apply tags in CloudVision + ansible.builtin.import_role: + name: arista.avd.cloudvision + delegate_to: cv_server + when: + - cvp_address != "None" diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/create.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/create.yml new file mode 100644 index 00000000000..e9ee34d3138 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/create.yml @@ -0,0 +1,13 @@ +--- +- name: Configure local folders + hosts: all + gather_facts: false + connection: local + vars: + root_dir: '{{ playbook_dir }}' + tasks: + - name: Create local output folders + delegate_to: 127.0.0.1 + ansible.builtin.import_role: + name: arista.avd.build_output_folders + run_once: true diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/destroy.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/destroy.yml new file mode 100644 index 00000000000..a10a3af0c1c --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/destroy.yml @@ -0,0 +1,17 @@ +--- +- name: Remove output folders + hosts: all + gather_facts: false + connection: local + vars: + root_dir: '{{ playbook_dir }}' + tasks: + - name: Delete local folders + delegate_to: 127.0.0.1 + run_once: true + ansible.builtin.file: + path: "{{ root_dir }}/{{ item }}" + state: absent + with_items: + - config_backup + - reports diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/cvp_tags.md b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/cvp_tags.md new file mode 100644 index 00000000000..2ba8d75bb59 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/cvp_tags.md @@ -0,0 +1,311 @@ +# CloudVision Portal Tags + +## Table of Contents + +- [CVP Tags for dc1-super-spine1](#cvp-tags-for-dc1-super-spine1) +- [CVP Tags for dc1-super-spine2](#cvp-tags-for-dc1-super-spine2) +- [CVP Tags for dc1-pod1-spine1](#cvp-tags-for-dc1-pod1-spine1) +- [CVP Tags for dc1-pod1-spine2](#cvp-tags-for-dc1-pod1-spine2) +- [CVP Tags for dc1-pod1-leaf1a](#cvp-tags-for-dc1-pod1-leaf1a) +- [CVP Tags for dc1-pod1-leaf1b](#cvp-tags-for-dc1-pod1-leaf1b) +- [CVP Tags for dc1-pod1-leaf2a](#cvp-tags-for-dc1-pod1-leaf2a) +- [CVP Tags for dc1-pod1-leaf2b](#cvp-tags-for-dc1-pod1-leaf2b) +- [CVP Tags for dc1-pod1-leaf1c](#cvp-tags-for-dc1-pod1-leaf1c) +- [CVP Tags for dc1-pod1-leaf2c](#cvp-tags-for-dc1-pod1-leaf2c) +- [CVP Tags for dc1-pod2-spine1](#cvp-tags-for-dc1-pod2-spine1) +- [CVP Tags for dc1-pod2-spine2](#cvp-tags-for-dc1-pod2-spine2) +- [CVP Tags for dc1-pod2-leaf1a](#cvp-tags-for-dc1-pod2-leaf1a) +- [CVP Tags for dc1-pod2-leaf1b](#cvp-tags-for-dc1-pod2-leaf1b) + +## CVP Tags for dc1-super-spine1 + +### dc1-super-spine1 Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | core | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | superspines | +| topology_hint_datacenter | DC1 | + +## CVP Tags for dc1-super-spine2 + +### dc1-super-spine2 Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | core | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | superspines | +| topology_hint_datacenter | DC1 | + +## CVP Tags for dc1-pod1-spine1 + +### dc1-pod1-spine1 Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | spine | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_datacenter | DC1 | + +### dc1-pod1-spine1 Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-leaf1a | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF1A_Ethernet1 | +| Ethernet1 | peer_int | Ethernet1 | +| Ethernet1 | interface_peer | dc1-pod2-leaf1a | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD2-LEAF1A_Ethernet1 | +| Ethernet1 | peer_int | Ethernet1 | +| Ethernet2 | interface_peer | dc1-pod1-leaf1b | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF1B_Ethernet1 | +| Ethernet2 | peer_int | Ethernet1 | +| Ethernet2 | interface_peer | dc1-pod2-leaf1b | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD2-LEAF1B_Ethernet1 | +| Ethernet2 | peer_int | Ethernet1 | +| Ethernet3 | interface_peer | dc1-pod1-leaf2a | +| Ethernet3 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF2A_Ethernet1 | +| Ethernet3 | peer_int | Ethernet1 | +| Ethernet4 | interface_peer | dc1-pod1-leaf2b | +| Ethernet4 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF2B_Ethernet1 | +| Ethernet4 | peer_int | Ethernet1 | + +## CVP Tags for dc1-pod1-spine2 + +### dc1-pod1-spine2 Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | spine | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_datacenter | DC1 | + +### dc1-pod1-spine2 Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-leaf1a | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF1A_Ethernet2 | +| Ethernet1 | peer_int | Ethernet2 | +| Ethernet1 | interface_peer | dc1-pod2-leaf1a | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD2-LEAF1A_Ethernet2 | +| Ethernet1 | peer_int | Ethernet2 | +| Ethernet2 | interface_peer | dc1-pod1-leaf1b | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF1B_Ethernet2 | +| Ethernet2 | peer_int | Ethernet2 | +| Ethernet2 | interface_peer | dc1-pod2-leaf1b | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD2-LEAF1B_Ethernet2 | +| Ethernet2 | peer_int | Ethernet2 | +| Ethernet3 | interface_peer | dc1-pod1-leaf2a | +| Ethernet3 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF2A_Ethernet2 | +| Ethernet3 | peer_int | Ethernet2 | +| Ethernet4 | interface_peer | dc1-pod1-leaf2b | +| Ethernet4 | interface_desc | P2P_LINK_TO_DC1-POD1-LEAF2B_Ethernet2 | +| Ethernet4 | peer_int | Ethernet2 | + +## CVP Tags for dc1-pod1-leaf1a + +### dc1-pod1-leaf1a Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_datacenter | DC1 | +| custom_tag | custom_value | + +### dc1-pod1-leaf1a Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-spine1 | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet1 | +| Ethernet1 | peer_int | Ethernet1 | +| Ethernet2 | interface_peer | dc1-pod1-spine2 | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE2_Ethernet1 | +| Ethernet2 | peer_int | Ethernet1 | +| Ethernet8 | interface_peer | dc1-pod1-leaf1c | +| Ethernet8 | interface_desc | DC1-POD1-LEAF1C_Ethernet1 | +| Ethernet8 | peer_int | Ethernet1 | + +## CVP Tags for dc1-pod1-leaf1b + +### dc1-pod1-leaf1b Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_datacenter | DC1 | + +### dc1-pod1-leaf1b Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-spine1 | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet2 | +| Ethernet1 | peer_int | Ethernet2 | +| Ethernet2 | interface_peer | dc1-pod1-spine2 | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE2_Ethernet2 | +| Ethernet2 | peer_int | Ethernet2 | + +## CVP Tags for dc1-pod1-leaf2a + +### dc1-pod1-leaf2a Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_datacenter | DC1 | + +### dc1-pod1-leaf2a Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-spine1 | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet3 | +| Ethernet1 | peer_int | Ethernet3 | +| Ethernet2 | interface_peer | dc1-pod1-spine2 | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE2_Ethernet3 | +| Ethernet2 | peer_int | Ethernet3 | +| Ethernet8 | interface_peer | dc1-pod1-leaf2c | +| Ethernet8 | interface_desc | DC1-POD1-LEAF2C_Ethernet1 | +| Ethernet8 | peer_int | Ethernet1 | + +## CVP Tags for dc1-pod1-leaf2b + +### dc1-pod1-leaf2b Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_datacenter | DC1 | + +### dc1-pod1-leaf2b Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-spine1 | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet4 | +| Ethernet1 | peer_int | Ethernet4 | +| Ethernet2 | interface_peer | dc1-pod1-spine2 | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE2_Ethernet4 | +| Ethernet2 | peer_int | Ethernet4 | + +## CVP Tags for dc1-pod1-leaf1c + +### dc1-pod1-leaf1c Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_rack | rack-1c | +| topology_hint_datacenter | DC1 | +| custom_tag | custom_value | + +### dc1-pod1-leaf1c Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-leaf1a | +| Ethernet1 | interface_desc | DC1-POD1-LEAF1A_Ethernet8 | +| Ethernet1 | peer_int | Ethernet8 | + +## CVP Tags for dc1-pod1-leaf2c + +### dc1-pod1-leaf2c Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | edge | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod1 | +| topology_hint_rack | rack-2c | +| topology_hint_datacenter | DC1 | + +### dc1-pod1-leaf2c Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-leaf2a | +| Ethernet1 | interface_desc | DC1-POD1-LEAF2A_Ethernet8 | +| Ethernet1 | peer_int | Ethernet8 | + +## CVP Tags for dc1-pod2-spine1 + +### dc1-pod2-spine1 Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | spine | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod2 | +| topology_hint_datacenter | DC1 | + +## CVP Tags for dc1-pod2-spine2 + +### dc1-pod2-spine2 Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | spine | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod2 | +| topology_hint_datacenter | DC1 | + +## CVP Tags for dc1-pod2-leaf1a + +### dc1-pod2-leaf1a Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod2 | +| topology_hint_datacenter | DC1 | +| custom_tag | custom_value | + +### dc1-pod2-leaf1a Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-spine1 | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet1 | +| Ethernet1 | peer_int | Ethernet1 | +| Ethernet2 | interface_peer | dc1-pod1-spine2 | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE2_Ethernet1 | +| Ethernet2 | peer_int | Ethernet1 | + +## CVP Tags for dc1-pod2-leaf1b + +### dc1-pod2-leaf1b Device Tags + +| Tag Name | Tag Value | +| -------- | --------- | +| topology_hint_type | leaf | +| topology_hint_fabric | FABRIC | +| topology_hint_pod | pod2 | +| topology_hint_datacenter | DC1 | + +### dc1-pod2-leaf1b Interface Tags + +| Interface | Tag Name | Tag Value | +| --------- |-------- | --------- | +| Ethernet1 | interface_peer | dc1-pod1-spine1 | +| Ethernet1 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet2 | +| Ethernet1 | peer_int | Ethernet2 | +| Ethernet2 | interface_peer | dc1-pod1-spine2 | +| Ethernet2 | interface_desc | P2P_LINK_TO_DC1-POD1-SPINE2_Ethernet2 | +| Ethernet2 | peer_int | Ethernet2 | diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-documentation.md b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-documentation.md new file mode 100644 index 00000000000..687a82f2f14 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-documentation.md @@ -0,0 +1,96 @@ +# FABRIC + +## Table of Contents + +- [Fabric Switches and Management IP](#fabric-switches-and-management-ip) + - [Fabric Switches with inband Management IP](#fabric-switches-with-inband-management-ip) +- [Fabric Topology](#fabric-topology) +- [Fabric IP Allocation](#fabric-ip-allocation) + - [Fabric Point-To-Point Links](#fabric-point-to-point-links) + - [Point-To-Point Links Node Allocation](#point-to-point-links-node-allocation) + - [Loopback Interfaces (BGP EVPN Peering)](#loopback-interfaces-bgp-evpn-peering) + - [Loopback0 Interfaces Node Allocation](#loopback0-interfaces-node-allocation) + - [VTEP Loopback VXLAN Tunnel Source Interfaces (VTEPs Only)](#vtep-loopback-vxlan-tunnel-source-interfaces-vteps-only) + - [VTEP Loopback Node allocation](#vtep-loopback-node-allocation) + +## Fabric Switches and Management IP + +| POD | Type | Node | Management IP | Platform | Provisioned in CloudVision | Serial Number | +| --- | ---- | ---- | ------------- | -------- | -------------------------- | ------------- | +| pod1 | l3leaf | dc1-pod1-leaf1a | 172.16.1.101/24 | vEOS-lab | Provisioned | - | +| pod1 | l3leaf | dc1-pod1-leaf1b | 172.16.1.102/24 | vEOS-lab | Provisioned | - | +| pod1 | l2leaf | dc1-pod1-leaf1c | 172.16.1.151/24 | vEOS-lab | Provisioned | - | +| pod1 | spine | dc1-pod1-spine1 | 172.16.1.11/24 | vEOS-lab | Provisioned | - | +| pod2 | l3leaf | dc1-pod2-leaf1a | 172.16.2.101/24 | vEOS-lab | Provisioned | - | +| pod2 | spine | dc1-pod2-spine1 | 172.16.2.11/24 | vEOS-lab | Provisioned | - | +| superspines | super-spine | dc1-super-spine1 | 172.16.1.1/24 | vEOS-lab | Provisioned | - | + +> Provision status is based on Ansible inventory declaration and do not represent real status from CloudVision. + +### Fabric Switches with inband Management IP + +| POD | Type | Node | Management IP | Inband Interface | +| --- | ---- | ---- | ------------- | ---------------- | + +## Fabric Topology + +| Type | Node | Node Interface | Peer Type | Peer Node | Peer Interface | +| ---- | ---- | -------------- | --------- | ----------| -------------- | +| l3leaf | dc1-pod1-leaf1a | Ethernet1 | spine | dc1-pod1-spine1 | Ethernet1 | +| l3leaf | dc1-pod1-leaf1a | Ethernet3 | mlag_peer | dc1-pod1-leaf1b | Ethernet3 | +| l3leaf | dc1-pod1-leaf1a | Ethernet4 | mlag_peer | dc1-pod1-leaf1b | Ethernet4 | +| l3leaf | dc1-pod1-leaf1a | Ethernet8 | l2leaf | dc1-pod1-leaf1c | Ethernet1 | +| l3leaf | dc1-pod1-leaf1b | Ethernet1 | spine | dc1-pod1-spine1 | Ethernet2 | +| l3leaf | dc1-pod1-leaf1b | Ethernet8 | l2leaf | dc1-pod1-leaf1c | Ethernet2 | +| spine | dc1-pod1-spine1 | Ethernet10 | super-spine | dc1-super-spine1 | Ethernet1 | +| l3leaf | dc1-pod2-leaf1a | Ethernet1 | spine | dc1-pod2-spine1 | Ethernet1 | +| spine | dc1-pod2-spine1 | Ethernet10 | super-spine | dc1-super-spine1 | Ethernet4 | + +## Fabric IP Allocation + +### Fabric Point-To-Point Links + +| Uplink IPv4 Pool | Available Addresses | Assigned addresses | Assigned Address % | +| ---------------- | ------------------- | ------------------ | ------------------ | +| 10.255.255.0/26 | 64 | 10 | 15.63 % | + +### Point-To-Point Links Node Allocation + +| Node | Node Interface | Node IP Address | Peer Node | Peer Interface | Peer IP Address | +| ---- | -------------- | --------------- | --------- | -------------- | --------------- | +| dc1-pod1-leaf1a | Ethernet1 | 10.255.255.1/31 | dc1-pod1-spine1 | Ethernet1 | 10.255.255.0/31 | +| dc1-pod1-leaf1b | Ethernet1 | 10.255.255.3/31 | dc1-pod1-spine1 | Ethernet2 | 10.255.255.2/31 | +| dc1-pod1-spine1 | Ethernet10 | 10.255.255.1/31 | dc1-super-spine1 | Ethernet1 | 10.255.255.0/31 | +| dc1-pod2-leaf1a | Ethernet1 | 10.255.255.9/31 | dc1-pod2-spine1 | Ethernet1 | 10.255.255.8/31 | +| dc1-pod2-spine1 | Ethernet10 | 10.255.255.3/31 | dc1-super-spine1 | Ethernet4 | 10.255.255.2/31 | + +### Loopback Interfaces (BGP EVPN Peering) + +| Loopback Pool | Available Addresses | Assigned addresses | Assigned Address % | +| ------------- | ------------------- | ------------------ | ------------------ | +| 10.255.0.0/27 | 32 | 6 | 18.75 % | + +### Loopback0 Interfaces Node Allocation + +| POD | Node | Loopback0 | +| --- | ---- | --------- | +| pod1 | dc1-pod1-leaf1a | 10.255.0.3/32 | +| pod1 | dc1-pod1-leaf1b | 10.255.0.4/32 | +| pod1 | dc1-pod1-spine1 | 10.255.0.1/32 | +| pod2 | dc1-pod2-leaf1a | 10.255.0.7/32 | +| pod2 | dc1-pod2-spine1 | 10.255.0.2/32 | +| superspines | dc1-super-spine1 | 10.255.0.1/32 | + +### VTEP Loopback VXLAN Tunnel Source Interfaces (VTEPs Only) + +| VTEP Loopback Pool | Available Addresses | Assigned addresses | Assigned Address % | +| --------------------- | ------------------- | ------------------ | ------------------ | +| 10.255.1.0/27 | 32 | 3 | 9.38 % | + +### VTEP Loopback Node allocation + +| POD | Node | Loopback1 | +| --- | ---- | --------- | +| pod1 | dc1-pod1-leaf1a | 10.255.1.3/32 | +| pod1 | dc1-pod1-leaf1b | 10.255.1.3/32 | +| pod2 | dc1-pod2-leaf1a | 10.255.1.7/32 | diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-p2p-links.csv b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-p2p-links.csv new file mode 100644 index 00000000000..2df32616aa6 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-p2p-links.csv @@ -0,0 +1,6 @@ +Type,Node,Node Interface,Leaf IP Address,Peer Type,Peer Node,Peer Interface,Peer IP Address +l3leaf,dc1-pod1-leaf1a,Ethernet1,10.255.255.1/31,spine,dc1-pod1-spine1,Ethernet1,10.255.255.0/31 +l3leaf,dc1-pod1-leaf1b,Ethernet1,10.255.255.3/31,spine,dc1-pod1-spine1,Ethernet2,10.255.255.2/31 +spine,dc1-pod1-spine1,Ethernet10,10.255.255.1/31,super-spine,dc1-super-spine1,Ethernet1,10.255.255.0/31 +l3leaf,dc1-pod2-leaf1a,Ethernet1,10.255.255.9/31,spine,dc1-pod2-spine1,Ethernet1,10.255.255.8/31 +spine,dc1-pod2-spine1,Ethernet10,10.255.255.3/31,super-spine,dc1-super-spine1,Ethernet4,10.255.255.2/31 diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-topology.csv b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-topology.csv new file mode 100644 index 00000000000..431835b61b6 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/documentation/fabric/FABRIC-topology.csv @@ -0,0 +1,21 @@ +Node Type,Node,Node Interface,Peer Type,Peer Node,Peer Interface,Node Interface Enabled +l3leaf,dc1-pod1-leaf1a,Ethernet1,spine,dc1-pod1-spine1,Ethernet1,True +l3leaf,dc1-pod1-leaf1a,Ethernet3,mlag_peer,dc1-pod1-leaf1b,Ethernet3,True +l3leaf,dc1-pod1-leaf1a,Ethernet4,mlag_peer,dc1-pod1-leaf1b,Ethernet4,True +l3leaf,dc1-pod1-leaf1a,Ethernet5,server,dc1-pod1-leaf1-server1,PCI1,True +l3leaf,dc1-pod1-leaf1a,Ethernet8,l2leaf,dc1-pod1-leaf1c,Ethernet1,True +l3leaf,dc1-pod1-leaf1b,Ethernet1,spine,dc1-pod1-spine1,Ethernet2,True +l3leaf,dc1-pod1-leaf1b,Ethernet3,mlag_peer,dc1-pod1-leaf1a,Ethernet3,True +l3leaf,dc1-pod1-leaf1b,Ethernet4,mlag_peer,dc1-pod1-leaf1a,Ethernet4,True +l3leaf,dc1-pod1-leaf1b,Ethernet5,server,dc1-pod1-leaf1-server1,PCI2,True +l3leaf,dc1-pod1-leaf1b,Ethernet8,l2leaf,dc1-pod1-leaf1c,Ethernet2,True +l2leaf,dc1-pod1-leaf1c,Ethernet1,l3leaf,dc1-pod1-leaf1a,Ethernet8,True +l2leaf,dc1-pod1-leaf1c,Ethernet2,l3leaf,dc1-pod1-leaf1b,Ethernet8,True +spine,dc1-pod1-spine1,Ethernet1,l3leaf,dc1-pod1-leaf1a,Ethernet1,True +spine,dc1-pod1-spine1,Ethernet2,l3leaf,dc1-pod1-leaf1b,Ethernet1,True +spine,dc1-pod1-spine1,Ethernet10,super-spine,dc1-super-spine1,Ethernet1,True +l3leaf,dc1-pod2-leaf1a,Ethernet1,spine,dc1-pod2-spine1,Ethernet1,True +spine,dc1-pod2-spine1,Ethernet1,l3leaf,dc1-pod2-leaf1a,Ethernet1,True +spine,dc1-pod2-spine1,Ethernet10,super-spine,dc1-super-spine1,Ethernet4,True +super-spine,dc1-super-spine1,Ethernet1,spine,dc1-pod1-spine1,Ethernet10,True +super-spine,dc1-super-spine1,Ethernet4,spine,dc1-pod2-spine1,Ethernet10,True diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1a.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1a.yml new file mode 100644 index 00000000000..60bb19ed7fc --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1a.yml @@ -0,0 +1,278 @@ +hostname: dc1-pod1-leaf1a +is_deployed: true +router_bgp: + as: '65102' + router_id: 10.255.0.3 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + peer_groups: + - name: MLAG-IPv4-UNDERLAY-PEER + type: ipv4 + remote_as: '65102' + next_hop_self: true + description: dc1-pod1-leaf1b + maximum_routes: 12000 + send_community: all + route_map_in: RM-MLAG-PEER-IN + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + - name: EVPN-OVERLAY-PEERS + type: evpn + update_source: Loopback0 + bfd: true + send_community: all + maximum_routes: 0 + ebgp_multihop: 3 + address_family_ipv4: + peer_groups: + - name: MLAG-IPv4-UNDERLAY-PEER + activate: true + - name: IPv4-UNDERLAY-PEERS + activate: true + - name: EVPN-OVERLAY-PEERS + activate: false + neighbors: + - ip_address: 10.255.1.97 + peer_group: MLAG-IPv4-UNDERLAY-PEER + description: dc1-pod1-leaf1b + - ip_address: 10.255.255.0 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65101' + description: dc1-pod1-spine1_Ethernet1 + - ip_address: 10.255.0.1 + peer_group: EVPN-OVERLAY-PEERS + description: dc1-pod1-spine1 + remote_as: '65101' + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + address_family_evpn: + peer_groups: + - name: EVPN-OVERLAY-PEERS + activate: true +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: mstp + mst_instances: + - id: '0' + priority: 4096 + no_spanning_tree_vlan: 4093-4094 +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.1.101/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +vlans: +- id: 4093 + tenant: system + name: LEAF_PEER_L3 + trunk_groups: + - LEAF_PEER_L3 +- id: 4094 + tenant: system + name: MLAG_PEER + trunk_groups: + - MLAG +vlan_interfaces: +- name: Vlan4093 + description: MLAG_PEER_L3_PEERING + shutdown: false + mtu: 9214 + ip_address: 10.255.1.96/31 +- name: Vlan4094 + description: MLAG_PEER + shutdown: false + ip_address: 10.255.1.64/31 + no_autostate: true + mtu: 9214 +port_channel_interfaces: +- name: Port-Channel3 + description: MLAG_PEER_dc1-pod1-leaf1b_Po3 + type: switched + shutdown: false + mode: trunk + trunk_groups: + - LEAF_PEER_L3 + - MLAG +- name: Port-Channel8 + description: DC1-POD1-LEAF1C_Po1 + type: switched + shutdown: false + mode: trunk + vlans: none + mlag: 8 +- name: Port-Channel5 + description: dc1-pod1-leaf1-server1_PortChannel dc1-pod1-leaf1-server1 + type: switched + shutdown: false + mode: trunk + vlans: 11-12,21-22 + native_vlan: 4092 + spanning_tree_portfast: edge + mlag: 5 +ethernet_interfaces: +- name: Ethernet3 + peer: dc1-pod1-leaf1b + peer_interface: Ethernet3 + peer_type: mlag_peer + description: MLAG_PEER_dc1-pod1-leaf1b_Ethernet3 + type: port-channel-member + shutdown: false + channel_group: + id: 3 + mode: active +- name: Ethernet4 + peer: dc1-pod1-leaf1b + peer_interface: Ethernet4 + peer_type: mlag_peer + description: MLAG_PEER_dc1-pod1-leaf1b_Ethernet4 + type: port-channel-member + shutdown: false + channel_group: + id: 3 + mode: active +- name: Ethernet1 + peer: dc1-pod1-spine1 + peer_interface: Ethernet1 + peer_type: spine + description: P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet1 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.1/31 +- name: Ethernet8 + peer: dc1-pod1-leaf1c + peer_interface: Ethernet1 + peer_type: l2leaf + description: DC1-POD1-LEAF1C_Ethernet1 + shutdown: false + type: port-channel-member + channel_group: + id: 8 + mode: active +- name: Ethernet5 + peer: dc1-pod1-leaf1-server1 + peer_interface: PCI1 + peer_type: server + description: dc1-pod1-leaf1-server1_PCI1 + shutdown: false + type: port-channel-member + channel_group: + id: 5 + mode: active +mlag_configuration: + domain_id: DC1_POD1_L3_LEAF1 + local_interface: Vlan4094 + peer_address: 10.255.1.65 + peer_link: Port-Channel3 + reload_delay_mlag: '300' + reload_delay_non_mlag: '330' +route_maps: +- name: RM-MLAG-PEER-IN + sequence_numbers: + - sequence: 10 + type: permit + set: + - origin incomplete + description: Make routes learned over MLAG Peer-link less preferred on spines to ensure optimal routing +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +loopback_interfaces: +- name: Loopback0 + description: EVPN_Overlay_Peering + shutdown: false + ip_address: 10.255.0.3/32 +- name: Loopback1 + description: VTEP_VXLAN_Tunnel_Source + shutdown: false + ip_address: 10.255.1.3/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.255.0.0/27 eq 32 + - sequence: 20 + action: permit 10.255.1.0/27 eq 32 +router_bfd: + multihop: + interval: 300 + min_rx: 300 + multiplier: 3 +ip_igmp_snooping: + globally_enabled: true +ip_virtual_router_mac_address: 00:1c:73:00:00:99 +vxlan_interface: + Vxlan1: + description: dc1-pod1-leaf1a_VTEP + vxlan: + udp_port: 4789 + source_interface: Loopback1 + virtual_router_encapsulation_mac_address: mlag-system-id +cv_tags: + device_tags: + - name: topology_hint_datacenter + value: DC1 + - name: topology_hint_fabric + value: FABRIC + - name: topology_hint_pod + value: pod1 + - name: topology_hint_type + value: leaf + - name: custom_tag + value: custom_value_leaf1a + interface_tags: + - interface: Ethernet3 + tags: + - name: peer_device_interface + value: Ethernet3 + - interface: Ethernet4 + tags: + - name: peer_device_interface + value: Ethernet4 + - interface: Ethernet1 + tags: + - name: peer_device_interface + value: Ethernet1 + - interface: Ethernet8 + tags: + - name: peer_device_interface + value: Ethernet1 + - interface: Ethernet5 + tags: + - name: peer_device_interface + value: PCI1 diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1b.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1b.yml new file mode 100644 index 00000000000..8e2b6a22236 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1b.yml @@ -0,0 +1,245 @@ +hostname: dc1-pod1-leaf1b +is_deployed: true +router_bgp: + as: '65102' + router_id: 10.255.0.4 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + peer_groups: + - name: MLAG-IPv4-UNDERLAY-PEER + type: ipv4 + remote_as: '65102' + next_hop_self: true + description: dc1-pod1-leaf1a + maximum_routes: 12000 + send_community: all + route_map_in: RM-MLAG-PEER-IN + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + - name: EVPN-OVERLAY-PEERS + type: evpn + update_source: Loopback0 + bfd: true + send_community: all + maximum_routes: 0 + ebgp_multihop: 3 + address_family_ipv4: + peer_groups: + - name: MLAG-IPv4-UNDERLAY-PEER + activate: true + - name: IPv4-UNDERLAY-PEERS + activate: true + - name: EVPN-OVERLAY-PEERS + activate: false + neighbors: + - ip_address: 10.255.1.96 + peer_group: MLAG-IPv4-UNDERLAY-PEER + description: dc1-pod1-leaf1a + - ip_address: 10.255.255.2 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65101' + description: dc1-pod1-spine1_Ethernet2 + - ip_address: 10.255.0.1 + peer_group: EVPN-OVERLAY-PEERS + description: dc1-pod1-spine1 + remote_as: '65101' + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + address_family_evpn: + peer_groups: + - name: EVPN-OVERLAY-PEERS + activate: true +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: mstp + mst_instances: + - id: '0' + priority: 4096 + no_spanning_tree_vlan: 4093-4094 +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.1.102/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +vlans: +- id: 4093 + tenant: system + name: LEAF_PEER_L3 + trunk_groups: + - LEAF_PEER_L3 +- id: 4094 + tenant: system + name: MLAG_PEER + trunk_groups: + - MLAG +vlan_interfaces: +- name: Vlan4093 + description: MLAG_PEER_L3_PEERING + shutdown: false + mtu: 9214 + ip_address: 10.255.1.97/31 +- name: Vlan4094 + description: MLAG_PEER + shutdown: false + ip_address: 10.255.1.65/31 + no_autostate: true + mtu: 9214 +port_channel_interfaces: +- name: Port-Channel3 + description: MLAG_PEER_dc1-pod1-leaf1a_Po3 + type: switched + shutdown: false + mode: trunk + trunk_groups: + - LEAF_PEER_L3 + - MLAG +- name: Port-Channel8 + description: DC1-POD1-LEAF1C_Po1 + type: switched + shutdown: false + mode: trunk + vlans: none + mlag: 8 +- name: Port-Channel5 + description: dc1-pod1-leaf1-server1_PortChannel dc1-pod1-leaf1-server1 + type: switched + shutdown: false + mode: trunk + vlans: 11-12,21-22 + native_vlan: 4092 + spanning_tree_portfast: edge + mlag: 5 +ethernet_interfaces: +- name: Ethernet3 + peer: dc1-pod1-leaf1a + peer_interface: Ethernet3 + peer_type: mlag_peer + description: MLAG_PEER_dc1-pod1-leaf1a_Ethernet3 + type: port-channel-member + shutdown: false + channel_group: + id: 3 + mode: active +- name: Ethernet4 + peer: dc1-pod1-leaf1a + peer_interface: Ethernet4 + peer_type: mlag_peer + description: MLAG_PEER_dc1-pod1-leaf1a_Ethernet4 + type: port-channel-member + shutdown: false + channel_group: + id: 3 + mode: active +- name: Ethernet1 + peer: dc1-pod1-spine1 + peer_interface: Ethernet2 + peer_type: spine + description: P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet2 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.3/31 +- name: Ethernet8 + peer: dc1-pod1-leaf1c + peer_interface: Ethernet2 + peer_type: l2leaf + description: DC1-POD1-LEAF1C_Ethernet2 + shutdown: false + type: port-channel-member + channel_group: + id: 8 + mode: active +- name: Ethernet5 + peer: dc1-pod1-leaf1-server1 + peer_interface: PCI2 + peer_type: server + description: dc1-pod1-leaf1-server1_PCI2 + shutdown: false + type: port-channel-member + channel_group: + id: 5 + mode: active +mlag_configuration: + domain_id: DC1_POD1_L3_LEAF1 + local_interface: Vlan4094 + peer_address: 10.255.1.64 + peer_link: Port-Channel3 + reload_delay_mlag: '300' + reload_delay_non_mlag: '330' +route_maps: +- name: RM-MLAG-PEER-IN + sequence_numbers: + - sequence: 10 + type: permit + set: + - origin incomplete + description: Make routes learned over MLAG Peer-link less preferred on spines to ensure optimal routing +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +loopback_interfaces: +- name: Loopback0 + description: EVPN_Overlay_Peering + shutdown: false + ip_address: 10.255.0.4/32 +- name: Loopback1 + description: VTEP_VXLAN_Tunnel_Source + shutdown: false + ip_address: 10.255.1.3/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.255.0.0/27 eq 32 + - sequence: 20 + action: permit 10.255.1.0/27 eq 32 +router_bfd: + multihop: + interval: 300 + min_rx: 300 + multiplier: 3 +ip_igmp_snooping: + globally_enabled: true +ip_virtual_router_mac_address: 00:1c:73:00:00:99 +vxlan_interface: + Vxlan1: + description: dc1-pod1-leaf1b_VTEP + vxlan: + udp_port: 4789 + source_interface: Loopback1 + virtual_router_encapsulation_mac_address: mlag-system-id diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1c.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1c.yml new file mode 100644 index 00000000000..6421ad09d11 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-leaf1c.yml @@ -0,0 +1,79 @@ +hostname: dc1-pod1-leaf1c +is_deployed: true +service_routing_protocols_model: multi-agent +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: mstp + mst_instances: + - id: '0' + priority: 32768 +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.1.151/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +ethernet_interfaces: +- name: Ethernet1 + peer: dc1-pod1-leaf1a + peer_interface: Ethernet8 + peer_type: l3leaf + description: DC1-POD1-LEAF1A_Ethernet8 + shutdown: false + type: port-channel-member + channel_group: + id: 1 + mode: active +- name: Ethernet2 + peer: dc1-pod1-leaf1b + peer_interface: Ethernet8 + peer_type: l3leaf + description: DC1-POD1-LEAF1B_Ethernet8 + shutdown: false + type: port-channel-member + channel_group: + id: 1 + mode: active +port_channel_interfaces: +- name: Port-Channel1 + description: DC1_POD1_L3_LEAF1_Po8 + type: switched + shutdown: false + mode: trunk + vlans: none +ip_igmp_snooping: + globally_enabled: true +cv_tags: + device_tags: + - name: topology_hint_datacenter + value: DC1 + - name: topology_hint_fabric + value: FABRIC + - name: topology_hint_pod + value: pod1 + - name: topology_hint_type + value: edge + - name: topology_hint_rack + value: rack-1c diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-spine1.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-spine1.yml new file mode 100644 index 00000000000..ff2c32c0525 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod1-spine1.yml @@ -0,0 +1,155 @@ +hostname: dc1-pod1-spine1 +is_deployed: true +router_bgp: + as: '65101' + router_id: 10.255.0.1 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + peer_groups: + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + - name: EVPN-OVERLAY-PEERS + type: evpn + update_source: Loopback0 + bfd: true + send_community: all + maximum_routes: 0 + ebgp_multihop: 3 + next_hop_unchanged: true + address_family_ipv4: + peer_groups: + - name: IPv4-UNDERLAY-PEERS + activate: true + - name: EVPN-OVERLAY-PEERS + activate: false + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + neighbors: + - ip_address: 10.255.255.1 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65102' + description: dc1-pod1-leaf1a_Ethernet1 + - ip_address: 10.255.255.3 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65102' + description: dc1-pod1-leaf1b_Ethernet1 + - ip_address: 10.255.255.0 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65100' + description: dc1-super-spine1_Ethernet1 + - ip_address: 10.255.0.3 + peer_group: EVPN-OVERLAY-PEERS + description: dc1-pod1-leaf1a + remote_as: '65102' + - ip_address: 10.255.0.4 + peer_group: EVPN-OVERLAY-PEERS + description: dc1-pod1-leaf1b + remote_as: '65102' + address_family_evpn: + peer_groups: + - name: EVPN-OVERLAY-PEERS + activate: true +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: none +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.1.11/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +ethernet_interfaces: +- name: Ethernet1 + peer: dc1-pod1-leaf1a + peer_interface: Ethernet1 + peer_type: l3leaf + description: P2P_LINK_TO_DC1-POD1-LEAF1A_Ethernet1 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.0/31 +- name: Ethernet2 + peer: dc1-pod1-leaf1b + peer_interface: Ethernet1 + peer_type: l3leaf + description: P2P_LINK_TO_DC1-POD1-LEAF1B_Ethernet1 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.2/31 +- name: Ethernet10 + peer: dc1-super-spine1 + peer_interface: Ethernet1 + peer_type: super-spine + description: P2P_LINK_TO_DC1-SUPER-SPINE1_Ethernet1 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.1/31 +loopback_interfaces: +- name: Loopback0 + description: EVPN_Overlay_Peering + shutdown: false + ip_address: 10.255.0.1/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.255.0.0/27 eq 32 +route_maps: +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +router_bfd: + multihop: + interval: 300 + min_rx: 300 + multiplier: 3 +cv_tags: + device_tags: + - name: topology_hint_datacenter + value: DC1 + - name: topology_hint_fabric + value: FABRIC + - name: topology_hint_pod + value: pod1 + - name: topology_hint_type + value: spine + - name: topology_hint_rack + value: RackB + - name: layer3_routing + value: 'True' diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod2-leaf1a.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod2-leaf1a.yml new file mode 100644 index 00000000000..2f2b4eb56ae --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod2-leaf1a.yml @@ -0,0 +1,145 @@ +hostname: dc1-pod2-leaf1a +is_deployed: true +router_bgp: + as: '65122' + router_id: 10.255.0.7 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + peer_groups: + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + - name: EVPN-OVERLAY-PEERS + type: evpn + update_source: Loopback0 + bfd: true + send_community: all + maximum_routes: 0 + ebgp_multihop: 3 + address_family_ipv4: + peer_groups: + - name: IPv4-UNDERLAY-PEERS + activate: true + - name: EVPN-OVERLAY-PEERS + activate: false + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + neighbors: + - ip_address: 10.255.255.8 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65121' + description: dc1-pod2-spine1_Ethernet1 + - ip_address: 10.255.0.2 + peer_group: EVPN-OVERLAY-PEERS + description: dc1-pod2-spine1 + remote_as: '65121' + address_family_evpn: + peer_groups: + - name: EVPN-OVERLAY-PEERS + activate: true +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: mstp + mst_instances: + - id: '0' + priority: 4096 +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.2.101/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +ethernet_interfaces: +- name: Ethernet1 + peer: dc1-pod2-spine1 + peer_interface: Ethernet1 + peer_type: spine + description: P2P_LINK_TO_DC1-POD2-SPINE1_Ethernet1 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.9/31 +loopback_interfaces: +- name: Loopback0 + description: EVPN_Overlay_Peering + shutdown: false + ip_address: 10.255.0.7/32 +- name: Loopback1 + description: VTEP_VXLAN_Tunnel_Source + shutdown: false + ip_address: 10.255.1.7/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.255.0.0/27 eq 32 + - sequence: 20 + action: permit 10.255.1.0/27 eq 32 +route_maps: +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +router_bfd: + multihop: + interval: 300 + min_rx: 300 + multiplier: 3 +ip_igmp_snooping: + globally_enabled: true +ip_virtual_router_mac_address: 00:1c:73:00:00:99 +vxlan_interface: + Vxlan1: + description: dc1-pod2-leaf1a_VTEP + vxlan: + udp_port: 4789 + source_interface: Loopback1 +cv_tags: + interface_tags: + - interface: Ethernet3 + tags: + - name: peer_device_interface + value: Ethernet3 + device_tags: + - name: topology_hint_datacenter + value: DC1 + - name: topology_hint_fabric + value: FABRIC + - name: topology_hint_pod + value: pod2 + - name: topology_hint_type + value: leaf + - name: layer3_routing + value: 'True' diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod2-spine1.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod2-spine1.yml new file mode 100644 index 00000000000..b1dff537bb2 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-pod2-spine1.yml @@ -0,0 +1,138 @@ +hostname: dc1-pod2-spine1 +is_deployed: true +router_bgp: + as: '65121' + router_id: 10.255.0.2 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + peer_groups: + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + - name: EVPN-OVERLAY-PEERS + type: evpn + update_source: Loopback0 + bfd: true + send_community: all + maximum_routes: 0 + ebgp_multihop: 3 + next_hop_unchanged: true + address_family_ipv4: + peer_groups: + - name: IPv4-UNDERLAY-PEERS + activate: true + - name: EVPN-OVERLAY-PEERS + activate: false + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + neighbors: + - ip_address: 10.255.255.9 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65122' + description: dc1-pod2-leaf1a_Ethernet1 + - ip_address: 10.255.255.2 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65100' + description: dc1-super-spine1_Ethernet4 + - ip_address: 10.255.0.7 + peer_group: EVPN-OVERLAY-PEERS + description: dc1-pod2-leaf1a + remote_as: '65122' + address_family_evpn: + peer_groups: + - name: EVPN-OVERLAY-PEERS + activate: true +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: none +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.2.11/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +ethernet_interfaces: +- name: Ethernet1 + peer: dc1-pod2-leaf1a + peer_interface: Ethernet1 + peer_type: l3leaf + description: P2P_LINK_TO_DC1-POD2-LEAF1A_Ethernet1 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.8/31 +- name: Ethernet10 + peer: dc1-super-spine1 + peer_interface: Ethernet4 + peer_type: super-spine + description: P2P_LINK_TO_DC1-SUPER-SPINE1_Ethernet4 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.3/31 +loopback_interfaces: +- name: Loopback0 + description: EVPN_Overlay_Peering + shutdown: false + ip_address: 10.255.0.2/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.255.0.0/27 eq 32 +route_maps: +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +router_bfd: + multihop: + interval: 300 + min_rx: 300 + multiplier: 3 +cv_tags: + device_tags: + - name: topology_hint_datacenter + value: DC1 + - name: topology_hint_fabric + value: FABRIC + - name: topology_hint_pod + value: pod2 + - name: topology_hint_type + value: spine + - name: topology_hint_rack + value: RackB + - name: layer3_routing + value: 'True' diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-super-spine1.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-super-spine1.yml new file mode 100644 index 00000000000..835f91f9286 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/intended/structured_configs/dc1-super-spine1.yml @@ -0,0 +1,113 @@ +hostname: dc1-super-spine1 +is_deployed: true +router_bgp: + as: '65100' + router_id: 10.255.0.1 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + peer_groups: + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + address_family_ipv4: + peer_groups: + - name: IPv4-UNDERLAY-PEERS + activate: true + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + neighbors: + - ip_address: 10.255.255.1 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65101' + description: dc1-pod1-spine1_Ethernet10 + - ip_address: 10.255.255.3 + peer_group: IPv4-UNDERLAY-PEERS + remote_as: '65121' + description: dc1-pod2-spine1_Ethernet10 +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +spanning_tree: + mode: none +local_users: +- name: admin + privilege: 15 + role: network-admin + no_password: true +- name: ansible + privilege: 15 + role: network-admin + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 +vrfs: +- name: MGMT + ip_routing: false +management_interfaces: +- name: Management1 + description: oob_management + shutdown: false + vrf: MGMT + ip_address: 172.16.1.1/24 + gateway: null + type: oob +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +ethernet_interfaces: +- name: Ethernet1 + peer: dc1-pod1-spine1 + peer_interface: Ethernet10 + peer_type: spine + description: P2P_LINK_TO_DC1-POD1-SPINE1_Ethernet10 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.0/31 +- name: Ethernet4 + peer: dc1-pod2-spine1 + peer_interface: Ethernet10 + peer_type: spine + description: P2P_LINK_TO_DC1-POD2-SPINE1_Ethernet10 + shutdown: false + mtu: 9214 + type: routed + ip_address: 10.255.255.2/31 +loopback_interfaces: +- name: Loopback0 + description: EVPN_Overlay_Peering + shutdown: false + ip_address: 10.255.0.1/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.255.0.0/27 eq 32 +route_maps: +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +cv_tags: + device_tags: + - name: topology_hint_datacenter + value: DC1 + - name: topology_hint_fabric + value: FABRIC + - name: topology_hint_pod + value: superspines + - name: topology_hint_type + value: core + - name: layer3_routing + value: 'True' diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/CONNECTED_ENDPOINTS.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/CONNECTED_ENDPOINTS.yml new file mode 100644 index 00000000000..5d5d6f1a535 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/CONNECTED_ENDPOINTS.yml @@ -0,0 +1,27 @@ +--- +# Definition of connected endpoints in the fabric. +servers: + # Name of the defined server. + dc1-pod1-leaf1-server1: + # Definition of adapters on the server. + adapters: + # Name of the server interfaces that will be used in the description of each interface + - endpoint_ports: [ PCI1, PCI2 ] + # Device ports where the server ports are connected. + switch_ports: [ Ethernet5, Ethernet5 ] + # Device names where the server ports are connected. + switches: [ dc1-pod1-leaf1a, dc1-pod1-leaf1b ] + # VLANs that will be configured on these ports. + vlans: 11-12,21-22 + # Native VLAN to be used on these ports. + native_vlan: 4092 + # L2 mode of the port. + mode: trunk + # Spanning tree portfast configuration on this port. + spanning_tree_portfast: edge + # Definition of the pair of ports as port channel. + port_channel: + # Description of the port channel interface. + description: PortChannel dc1-pod1-leaf1-server1 + # Port channel mode for LACP. + mode: active diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1.yml new file mode 100644 index 00000000000..6c161b450fe --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1.yml @@ -0,0 +1,152 @@ +--- + +# Specify the name of the Datacenter +dc_name: DC1 + +# Specify the name of the pod. For this 3 clos network +# we will just have 1 pod + +super_spine: + defaults: + # Set the relevant platform as each platform has different default values in Ansible AVD + platform: vEOS-lab + # Pool of IPv4 addresses to configure interface Loopback0 used for BGP EVPN sessions + loopback_ipv4_pool: 10.255.0.0/27 + # ASN to be used by BGP + bgp_as: 65100 + + # Definition of nodes contained in this group. + # Specific configuration of device must take place under the node definition. Each node inherites all values defined under 'defaults' + nodes: + # Name of the node to be defined (must be consistent with definition in inventory) + dc1-super-spine1: + # Device ID definition. An integer number used for internal calculations (ie. IPv4 address of the loopback_ipv4_pool among others) + id: 1 + # Management IP to be assigned to the management interface + mgmt_ip: 172.16.1.1/24 + + +# Spine switch group +spine: + # Definition of default values that will be configured to all nodes defined in this group + defaults: + # Set the relevant platform as each platform has different default values in Ansible AVD + platform: vEOS-lab + # Pool of IPv4 addresses to configure interface Loopback0 used for BGP EVPN sessions + loopback_ipv4_pool: 10.255.0.0/27 + uplink_ipv4_pool: 10.255.255.0/26 + # ASN to be used by BGP + uplink_switches: [ dc1-super-spine1 ] + uplink_interfaces: [ Ethernet10 ] + + # Definition of nodes contained in this group. + # Specific configuration of device must take place under the node definition. Each node inherites all values defined under 'defaults' + nodes: + # Name of the node to be defined (must be consistent with definition in inventory) + dc1-pod1-spine1: + # Device ID definition. An integer number used for internal calculations (ie. IPv4 address of the loopback_ipv4_pool among others) + id: 1 + rack: RackB + # Management IP to be assigned to the management interface + mgmt_ip: 172.16.1.11/24 + bgp_as: 65101 + uplink_switch_interfaces: + - Ethernet1 + + dc1-pod2-spine1: + # Device ID definition. An integer number used for internal calculations (ie. IPv4 address of the loopback_ipv4_pool among others) + id: 2 + rack: RackB + # Management IP to be assigned to the management interface + mgmt_ip: 172.16.2.11/24 + bgp_as: 65121 + uplink_switch_interfaces: + - Ethernet4 + +# L3 Leaf switch group +l3leaf: + defaults: + # Set the relevant platform as each platform has different default values in Ansible AVD + platform: vEOS-lab + # Pool of IPv4 addresses to configure interface Loopback0 used for BGP EVPN sessions + loopback_ipv4_pool: 10.255.0.0/27 + # Offset all assigned loopback IP addresses. + # Required when the < loopback_ipv4_pool > is same for 2 different node_types (like spine and l3leaf) to avoid over-lapping IPs. + # For example, set the minimum offset l3leaf.defaults.loopback_ipv4_offset: < total # spine switches > or vice versa. + loopback_ipv4_offset: 2 + # Definition of pool of IPs to be used as Virtual Tunnel EndPoint (VXLAN origin and destination IPs) + vtep_loopback_ipv4_pool: 10.255.1.0/27 + # Interfaces connecting this device towards the upper level of the hierarchy (Spines in this case) + uplink_interfaces: ['Ethernet1', 'Ethernet2'] + # Ansible hostname of the devices used to establish neighborship (IP assignments and BGP peering) + # Definition of pool of IPs to be used in P2P links + uplink_ipv4_pool: 10.255.255.0/26 + # MLAG Peer link physical interface definition + mlag_interfaces: ['Ethernet3', 'Ethernet4'] + # Definition of pool of IPs to be used for MLAG peer-link connectivity + mlag_peer_ipv4_pool: 10.255.1.64/27 + # iBGP Peering between MLAG peers + mlag_peer_l3_ipv4_pool: 10.255.1.96/27 + # Virtual router mac for VNIs assigned to Leaf switches in format xx:xx:xx:xx:xx:xx + virtual_router_mac_address: 00:1c:73:00:00:99 + spanning_tree_priority: 4096 + spanning_tree_mode: mstp + + # If two nodes (and only two) are in the same node_group, they will automatically form an MLAG pair + node_groups: + # Definition of a node group that will include two devices in MLAG. + # Definitions under the group will be inherited by both nodes in the group + DC1_POD1_L3_LEAF1: + # ASN to be used by BGP for the group. Both devices in the MLAG pair will use the same BGP ASN + bgp_as: 65102 + uplink_switches: ['dc1-pod1-spine1'] + nodes: + # Definition of hostnames under the node_group + dc1-pod1-leaf1a: + id: 1 + mgmt_ip: 172.16.1.101/24 + # Definition of the port to be used in the uplink device facing this device. + # Note that the number of elements in this list must match the length of 'uplink_switches' as well as 'uplink_interfaces' + uplink_switch_interfaces: + - Ethernet1 + - Ethernet1 + dc1-pod1-leaf1b: + id: 2 + mgmt_ip: 172.16.1.102/24 + uplink_switch_interfaces: + - Ethernet2 + - Ethernet2 + + DC1_POD2_L3_LEAF1: + # ASN to be used by BGP for the group. Both devices in the MLAG pair will use the same BGP ASN + bgp_as: 65122 + uplink_switches: ['dc1-pod2-spine1'] + nodes: + # Definition of hostnames under the node_group + dc1-pod2-leaf1a: + id: 5 + mgmt_ip: 172.16.2.101/24 + # Definition of the port to be used in the uplink device facing this device. + # Note that the number of elements in this list must match the length of 'uplink_switches' as well as 'uplink_interfaces' + uplink_switch_interfaces: + - Ethernet1 + - Ethernet1 + +# L2 Leaf switch group +l2leaf: + defaults: + platform: vEOS-lab + uplink_interfaces: ['Ethernet1', 'Ethernet2'] + spanning_tree_mode: mstp + + node_groups: + DC1_POD1_L2_LEAF1: + uplink_switches: ['dc1-pod1-leaf1a', 'dc1-pod1-leaf1b'] + nodes: + dc1-pod1-leaf1c: + id: 1 + rack: rack-1c + mgmt_ip: 172.16.1.151/24 + uplink_switch_interfaces: + - Ethernet8 + - Ethernet8 diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1.yml new file mode 100644 index 00000000000..95a5046885f --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1.yml @@ -0,0 +1,4 @@ +--- +# Specify the name of the pod. For this 3 clos network +# we will just have 1 pod +pod_name: pod1 diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_L2_LEAVES.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_L2_LEAVES.yml new file mode 100644 index 00000000000..75c134065d5 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_L2_LEAVES.yml @@ -0,0 +1,8 @@ +--- +type: l2leaf + +# Test overriding default topology hint +# associated with l2leafs for leafs in this +# group. These leafs should be set to edge +# in the topology on CloudVision. +cv_tags_topology_type: edge diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_L3_LEAVES.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_L3_LEAVES.yml new file mode 100644 index 00000000000..6a2d5cf2593 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_L3_LEAVES.yml @@ -0,0 +1,12 @@ +--- +type: l3leaf + +cv_tags_generate_interface: + - data_path: peer_interface + name: peer_device_interface + +# Override the Fabric-wide variable +# so that this group should only have +# the default cv_tags required for the +# cloudvision topology generation +cv_tags_generate_device: [] diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_SPINES.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_SPINES.yml new file mode 100644 index 00000000000..ea38f466bf5 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD1_SPINES.yml @@ -0,0 +1,2 @@ +--- +type: spine diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2.yml new file mode 100644 index 00000000000..4343f7244fd --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2.yml @@ -0,0 +1,4 @@ +--- +# Specify the name of the pod. For this 3 clos network +# we will just have 1 pod +pod_name: pod2 diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2_L3_LEAVES.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2_L3_LEAVES.yml new file mode 100644 index 00000000000..3f17c199728 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2_L3_LEAVES.yml @@ -0,0 +1,14 @@ +--- +type: l3leaf + +# Define the cv_tags directly, +# overriding anything that was generated. +# This will also test the case where a device +# has only interface_tags defined and no +# device_tags +custom_structured_configuration_cv_tags: + interface_tags: + - interface: Ethernet3 + tags: + - name: peer_device_interface + value: Ethernet3 diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2_SPINES.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2_SPINES.yml new file mode 100644 index 00000000000..ea38f466bf5 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_POD2_SPINES.yml @@ -0,0 +1,2 @@ +--- +type: spine diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_SUPER_SPINES.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_SUPER_SPINES.yml new file mode 100644 index 00000000000..fac7cb2d934 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/DC1_SUPER_SPINES.yml @@ -0,0 +1,4 @@ +--- +type: super-spine + +pod_name: superspines diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/FABRIC.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/FABRIC.yml new file mode 100644 index 00000000000..e519c0dd77f --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/group_vars/FABRIC.yml @@ -0,0 +1,44 @@ +--- +# Ansible connectivity definitions +# eAPI connectivity via HTTPS is specified (as opposed to CLI via SSH) +ansible_connection: ansible.netcommon.httpapi +# Specifies that we are indeed using Arista EOS +ansible_network_os: arista.eos.eos +# This user/password must exist on the switches to enable Ansible access +ansible_user: ansible +ansible_password: ansible +# User escalation (to enter enable mode) +ansible_become: true +ansible_become_method: enable +# Use SSL (HTTPS) +ansible_httpapi_use_ssl: true +# Do not try to validate certs +ansible_httpapi_validate_certs: false + +# Common AVD group variables +fabric_name: FABRIC + +# Define underlay and overlay routing protocol to be used +underlay_routing_protocol: ebgp +overlay_routing_protocol: ebgp + +# Local users +local_users: + # Define a new user, which is called "ansible" + ansible: + privilege: 15 + role: network-admin + # Password set to "ansible". Same string as the device generates when configuring a username. + sha512_password: $6$7u4j1rkb3VELgcZE$EJt2Qff8kd/TapRoci0XaIZsL4tFzgq1YZBLD9c6f/knXzvcYY0NcMKndZeCv0T268knGKhOEwZAxqKjlMm920 + admin: + privilege: 15 + role: network-admin + no_password: true + +cv_tags_enabled: True + +# Generate device tags for all devices in the fabric +# (except where it may be overridden in group_vars/host_vars) +cv_tags_generate_device: + - data_path: ip_routing + name: layer3_routing diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/host_vars/dc1-pod1-leaf1a.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/host_vars/dc1-pod1-leaf1a.yml new file mode 100644 index 00000000000..54867071637 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/host_vars/dc1-pod1-leaf1a.yml @@ -0,0 +1,6 @@ +--- +# Test setting custom cloudvision tags for +# a single device. +cv_tags_device_custom: + - name: custom_tag + value: custom_value_leaf1a diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/host_vars/dc1-pod1-leaf1b.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/host_vars/dc1-pod1-leaf1b.yml new file mode 100644 index 00000000000..7f3d2d2549f --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/host_vars/dc1-pod1-leaf1b.yml @@ -0,0 +1,6 @@ +--- +# The below variable is being set to test turning off +# tag generation for a single device. +# This host should not have any tags generated, and should be skipped +# by the `arista.avd.cloudvision` role. +cv_tags_enabled: False diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/inventory.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/inventory.yml new file mode 100644 index 00000000000..ea6c9cfb06a --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/inventory/inventory.yml @@ -0,0 +1,47 @@ +--- +all: + children: + CVP: + hosts: + cv_server: + ansible_host: "{{ lookup('ansible.builtin.env', 'CVP_ADDRESS') }}" + ansible_user: "{{ lookup('ansible.builtin.env', 'CVP_USERNAME') }}" + ansible_password: "{{ lookup('ansible.builtin.env', 'CVP_PASSWORD') }}" + ansible_connection: httpapi + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_network_os: eos + ansible_httpapi_port: 443 + FABRIC: + children: + DC1: + children: + DC1_SUPER_SPINES: + hosts: + dc1-super-spine1: + DC1_POD1: + children: + DC1_POD1_SPINES: + hosts: + dc1-pod1-spine1: + DC1_POD1_L3_LEAVES: + hosts: + dc1-pod1-leaf1a: + dc1-pod1-leaf1b: + DC1_POD1_L2_LEAVES: + hosts: + dc1-pod1-leaf1c: + DC1_POD2: + children: + DC1_POD2_SPINES: + hosts: + dc1-pod2-spine1: + DC1_POD2_L3_LEAVES: + hosts: + dc1-pod2-leaf1a: + + CONNECTED_ENDPOINTS: + children: + DC1_POD1_L3_LEAVES: + DC1_POD1_L2_LEAVES: + DC1_POD2_L3_LEAVES: diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/molecule.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/molecule.yml new file mode 100644 index 00000000000..2a5edb0155f --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/molecule.yml @@ -0,0 +1,37 @@ +--- +scenario: + converge_sequence: + - syntax + - create + - converge + test_sequence: + - syntax + - create + - converge + cleanup_sequence: + - destroy +dependency: + name: galaxy + env: + CVP_ADDRESS: ${CVP_ADDRESS} + CVP_USERNAME: ${CVP_USERNAME} + CVP_PASSWORD: ${CVP_PASSWORD} +driver: + name: delegated +platforms: + - name: dummy + managed: false +provisioner: + name: ansible + config_options: + defaults: + jinja2_extensions: 'jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n' + gathering: explicit + inventory: + links: + hosts: 'inventory/inventory.yml' + group_vars: 'inventory/group_vars/' + host_vars: 'inventory/host_vars/' + ansible_args: [] +verifier: + name: ansible diff --git a/ansible_collections/arista/avd/molecule/cloudvision_tags/verify.yml b/ansible_collections/arista/avd/molecule/cloudvision_tags/verify.yml new file mode 100644 index 00000000000..f182ddd5e15 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/cloudvision_tags/verify.yml @@ -0,0 +1,12 @@ +--- +# This verification does not yet work +- name: Verify tags on CVP + hosts: all + gather_facts: false + connection: local + tasks: + + - name: Generate device intended config and documentation + delegate_to: 127.0.0.1 + ansible.builtin.import_role: + name: arista.avd.eos_cli_config_gen diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1A.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1A.yml index 2d121014ca9..623bbcf44e2 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1A.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1A.yml @@ -615,3 +615,9 @@ sflow: - destination: 10.0.200.90 source_interface: Management1 ntp: null +cv_tags: + device_tags: + - name: topology_hint_fabric + value: EOS_DESIGNS_UNIT_TESTS + - name: topology_hint_type + value: leaf diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/DC1-BL1A.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/DC1-BL1A.yml index b72f90d1014..ea0e81017bd 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/DC1-BL1A.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/DC1-BL1A.yml @@ -51,3 +51,9 @@ my_mac_address_table: logging: "{{ switch.id == 6 }}" override_mac_address_table: "{{ my_mac_address_table }}" + +# Test that we can enable CloudVision Tag generation +# for a single device in a Fabric. The structured config for this node +# should have CloudVision tags generated for it. All other nodes in +# eos_designs_unit_tests should not have any CloudVision tags generate for them +cv_tags_enabled: True diff --git a/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/misc.py b/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/misc.py index 6d48adbf67b..1bcca67c37f 100644 --- a/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/misc.py +++ b/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/misc.py @@ -168,6 +168,14 @@ def evpn_prevent_readvertise_to_server(self: SharedUtils) -> bool: def dc_name(self: SharedUtils) -> str | None: return get(self.hostvars, "dc_name") + @cached_property + def fabric_name(self: SharedUtils) -> str: + return get(self.hostvars, "fabric_name", required=True) + + @cached_property + def rack(self: SharedUtils) -> str | None: + return get(self.switch_data_combined, "rack") + @cached_property def network_services_keys(self: SharedUtils) -> list[dict]: """ diff --git a/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/node_type_keys.py b/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/node_type_keys.py index 36904db455d..466624e6dee 100644 --- a/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/node_type_keys.py +++ b/ansible_collections/arista/avd/plugins/plugin_utils/eos_designs_shared_utils/node_type_keys.py @@ -21,6 +21,7 @@ "type": "spine", "default_evpn_role": "server", "default_ptp_priority1": 20, + "cv_tags_topology_type": "spine", }, { "key": "l3leaf", @@ -34,6 +35,7 @@ }, "vtep": True, "default_ptp_priority1": 30, + "cv_tags_topology_type": "leaf", }, { "key": "l2leaf", @@ -45,15 +47,18 @@ }, "underlay_router": False, "uplink_type": "port-channel", + "cv_tags_topology_type": "leaf", }, { "key": "super_spine", "type": "super-spine", + "cv_tags_topology_type": "core", }, { "key": "overlay_controller", "type": "overlay-controller", "default_evpn_role": "server", + "cv_tags_topology_type": "spine", }, ], "mpls": [ diff --git a/ansible_collections/arista/avd/roles/cloudvision/README.md b/ansible_collections/arista/avd/roles/cloudvision/README.md new file mode 100644 index 00000000000..2e6dbe286e5 --- /dev/null +++ b/ansible_collections/arista/avd/roles/cloudvision/README.md @@ -0,0 +1,127 @@ + + +# cloudvision + +## Overview + +**cloudvision** is a role designed to apply AVD related changes to CloudVision. +At the moment, the changes supported are: + +- Add CloudVision Tags + +The **cloudvision** role : + +- Designed to have the device inventory as targets. +- Follows the `hosts` definition as would `eos_designs` and `eos_cli_config_gen` +- Works well with intentory groups, and cli `--limit` arguments +- Uses the `delegate_to` functionality to point to the CloudVision instance. + +## Role requirements + +This role requires the `arista.cvp` collection to support CloudVision interactions. This can be installed using: + +```shell +ansible-galaxy collection install arista.cvp +``` + +!!! note + When using ansible-cvp modules, the user executing the Ansible playbook must have write access to both CVP and the EOS CLI. + +## Role Inputs and Outputs + +The role will excute the following tasks in order: + +1. Reads the inventory. +2. Role looks for structured configuration previously generated by [`arista.avd.eos_designs`](../eos_designs/README.md). +3. Role looks for any tags that have been generated for each device. +4. Applies any tags found to CloudVision using the [`arista.cvp`](https://github.com/aristanetworks/ansible-cvp/) collection. + +### Inputs + +**Sturctured Config:** + +The role expects that the `structured_config` for the devices to be targeted has already been generated using `eos_designs` role. + +Refer to: + +- [eos_designs CloudVision Tags](../eos_designs/docs/input-variables.md#cloudvision-tags) for the inputs expected by eos_designs. +- [How-To Guide for CloudVision Tags](../eos_designs/docs/how-to/cloudvision-tags.md) + +**Inventory configuration:** + +The inventory must include an entry for the CloudVision server. +`arista.cvp` modules use the httpapi connection, and the tasks that imports the cloudvision role must delegate to the CloudVision node as named in the inventory. Refer to the example below that shows such an entry in the inventory. + +!!! info + **As opposed to `eos_config_deploy_cvp`, the target/host that the role expectes to be run against in the playbook is the EOS device. The `delegate_to` functionality should be used to delegate to the cloudvision node listed in the inventory.** + +```yaml +all: + children: + cloudvision: + hosts: + cv_server01: + ansible_host: 10.83.28.164 + ansible_user: ansible + ansible_password: ansible + ansible_connection: httpapi + ansible_httpapi_use_ssl: True + ansible_httpapi_validate_certs: False + ansible_network_os: eos + ansible_httpapi_port: 443 +``` + +For a complete list of authentication options available with CloudVision Ansible collection, you can read the dedicated page on [arista.cvp collection](https://cvp.avd.sh/en/latest/docs/how-to/cvp-authentication/). + +#### Getting Started + +Below is an example of how to use role: + +```yaml +- name: Apply CloudVision Tags + hosts: DC1_FABRIC + gather_facts: false + tasks: + - name: Apply Generated CloudVision Tags + ansible.builtin.import_role: + name: arista.avd.cloudvision + delegate_to: cv_server01 +``` + +#### Ignore devices not provisioned in CloudVision + +When you want to apply CloudVision tags to a complete topology and devices aren't already in CloudVision, you can configure inventory to ignore these devices by using a host variable: `is_deployed`. + +- `is_deployed: true` or `is_deployed is not defined`: The tags for this device are applied on CloudVision +- `is_deployed: false`: Device is skipped. + +Here is an overview with the key configured in the YAML inventory: + +```yaml + DC1_BL_LEAFS: + hosts: + DC1-BL1A: + # is_deployed is assumed to be True + # Device configuration is generated by AVD + # Device configurationa and tags are applied on Cloudvision + DC1-BL2A: + # Device configuration is generated by AVD + # Device is not configured on Cloudvision and no CloudVision tags are applied. + is_deployed: false +``` + +### Outputs + +- No outputs to Ansible or to file. All changes are made via API calls to CloudVision. + +## Requirements + +Requirements are located here: [avd-requirements](../../README.md#Requirements) + +## License + +Project is published under [Apache 2.0 License](../../LICENSE) diff --git a/ansible_collections/arista/avd/roles/cloudvision/defaults/main/main.yml b/ansible_collections/arista/avd/roles/cloudvision/defaults/main/main.yml new file mode 100644 index 00000000000..5e0a4040ad3 --- /dev/null +++ b/ansible_collections/arista/avd/roles/cloudvision/defaults/main/main.yml @@ -0,0 +1,14 @@ +# Roles Defaults +# Root directory where to build output structure +root_dir: '{{ inventory_dir }}' + +# AVD configurations output +# Main output directory +output_dir_name: 'intended' +output_dir: '{{ root_dir }}/{{ output_dir_name }}' + +# Output for structured YAML files: +structured_dir_name: 'structured_configs' +structured_dir: '{{ output_dir }}/{{ structured_dir_name }}' + +avd_structured_config_file_format: "yml" diff --git a/ansible_collections/arista/avd/roles/cloudvision/tasks/main.yml b/ansible_collections/arista/avd/roles/cloudvision/tasks/main.yml new file mode 100644 index 00000000000..46355e5f525 --- /dev/null +++ b/ansible_collections/arista/avd/roles/cloudvision/tasks/main.yml @@ -0,0 +1,25 @@ +# Copyright (c) 2023 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +--- +- name: "Load tag information from structured config" + ansible.builtin.include_vars: + file: "{{ filename }}" + delegate_to: localhost + when: + - structured_config is not defined and lookup('first_found', filename, skip=True, errors='ignore') + - is_deployed is not arista.avd.defined(false) + vars: + filename: "{{ structured_dir }}/{{ inventory_hostname }}.{{ avd_structured_config_file_format }}" + +- name: "Update tags on CVP" + arista.cvp.cv_tag_v3: + tags: + - device: "{{ inventory_hostname }}" + device_tags: "{{ cv_tags.device_tags | arista.avd.default(omit) }}" + interface_tags: "{{ cv_tags.interface_tags | arista.avd.default(omit) }}" + mode: assign + auto_create: true + when: + - is_deployed is not arista.avd.defined(false) + - cv_tags is arista.avd.defined diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/cv-tags.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/cv-tags.md new file mode 100644 index 00000000000..6b335f1396d --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/cv-tags.md @@ -0,0 +1,32 @@ + +=== "Table" + + | Variable | Type | Required | Default | Value Restrictions | Description | + | -------- | ---- | -------- | ------- | ------------------ | ----------- | + | [cv_tags](## "cv_tags") | Dictionary | | | | | + | [  device_tags](## "cv_tags.device_tags") | List, items: Dictionary | | | | | + | [    - name](## "cv_tags.device_tags.[].name") | String | Required | | | | + | [      value](## "cv_tags.device_tags.[].value") | String | Required | | | | + | [  interface_tags](## "cv_tags.interface_tags") | List, items: Dictionary | | | | | + | [    - interface](## "cv_tags.interface_tags.[].interface") | String | Required | | | | + | [      tags](## "cv_tags.interface_tags.[].tags") | List, items: Dictionary | | | | | + | [        - name](## "cv_tags.interface_tags.[].tags.[].name") | String | Required | | | | + | [          value](## "cv_tags.interface_tags.[].tags.[].value") | String | Required | | | | + +=== "YAML" + + ```yaml + cv_tags: + device_tags: + - name: + value: + interface_tags: + - interface: + tags: + - name: + value: + ``` diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.jsonschema.json b/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.jsonschema.json index 52129878854..951f6d7ec4d 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.jsonschema.json +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.jsonschema.json @@ -1086,6 +1086,86 @@ }, "title": "Clock" }, + "cv_tags": { + "type": "object", + "properties": { + "device_tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "value": { + "type": "string", + "title": "Value" + } + }, + "required": [ + "name", + "value" + ], + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + } + }, + "title": "Device Tags" + }, + "interface_tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "interface": { + "type": "string", + "title": "Interface" + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "value": { + "type": "string", + "title": "Value" + } + }, + "required": [ + "name", + "value" + ], + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + } + }, + "title": "Tags" + } + }, + "required": [ + "interface" + ], + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + } + }, + "title": "Interface Tags" + } + }, + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + }, + "title": "Cv Tags" + }, "community_lists": { "type": "array", "title": "Community Lists (legacy model)", diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.schema.yml b/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.schema.yml index 3c3fc5f3690..8beecaa258a 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.schema.yml +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/eos_cli_config_gen.schema.yml @@ -660,6 +660,41 @@ keys: keys: timezone: type: str + cv_tags: + type: dict + keys: + device_tags: + required: false + type: list + items: + type: dict + keys: + name: + type: str + required: true + value: + type: str + required: true + interface_tags: + required: false + type: list + items: + type: dict + keys: + interface: + type: str + required: true + tags: + type: list + items: + type: dict + keys: + name: + type: str + required: true + value: + type: str + required: true community_lists: type: list primary_key: name diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/schema_fragments/cloudvision_tags.schema.yml b/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/schema_fragments/cloudvision_tags.schema.yml new file mode 100644 index 00000000000..98cc7fe62a8 --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/schemas/schema_fragments/cloudvision_tags.schema.yml @@ -0,0 +1,43 @@ +# Copyright (c) 2023 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +# yaml-language-server: $schema=../../../../plugins/plugin_utils/schema/avd_meta_schema.json +# Line above is used by RedHat's YAML Schema vscode extension +# Use Ctrl + Space to get suggestions for every field. Autocomplete will pop up after typing 2 letters. +type: dict +keys: + cv_tags: + type: dict + keys: + device_tags: + required: false + type: list + items: + type: dict + keys: + name: + type: str + required: true + value: + type: str + required: true + interface_tags: + required: false + type: list + items: + type: dict + keys: + interface: + type: str + required: true + tags: + type: list + items: + type: dict + keys: + name: + type: str + required: true + value: + type: str + required: true diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/how-to/cloudvision-tags.md b/ansible_collections/arista/avd/roles/eos_designs/docs/how-to/cloudvision-tags.md new file mode 100644 index 00000000000..f251588db97 --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/how-to/cloudvision-tags.md @@ -0,0 +1,193 @@ + + +# CloudVision Tags + +If `cv_tags_enabled` is set to `True`, `arista.avd.eos_designs` can generate CloudVision Tags that can be applied to interfaces and/or devices. These tags can be used during Topology generateion, or used in searches/filters to select devices based on tags values. + +!!! note + - **By default this new feature is turned off. Set `cv_tags_enabled: True` in the Fabric to enable this feature.** + - **These tags would need to be applied to CloudVision using the [`arista.avd.cloudvision`](../../../cloudvision/README.md) role.** + +## Available Input Variables + +--8<-- +roles/eos_designs/docs/tables/cloudvision-tags.md +--8<-- + +## CloudVision Topology Tags + +`arista.avd.eos_designs` will attempt to generate CloudVision tags that assist CloudVision with rendering the Topology correctly. +It will attempt to generate what are called 'hints' for the following fields. These are picked up from the fabric variables if they are defined. + +| Hint Tag Name | Description | Source of information | +| ---------- | ----------- |--------------------- | +| `topology_hint_type` | Indicates whether the node is a leaf, spine, core device etc. | As defined in `node_type` keys. | +| `topology_hint_fabric` | The overall fabric that the devices pertains to. Useful for multi-fabric deployments. | `fabric_name` | +| `topology_hint_datacenter` | The datacenter to which the devices belongs. Helpful for multi-dc deployments. | `dc_name` | +| `topology_hint_pod` | The pod to which the devices belongs. | `pod_name` | +| `topology_hint_rack` | The physical rack in which the device is located. | `rack` defined on `node` or `node_group` | + +The `topology_hint_type` for a particular node can be overriden by defining the `cv_tags_topology_type` as per the below table. + +??? example "Example: Overriding default topology type" + + ```yaml + # This should be defined in host_vars or group_vars for the devices + # we want to affect + cv_tags_topology_type: edge + ``` + +## CloudVision Custom Tags + +It is possible to assing custom Tags and values to devices. Depending where the key `cv_tags_device_custom` is defined, the tag can be generated for a whole group or only a single device. + +!!! warning + **Tag names cannot have the name of any existing system tags on CloudVision. System tags cannot be emanded with this approach.** + +??? example "Example: Adding custom tags to devices" + + ```yaml + # This should be defined in host_vars or group_vars for the devices + # we want to affect + cv_tags_device_custom: + - name: custom_tag + value: custom_value_leaf1a + ``` + +## CloudVision Generated Tags + +It is possible to generate tags based on values that would be defined in `structured_config`. Any value that is **not**: + +- a list +- a dictionary +- a value in a list + +can be defined as the value for a tag. This allows for tags to be generated with values that are calculated for that device. Refer to the example below. + +!!! tip + **Generate the `structured_config` first to get a better idea of what keys are available.** + +!!! warning + - **Tag names cannot have the name of any existing system tags on CloudVision. System tags cannot be emanded with this approach.** + - **If the key specified in `data_path` is not found, the tag is not generated. This avoids having a lot of empty tags.** + +??? example "Example: Generating device and interface tags" + + ```yaml + # Each interface will have a tag called 'peer_device_name' with the hostname of + # of the peer, and a tag 'peer_device_interface' with the value of the + # interface name on the peer device. + # If the keys `peer` or `peer_interface` are not found in the structured_config + # the respective tags will not be created. + cv_tags_generate_interface: + - data_path: peer + name: peer_device_name + - data_path: peer_interface + name: peer_device_interface + + cv_tags_generate_device: + # Each EOS device will have tag called 'ip_routing' which will + # have a value of 'True' if ip_routing is enabled on the device. + - data_path: ip_routing + name: layer3_routing + # Each EOS device will have tag called 'bgp_router_id' which will + # have the router_id used for BGP, if it is configured for the device. + # Note the use of dot notation for the data_path. + - data_path: router_bgp.router_id + name: bgp_router_id + ``` + +## Applying the Tags on CloudVision + +For the tags to be available and applied to devices in CloudVision, the role [`arista.avd.cloudvision`](./../../../cloudvision/README.md) needs to be called against the fabric. + +The **cloudvision** role: + +- Is designed to have the device inventory as targets. +- Follows the `hosts` definition as would `eos_designs` and `eos_cli_config_gen` +- Works well with intentory groups, and cli `--limit` arguments +- Uses the `delegate_to` functionality to point to the CloudVision instance. + +### Role requirements + +This role requires `arista.cvp` collection to support CloudVision interactions. + +```shell +ansible-galaxy collection install arista.cvp +``` + +!!! note + When using ansible-cvp modules, the user executing the Ansible playbook must have write access to both CVP and the EOS CLI. + +### Inputs + +**Sturctured Config:** + +The role expects that the `structured_config` for the devices to be target has already been generated using `eos_designs` role. + +**Inventory configuration:** + +The inventory must include an entry for the CloudVision server. +`arista.cvp` modules use the httpapi connection, and the tasks that imports the cloudvision role must delegate to the CloudVision node as named in the inventory. Refer to the example below that shows such an entry in the inventory. + +!!! info + **As opposed to `eos_config_deploy_cvp`, the target/host that the role expectes to be run against in the playbook is the EOS device. The `delegate_to` functionality should be used to delegate to the cloudvision node listed in the inventory.** + +```yaml +# Example of cloudvision being defined in the inventory +all: + children: + cloudvision: + hosts: + cv_server01: + ansible_host: 10.83.28.164 + ansible_user: ansible + ansible_password: ansible + ansible_connection: httpapi + ansible_httpapi_use_ssl: True + ansible_httpapi_validate_certs: False + ansible_network_os: eos + ansible_httpapi_port: 443 +``` + +Below is an example of how to use the role: + +```yaml +- name: Apply CloudVision Tags + hosts: DC1_FABRIC + gather_facts: false + tasks: + - name: Apply Generated CloudVision Tags + ansible.builtin.import_role: + name: arista.avd.cloudvision + delegate_to: cv_server01 +``` + +!!! info + For a complete list of authentication options available with CloudVision Ansible collection, you can read the dedicated page on [arista.cvp collection](https://cvp.avd.sh/en/latest/docs/how-to/cvp-authentication/). + +#### Ignore devices not provisioned in CloudVision + +When you want to apply CloudVision tags to a complete topology and devices aren't already in CloudVision, you can configure inventory to ignore these devices by setting the host variable `is_deployed`. + +- `is_deployed: true` or `is_deployed is not defined`: The tags for this device are applied on CloudVision +- `is_deployed: false`: Device is skipped. + +Here is an overview with the key configured in the YAML inventory: + +```yaml + DC1_BL_LEAFS: + hosts: + DC1-BL1A: + # is_deployed is assumed to be True + # Device configuration is generated by AVD + # Device configurationa and tags are applied on Cloudvision + DC1-BL2A: + # Device configuration is generated by AVD + # Device is not configured on Cloudvision and no CloudVision tags are applied. + is_deployed: false +``` diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md b/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md index a1b4b68b8bd..b01868a708d 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md @@ -723,6 +723,15 @@ roles/eos_designs/docs/tables/system-settings.md roles/eos_designs/docs/tables/cloudvision-settings.md --8<-- +## CloudVision Tags + +!!! tip + Refer to the how-to document [**here**](./how-to/cloudvision-tags.md) for more information. + +--8<-- +roles/eos_designs/docs/tables/cloudvision-tags.md +--8<-- + ## Endpoint connectivity AVD supports two different data models for defining connectivity to endpoints: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/role-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/role-configuration.md index 51931f1d0b8..61ff336e02e 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/role-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/role-configuration.md @@ -28,3 +28,15 @@ roles/eos_designs/docs/tables/role-documentation-output-settings.md --8<-- roles/eos_designs/docs/tables/role-custom-templates.md --8<-- + +## CloudVision Tags + +By default, `eos_designs` will not generate the CloudVision Tags. To enable this feature, the `cv_tags_enabled` needs to be set to `True`. +This can be set in the host/group vars to influence all or some of the devices. Alternatively it can be set in the playbook. + +!!! note + **Refer to the [how-to](./how-to/cloudvision-tags.md) documentation for more details on how to use this feature.** + +--8<-- +roles/eos_designs/docs/tables/cloudvision-tags.md +--8<-- diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/cloudvision-tags.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/cloudvision-tags.md new file mode 100644 index 00000000000..b23f2e5fdd5 --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/cloudvision-tags.md @@ -0,0 +1,36 @@ + +=== "Table" + + | Variable | Type | Required | Default | Value Restrictions | Description | + | -------- | ---- | -------- | ------- | ------------------ | ----------- | + | [cv_tags_enabled](## "cv_tags_enabled") | Boolean | | `False` | | Enable the generation of CloudVision tags that can be applied using the `cloudvision` role. If not defined, the value is assumed to be False and no tags are generated.
| + | [cv_tags_topology_type](## "cv_tags_topology_type") | String | | | Valid Values:
- leaf
- spine
- core
- edge | Device type that CloudVision should use when generating the Topology.
| + | [cv_tags_generate_interface](## "cv_tags_generate_interface") | List, items: Dictionary | | | | List of interface tags that should be generated from
structured configuration.
| + | [  - name](## "cv_tags_generate_interface.[].name") | String | Required | | Value is converted to lower case | Tag name to be assigned to generated tags.
| + | [    data_path](## "cv_tags_generate_interface.[].data_path") | String | Required | | | Structured config field/key path to be used to find the value for the tag. Dot notation is supported to reference values inside dictionaries.
For Example: 'data_path: channel_group.id' would set the tag with the value of the channel id of the interface. If there is no channel id, the tag is not created.
| + | [cv_tags_generate_device](## "cv_tags_generate_device") | List, items: Dictionary | | | | List of device tags that should be generated from
structured configuration.
| + | [  - name](## "cv_tags_generate_device.[].name") | String | Required | | Value is converted to lower case | Tag name to be assigned to generated tags.
| + | [    data_path](## "cv_tags_generate_device.[].data_path") | String | Required | | | Structured config field/key path to be used to find the value for the tag. Dot notation is supported to reference values inside dictionaries.
For Example: 'data_path: router_bfd.multihop.interval' would set the tag with the value of the interval for multihop bfd. If this value is not specified in the structured config, the tag is not created.
| + | [cv_tags_device_custom](## "cv_tags_device_custom") | List, items: Dictionary | | | | List of user defined tags and their values to be applied to this
device on CVP.
| + | [  - name](## "cv_tags_device_custom.[].name") | String | Required | | Value is converted to lower case | Tag name to be assigned to generated tag.
| + | [    value](## "cv_tags_device_custom.[].value") | String | Required | | | Value to be assigned to the tag.
| + +=== "YAML" + + ```yaml + cv_tags_enabled: + cv_tags_topology_type: + cv_tags_generate_interface: + - name: + data_path: + cv_tags_generate_device: + - name: + data_path: + cv_tags_device_custom: + - name: + value: + ``` diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-keys.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-keys.md index f3c3b31d749..9d2bec0db16 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-keys.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-keys.md @@ -54,6 +54,7 @@ | [      connected_endpoints_port_channel_interfaces](## "node_type_keys.[].interface_descriptions.connected_endpoints_port_channel_interfaces") | String | | | | Path to Custom J2 template. | | [      overlay_loopback_interface](## "node_type_keys.[].interface_descriptions.overlay_loopback_interface") | String | | | | Path to Custom J2 template. | | [      vtep_loopback_interface](## "node_type_keys.[].interface_descriptions.vtep_loopback_interface") | String | | | | Path to Custom J2 template. | + | [    cv_tags_topology_type](## "node_type_keys.[].cv_tags_topology_type") | String | | | Valid Values:
- leaf
- spine
- core
- edge | Type that CloudVision should use when generating the Topology.
| === "YAML" @@ -105,4 +106,5 @@ connected_endpoints_port_channel_interfaces: overlay_loopback_interface: vtep_loopback_interface: + cv_tags_topology_type: ``` diff --git a/ansible_collections/arista/avd/roles/eos_designs/python_modules/base/snmp_server.py b/ansible_collections/arista/avd/roles/eos_designs/python_modules/base/snmp_server.py index 8f8e9174843..b8c8d1bac0a 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/python_modules/base/snmp_server.py +++ b/ansible_collections/arista/avd/roles/eos_designs/python_modules/base/snmp_server.py @@ -93,10 +93,10 @@ def _snmp_location(self, snmp_settings: dict) -> str | None: return None location_elements = [ - get(self._hostvars, "fabric_name"), + self.shared_utils.fabric_name, self.shared_utils.dc_name, self.shared_utils.pod_name, - get(self.shared_utils.switch_data_combined, "rack"), + self.shared_utils.rack, self.shared_utils.hostname, ] location_elements = [location for location in location_elements if location not in [None, ""]] diff --git a/ansible_collections/arista/avd/roles/eos_designs/python_modules/cv_tags/__init__.py b/ansible_collections/arista/avd/roles/eos_designs/python_modules/cv_tags/__init__.py new file mode 100644 index 00000000000..5be6319a2fd --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_designs/python_modules/cv_tags/__init__.py @@ -0,0 +1,6 @@ +# Copyright (c) 2023 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +from .avdstructuredconfig import AvdStructuredConfigTags + +__all__ = ["AvdStructuredConfigTags"] diff --git a/ansible_collections/arista/avd/roles/eos_designs/python_modules/cv_tags/avdstructuredconfig.py b/ansible_collections/arista/avd/roles/eos_designs/python_modules/cv_tags/avdstructuredconfig.py new file mode 100644 index 00000000000..6b270b9bbef --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_designs/python_modules/cv_tags/avdstructuredconfig.py @@ -0,0 +1,182 @@ +# Copyright (c) 2023 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +from __future__ import annotations + +from functools import cached_property + +from ansible_collections.arista.avd.plugins.plugin_utils.avdfacts import AvdFacts +from ansible_collections.arista.avd.plugins.plugin_utils.errors import AristaAvdError +from ansible_collections.arista.avd.plugins.plugin_utils.utils import get + +INVALID_CUSTOM_DEVICE_TAGS = [ + "topology_hint_type", + "topology_type", + "topology_hint_datacenter", + "topology_datacenter", + "topology_hint_rack", + "topology_rack", + "topology_pod", + "topology_hint_pod", + "eos", + "eostrain", + "ztp", + "bgp", + "container", + "mpls", + "topology_network_type", + "model", + "systype", + "serialnumber", + "tapagg", + "hostname", + "terminattr", +] + + +class AvdStructuredConfigTags(AvdFacts): + """ + This returns the cv_tags data strucutre as per the below example + { + "cv_tags": { + "device_tags": [ + {"name": "topology_hint_type", "value": }, + {"name: "topology_hint_dc", "value": }, + {"name": "topology_hint_fabric", "value": }, + {"name": "topology_hint_pod", "value": }, + {"name": "topolgoy_hint_rack", "value": }, + {"name": "", "value": "custom tag value"}, + {"name": "", "value": ""} + }, + "interface_tags": [ + { + "interface": "Ethernet1", + "tags":[ + { + "name": "peer" + "value": "leaf1a" + } + ] + } + ] + }, + } + + """ + + @cached_property + def cv_tags(self) -> str | None: + """ + Generate the data structure `cv_tags`. + """ + if not get(self._hostvars, "cv_tags_enabled", False): + # We do not want to define this datastructure if the feature is not + # enabled + return + + hints = [self._topology_hint_dc, self._topology_hint_fabric, self._topology_hint_pod, self._topology_hint_type, self._topology_hint_rack] + device_tags = [hint for hint in hints if hint] + + for custom_tag in get(self._hostvars, "cv_tags_device_custom", []): + if custom_tag["name"].lower() not in INVALID_CUSTOM_DEVICE_TAGS: + device_tags.append(custom_tag) + else: + raise AristaAvdError( + f"The CloudVision tag name {custom_tag['name']} is Invalid. System Tags cannot be overriden. Try using a different name for this tag." + ) + + for generate_tag in get(self._hostvars, "cv_tags_generate_device", []): + value = get(self._hostvars, generate_tag["data_path"], None) + if generate_tag["name"] in INVALID_CUSTOM_DEVICE_TAGS: + raise AristaAvdError( + f"The CloudVision tag name {generate_tag['name']} is Invalid. System Tags cannot be overriden. Try using a different name for this tag." + ) + if type(value) in [list, dict]: + raise AristaAvdError( + f"The data_path {generate_tag['data_path']} appears to be a {type(value).__name__}. This is not supported for cloudvision data_paths." + ) + if value is not None: + device_tags.append(self._tag_dict(generate_tag["name"], value)) + + interface_tags = [] + for link in get(self._hostvars, "ethernet_interfaces", []): + interface_tags.extend(self._interface_tags(link)) + + result = {"device_tags": device_tags} + if interface_tags: + result["interface_tags"] = interface_tags + + return result + + def _tag_dict(self, name, value) -> dict: + return {"name": name, "value": str(value)} + + @cached_property + def _topology_hint_type(self) -> dict | None: + """ + Return the topology hint type for the device. + """ + hint_type = get(self.shared_utils.node_type_key_data, "cv_tags_topology_type") + + hint_type = get(self._hostvars, "cv_tags_topology_type", hint_type) + + if not hint_type: + return None + + return self._tag_dict("topology_hint_type", hint_type) + + @cached_property + def _topology_hint_fabric(self) -> dict: + """ + Return the topology fabric hint tag. + """ + # `fabric_name` is required for any fabric, so we don't need to handle + # the case this is not available + return self._tag_dict("topology_hint_fabric", self.shared_utils.fabric_name) + + @cached_property + def _topology_hint_pod(self) -> dict | None: + """ + Return the topology fabric hint tag. + """ + if not self.shared_utils.pod_name: + return None + + return self._tag_dict("topology_hint_pod", self.shared_utils.pod_name) + + @cached_property + def _topology_hint_dc(self) -> dict | None: + """ + Return the topology fabric hint tag. + """ + if not self.shared_utils.dc_name: + return None + return self._tag_dict("topology_hint_datacenter", self.shared_utils.dc_name) + + @cached_property + def _topology_hint_rack(self) -> dict | None: + """ + Return the topology hint for the rack tag. + """ + if not self.shared_utils.rack: + return None + return self._tag_dict("topology_hint_rack", self.shared_utils.rack) + + def _interface_tags(self, link) -> list: + tags = [] + + for generate_tag in get(self._hostvars, "cv_tags_generate_interface", []): + value = get(link, generate_tag["data_path"]) + if generate_tag["name"] in INVALID_CUSTOM_DEVICE_TAGS: + raise AristaAvdError( + f"The CloudVision tag name {generate_tag['name']} is Invalid. System Tags cannot be overriden. Try using a different name for this tag." + ) + if type(value) in [list, dict]: + raise AristaAvdError( + f"The data_path {generate_tag['data_path']} appears to be a {type(value).__name__}. This is not supported for cloudvision data_paths." + ) + if value: + tags.append(self._tag_dict(generate_tag["name"], value)) + if tags: + return [{"interface": link["name"], "tags": tags}] + return [] diff --git a/ansible_collections/arista/avd/roles/eos_designs/python_modules/get_structured_config/get_structured_config.py b/ansible_collections/arista/avd/roles/eos_designs/python_modules/get_structured_config/get_structured_config.py index a45c1951e9e..ccb4c413b43 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/python_modules/get_structured_config/get_structured_config.py +++ b/ansible_collections/arista/avd/roles/eos_designs/python_modules/get_structured_config/get_structured_config.py @@ -15,6 +15,7 @@ from ..connected_endpoints import AvdStructuredConfigConnectedEndpoints from ..core_interfaces_and_l3_edge import AvdStructuredConfigCoreInterfacesAndL3Edge from ..custom_structured_configuration import AvdStructuredConfigCustomStructuredConfiguration +from ..cv_tags import AvdStructuredConfigTags from ..inband_management import AvdStructuredConfigInbandManagement from ..mlag import AvdStructuredConfigMlag from ..network_services import AvdStructuredConfigNetworkServices @@ -31,6 +32,7 @@ AvdStructuredConfigConnectedEndpoints, AvdStructuredConfigInbandManagement, AvdStructuredConfigCustomStructuredConfiguration, + AvdStructuredConfigTags, ] """ AVD_STRUCTURED_CONFIG contains a list of AvdStructuredConfig classes which generate the complete structured config. diff --git a/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.jsonschema.json b/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.jsonschema.json index a8d941d7865..cc4f7e38dc9 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.jsonschema.json +++ b/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.jsonschema.json @@ -2926,6 +2926,107 @@ ], "title": "Custom Structured Configuration Prefix" }, + "cv_tags_enabled": { + "type": "boolean", + "default": false, + "description": "Enable the generation of CloudVision tags that can be applied using the `cloudvision` role. If not defined, the value is assumed to be False and no tags are generated.\n", + "title": "Cv Tags Enabled" + }, + "cv_tags_topology_type": { + "description": "Device type that CloudVision should use when generating the Topology.\n", + "type": "string", + "enum": [ + "leaf", + "spine", + "core", + "edge" + ], + "title": "Cv Tags Topology Type" + }, + "cv_tags_generate_interface": { + "description": "List of interface tags that should be generated from\nstructured configuration.\n", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Tag name to be assigned to generated tags.\n", + "type": "string", + "title": "Name" + }, + "data_path": { + "description": "Structured config field/key path to be used to find the value for the tag. Dot notation is supported to reference values inside dictionaries.\nFor Example: 'data_path: channel_group.id' would set the tag with the value of the channel id of the interface. If there is no channel id, the tag is not created.\n", + "type": "string", + "title": "Data Path" + } + }, + "required": [ + "name", + "data_path" + ], + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + } + }, + "title": "Cv Tags Generate Interface" + }, + "cv_tags_generate_device": { + "description": "List of device tags that should be generated from\nstructured configuration.\n", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Tag name to be assigned to generated tags.\n", + "type": "string", + "title": "Name" + }, + "data_path": { + "description": "Structured config field/key path to be used to find the value for the tag. Dot notation is supported to reference values inside dictionaries.\nFor Example: 'data_path: router_bfd.multihop.interval' would set the tag with the value of the interval for multihop bfd. If this value is not specified in the structured config, the tag is not created.\n", + "type": "string", + "title": "Data Path" + } + }, + "required": [ + "name", + "data_path" + ], + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + } + }, + "title": "Cv Tags Generate Device" + }, + "cv_tags_device_custom": { + "description": "List of user defined tags and their values to be applied to this\ndevice on CVP.\n", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Tag name to be assigned to generated tag.\n", + "type": "string", + "title": "Name" + }, + "value": { + "description": "Value to be assigned to the tag.\n", + "type": "string", + "title": "Value" + } + }, + "required": [ + "name", + "value" + ], + "additionalProperties": false, + "patternProperties": { + "^_.+$": {} + } + }, + "title": "Cv Tags Device Custom" + }, "cv_topology": { "type": "array", "description": "Generate AVD configurations directly from the given CloudVision topology.\nActivate this feature by setting `use_cv_topology` to `true`.\nRequires `default_interfaces` to be set for the relevant platforms and node types to detect the proper interface roles automatically.\nNeighbor hostnames must match the inventory hostnames of the AVD inventory to be taken into consideration.", @@ -9504,6 +9605,17 @@ "^_.+$": {} }, "title": "Interface Descriptions" + }, + "cv_tags_topology_type": { + "description": "Type that CloudVision should use when generating the Topology.\n", + "type": "string", + "enum": [ + "leaf", + "spine", + "core", + "edge" + ], + "title": "Cv Tags Topology Type" } }, "additionalProperties": false, diff --git a/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.schema.yml b/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.schema.yml index fbef3895559..583d49e2fbe 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.schema.yml +++ b/ansible_collections/arista/avd/roles/eos_designs/schemas/eos_designs.schema.yml @@ -478,6 +478,113 @@ keys: existing keys will be replaced.\n" default: - custom_structured_configuration_ + cv_tags_enabled: + documentation_options: + table: cloudvision-tags + type: bool + default: false + description: 'Enable the generation of CloudVision tags that can be applied using + the `cloudvision` role. If not defined, the value is assumed to be False and + no tags are generated. + + ' + cv_tags_topology_type: + documentation_options: + table: cloudvision-tags + description: 'Device type that CloudVision should use when generating the Topology. + + ' + type: str + valid_values: + - leaf + - spine + - core + - edge + cv_tags_generate_interface: + documentation_options: + table: cloudvision-tags + description: 'List of interface tags that should be generated from + + structured configuration. + + ' + type: list + items: + type: dict + keys: + name: + description: 'Tag name to be assigned to generated tags. + + ' + convert_to_lower_case: true + required: true + type: str + data_path: + description: 'Structured config field/key path to be used to find the value + for the tag. Dot notation is supported to reference values inside dictionaries. + + For Example: ''data_path: channel_group.id'' would set the tag with the + value of the channel id of the interface. If there is no channel id, the + tag is not created. + + ' + required: true + type: str + cv_tags_generate_device: + documentation_options: + table: cloudvision-tags + description: 'List of device tags that should be generated from + + structured configuration. + + ' + type: list + items: + type: dict + keys: + name: + description: 'Tag name to be assigned to generated tags. + + ' + convert_to_lower_case: true + required: true + type: str + data_path: + description: 'Structured config field/key path to be used to find the value + for the tag. Dot notation is supported to reference values inside dictionaries. + + For Example: ''data_path: router_bfd.multihop.interval'' would set the + tag with the value of the interval for multihop bfd. If this value is + not specified in the structured config, the tag is not created. + + ' + required: true + type: str + cv_tags_device_custom: + documentation_options: + table: cloudvision-tags + description: 'List of user defined tags and their values to be applied to this + + device on CVP. + + ' + type: list + items: + type: dict + keys: + name: + description: 'Tag name to be assigned to generated tag. + + ' + required: true + type: str + convert_to_lower_case: true + value: + description: 'Value to be assigned to the tag. + + ' + required: true + type: str cv_topology: documentation_options: table: cv-topology @@ -1537,6 +1644,16 @@ keys: vtep_loopback_interface: type: str description: Path to Custom J2 template. + cv_tags_topology_type: + description: 'Type that CloudVision should use when generating the Topology. + + ' + type: str + valid_values: + - leaf + - spine + - core + - edge only_local_vlan_trunk_groups: documentation_options: table: fabric-settings diff --git a/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/cv_tags.schema.yml b/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/cv_tags.schema.yml new file mode 100644 index 00000000000..3636346bf68 --- /dev/null +++ b/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/cv_tags.schema.yml @@ -0,0 +1,93 @@ +# Copyright (c) 2023 Arista Networks, Inc. +# Use of this source code is governed by the Apache License 2.0 +# that can be found in the LICENSE file. +# yaml-language-server: $schema=../../../../plugins/plugin_utils/schema/avd_meta_schema.json +# Line above is used by RedHat's YAML Schema vscode extension +# Use Ctrl + Space to get suggestions for every field. Autocomplete will pop up after typing 2 letters. +type: dict +keys: + cv_tags_enabled: + documentation_options: + table: cloudvision-tags + type: bool + default: False + description: | + Enable the generation of CloudVision tags that can be applied using the `cloudvision` role. If not defined, the value is assumed to be False and no tags are generated. + cv_tags_topology_type: + documentation_options: + table: cloudvision-tags + description: | + Device type that CloudVision should use when generating the Topology. + type: str + valid_values: + - "leaf" + - "spine" + - "core" + - "edge" + cv_tags_generate_interface: + documentation_options: + table: cloudvision-tags + description: | + List of interface tags that should be generated from + structured configuration. + type: list + items: + type: dict + keys: + name: + description: | + Tag name to be assigned to generated tags. + convert_to_lower_case: true + required: true + type: str + data_path: + description: | + Structured config field/key path to be used to find the value for the tag. Dot notation is supported to reference values inside dictionaries. + For Example: 'data_path: channel_group.id' would set the tag with the value of the channel id of the interface. If there is no channel id, the tag is not created. + required: true + type: str + + cv_tags_generate_device: + documentation_options: + table: cloudvision-tags + description: | + List of device tags that should be generated from + structured configuration. + type: list + items: + type: dict + keys: + name: + description: | + Tag name to be assigned to generated tags. + convert_to_lower_case: true + required: true + type: str + data_path: + description: | + Structured config field/key path to be used to find the value for the tag. Dot notation is supported to reference values inside dictionaries. + For Example: 'data_path: router_bfd.multihop.interval' would set the tag with the value of the interval for multihop bfd. If this value is not specified in the structured config, the tag is not created. + required: true + type: str + + cv_tags_device_custom: + documentation_options: + table: cloudvision-tags + description: | + List of user defined tags and their values to be applied to this + device on CVP. + type: list + items: + type: dict + keys: + name: + description: | + Tag name to be assigned to generated tag. + required: true + type: str + convert_to_lower_case: true + value: + description: | + Value to be assigned to the tag. + required: true + type: str diff --git a/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/node_type_keys.schema.yml b/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/node_type_keys.schema.yml index a717126be87..e924f68a133 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/node_type_keys.schema.yml +++ b/ansible_collections/arista/avd/roles/eos_designs/schemas/schema_fragments/node_type_keys.schema.yml @@ -225,3 +225,12 @@ keys: vtep_loopback_interface: type: str description: Path to Custom J2 template. + cv_tags_topology_type: + description: | + Type that CloudVision should use when generating the Topology. + type: str + valid_values: + - "leaf" + - "spine" + - "core" + - "edge" diff --git a/mkdocs.yml b/mkdocs.yml index 5c832ab6046..98c4f7ba22f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -146,6 +146,7 @@ nav: - Configuring PTP: roles/eos_designs/docs/how-to/ptp.md - Custom Structured Configuration: roles/eos_designs/docs/how-to/custom-structured-configuration.md - Custom Templates: roles/eos_designs/docs/how-to/custom-templates.md + - CloudVision Tags: roles/eos_designs/docs/how-to/cloudvision-tags.md - eos_cli_config_gen: - Overview: roles/eos_cli_config_gen/README.md - Role Configuration: roles/eos_cli_config_gen/docs/role-configuration.md @@ -153,6 +154,7 @@ nav: - How-To Guides: - Custom Templates: roles/eos_cli_config_gen/docs/how-to/custom-templates.md - cvp_configlet_upload: roles/cvp_configlet_upload/README.md + - cloudvision: roles/cloudvision/README.md - eos_config_deploy_cvp: - Overview: roles/eos_config_deploy_cvp/README.md - Integrating AVD with CVaaS: roles/eos_config_deploy_cvp/docs/avd-to-cvaas.md