From 824bbf977d303a7e8fc3612f1763162fb85fe06f Mon Sep 17 00:00:00 2001 From: Claus Holbech Date: Fri, 1 Nov 2024 22:19:38 +0100 Subject: [PATCH] Refactoring more in shared_utils --- .../intended/structured_configs/DC1-BL1A.yml | 24 +++--- .../intended/structured_configs/DC1-BL1B.yml | 24 +++--- .../structured_configs/DC1-LEAF2A.yml | 12 +-- .../structured_configs/DC1-LEAF2B.yml | 12 +-- .../intended/structured_configs/DC1-SVC3A.yml | 12 +-- .../intended/structured_configs/DC1-SVC3B.yml | 12 +-- .../DC1_UNDEPLOYED_LEAF1A.yml | 12 +-- .../DC1_UNDEPLOYED_LEAF1B.yml | 12 +-- .../cv-pathfinder-edge1.yml | 34 ++++---- .../evpn_services_l2_only_false.yml | 12 +-- .../intended/structured_configs/ipv4-acls.yml | 60 +++++++------- .../eos_designs/docs/tables/evpn-settings.md | 6 +- .../docs/tables/overlay-settings.md | 14 ++-- .../_eos_designs/eos_designs_facts/mlag.py | 2 +- .../pyavd/_eos_designs/schema/__init__.py | 41 +++++----- .../schema/eos_designs.schema.yml | 18 +++-- .../fabric_evpn_encapsulation.schema.yml | 3 +- .../overlay_rd_type.schema.yml | 11 ++- .../overlay_rt_type.schema.yml | 1 - .../shared_utils/interface_descriptions.py | 2 - .../shared_utils/ip_addressing.py | 6 -- .../shared_utils/l3_interfaces.py | 20 ++--- .../pyavd/_eos_designs/shared_utils/mgmt.py | 10 +-- .../pyavd/_eos_designs/shared_utils/misc.py | 79 ++++++++----------- .../pyavd/_eos_designs/shared_utils/mlag.py | 4 - .../_eos_designs/shared_utils/node_type.py | 10 +-- .../_eos_designs/shared_utils/overlay.py | 41 ++-------- .../structured_config/base/__init__.py | 19 +++-- .../structured_config/mlag/__init__.py | 18 ++--- .../network_services/ip_access_lists.py | 10 +-- .../network_services/router_bgp.py | 2 +- .../network_services/utils.py | 39 +++------ .../network_services/utils_wan.py | 8 +- .../network_services/vlan_interfaces.py | 2 +- .../network_services/vlans.py | 6 +- .../network_services/vrfs.py | 2 +- .../underlay/loopback_interfaces.py | 4 +- .../underlay/prefix_lists.py | 4 +- .../structured_config/underlay/utils.py | 4 +- 39 files changed, 274 insertions(+), 338 deletions(-) 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 42c6bae9f27..fe1d37c0bf5 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 @@ -650,32 +650,32 @@ vlans: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet12 entries: - - sequence: 15 + - source: any + destination: 10.10.40.10 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.10.40.10 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet13.10 entries: - - sequence: 15 + - source: any + destination: 10.10.40.20 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.10.40.20 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet12 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.10.40.10 + - source: 10.10.40.10 destination: any + action: permit + protocol: ip - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet13.10 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.10.40.20 + - source: 10.10.40.20 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true ip_virtual_router_mac_address: 00:dc:00:00:00:0a diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1B.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1B.yml index 3d9f6374479..76f7fe9657a 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1B.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-BL1B.yml @@ -618,32 +618,32 @@ vlans: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet12 entries: - - sequence: 15 + - source: any + destination: 10.10.50.10 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.10.50.10 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet13.10 entries: - - sequence: 15 + - source: any + destination: 10.10.50.20 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.10.50.20 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet12 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.10.50.10 + - source: 10.10.50.10 destination: any + action: permit + protocol: ip - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet13.10 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.10.50.20 + - source: 10.10.50.20 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true ip_virtual_router_mac_address: 00:dc:00:00:00:0a diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2A.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2A.yml index e90041c1fc0..bba5dc58d5a 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2A.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2A.yml @@ -978,18 +978,18 @@ vlans: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2B.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2B.yml index 943e4430946..3dec98776f7 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2B.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-LEAF2B.yml @@ -923,18 +923,18 @@ vlans: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3A.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3A.yml index 3cfe854346c..3d967604efc 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3A.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3A.yml @@ -1679,18 +1679,18 @@ router_bfd: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3B.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3B.yml index 552f4f9aaba..cd71fb1db32 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3B.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1-SVC3B.yml @@ -1628,18 +1628,18 @@ router_bfd: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1A.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1A.yml index d73f5a5d68b..13ffd40524e 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1A.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1A.yml @@ -1009,18 +1009,18 @@ router_bfd: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1B.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1B.yml index 454390c9b91..c6ebe9bcda8 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1B.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/DC1_UNDEPLOYED_LEAF1B.yml @@ -1009,18 +1009,18 @@ router_bfd: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/cv-pathfinder-edge1.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/cv-pathfinder-edge1.yml index 9c107d146e2..646a2abbf98 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/cv-pathfinder-edge1.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/cv-pathfinder-edge1.yml @@ -365,39 +365,39 @@ agents: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet1_49.3 entries: - - sequence: 15 + - source: any + destination: 172.24.49.3 + sequence: 15 action: deny protocol: ip - source: any + - source: 172.24.49.2 destination: 172.24.49.3 - - action: permit + action: permit protocol: ip - source: 172.24.49.2 - destination: 172.24.49.3 - name: ACL-NAT-IE-DIRECT entries: - - sequence: 10 + - source: any + destination: 5.0.0.0/24 + sequence: 10 action: deny protocol: ip - source: any - destination: 5.0.0.0/24 - - sequence: 20 + - source: any + destination: any + sequence: 20 action: permit protocol: ip - source: any - destination: any - name: ACL-NAT-IE-ZSCALER entries: - - sequence: 10 + - source: any + destination: 10.0.0.0/24 + sequence: 10 action: permit protocol: ip - source: any - destination: 10.0.0.0/24 - - sequence: 20 + - source: any + destination: any + sequence: 20 action: deny protocol: ip - source: any - destination: any ip_extcommunity_lists: - name: ECL-EVPN-SOO entries: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/evpn_services_l2_only_false.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/evpn_services_l2_only_false.yml index 2055b251e79..1c9f99e5f53 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/evpn_services_l2_only_false.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/evpn_services_l2_only_false.yml @@ -666,18 +666,18 @@ vlans: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Vlan110 entries: - - sequence: 15 + - source: any + destination: 10.1.10.1 + sequence: 15 action: deny protocol: ip - source: any - destination: 10.1.10.1 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Vlan110 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 10.1.10.1 + - source: 10.1.10.1 destination: any + action: permit + protocol: ip ip_igmp_snooping: globally_enabled: true vlans: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/ipv4-acls.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/ipv4-acls.yml index 07a575b0515..611066ee156 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/ipv4-acls.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/ipv4-acls.yml @@ -134,70 +134,70 @@ route_maps: ip_access_lists: - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet1 entries: - - sequence: 15 + - source: any + destination: 172.16.0.2 + sequence: 15 action: deny protocol: ip - source: any + - source: 172.16.0.1/30 destination: 172.16.0.2 - - action: permit + action: permit protocol: ip - source: 172.16.0.1/30 - destination: 172.16.0.2 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet2 entries: - - sequence: 15 + - source: any + destination: 172.17.17.17 + sequence: 15 action: deny protocol: ip - source: any + - source: 172.17.17.1 destination: 172.17.17.17 - - action: permit + action: permit protocol: ip - source: 172.17.17.1 - destination: 172.17.17.17 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-IN_Ethernet5_1 entries: - - sequence: 15 + - source: any + destination: 172.20.20.0 + sequence: 15 action: deny protocol: ip - source: any + - source: 172.20.20.1/30 destination: 172.20.20.0 - - action: permit + action: permit protocol: ip - source: 172.20.20.1/30 - destination: 172.20.20.0 - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet1 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 172.16.0.2 + - source: 172.16.0.2 destination: any + action: permit + protocol: ip - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet2 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 172.17.17.17 + - source: 172.17.17.17 destination: any + action: permit + protocol: ip - name: TEST-IPV4-ACL-WITH-IP-FIELDS-OUT_Ethernet6_6.6 entries: - remark: Some remark will not require source and destination fields. - - action: permit - protocol: ip - source: 172.21.21.0 + - source: 172.21.21.0 destination: any + action: permit + protocol: ip - name: TEST-IPV4-ACL-WITH-NO-FIELDS-IN entries: - - action: permit - protocol: ip - source: 172.18.18.18 + - source: 172.18.18.18 destination: any + action: permit + protocol: ip - name: TEST-IPV4-ACL-WITH-NO-FIELDS-OUT entries: - - action: permit - protocol: ip - source: 172.18.18.18 + - source: 172.18.18.18 destination: any + action: permit + protocol: ip router_bfd: multihop: interval: 300 diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/evpn-settings.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/evpn-settings.md index 1ed5abe5f14..15905d32d6b 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/evpn-settings.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/evpn-settings.md @@ -21,7 +21,7 @@ | [evpn_prevent_readvertise_to_server](## "evpn_prevent_readvertise_to_server") | Boolean | | `False` | | Configure route-map on eBGP sessions towards route-servers, where prefixes with the peer's ASN in the AS Path are filtered away.
This is very useful in large-scale networks, where convergence will be quicker by not returning all updates received
from Route-server-1 to Router-server-2 just for Route-server-2 to throw them away because of AS Path loop detection.
| | [evpn_short_esi_prefix](## "evpn_short_esi_prefix") | String | | `0000:0000:` | | Configure prefix for "short_esi" values. | | [evpn_vlan_aware_bundles](## "evpn_vlan_aware_bundles") | Boolean | | `False` | | Enable VLAN aware bundles for every EVPN MAC-VRF.
If set to `true` all SVIs in a VRF are configured in a vlan-aware-bundle using the VRF name as the bundle name. `l2vlans` are bundled in vlan-aware-bundles using the VLAN name as the bundle name.

The `evpn_vlan_bundle` option under `svis` and `l2vlans` takes precedence and overrides this behavior. Per svi/l2vlan `evpn_vlan_bundle` also works when this setting is disabled which allow mixing vlan-aware-bundles with regular MAC-VRFs. | - | [fabric_evpn_encapsulation](## "fabric_evpn_encapsulation") | String | | `vxlan` | Valid Values:
- vxlan
- mpls | Should be set to mpls for evpn-mpls scenario. | + | [fabric_evpn_encapsulation](## "fabric_evpn_encapsulation") | String | | | Valid Values:
- vxlan
- mpls | Should be set to mpls for evpn-mpls scenario. This overrides the evpn_encapsulation setting under node_type_keys. | === "YAML" @@ -83,6 +83,6 @@ # The `evpn_vlan_bundle` option under `svis` and `l2vlans` takes precedence and overrides this behavior. Per svi/l2vlan `evpn_vlan_bundle` also works when this setting is disabled which allow mixing vlan-aware-bundles with regular MAC-VRFs. evpn_vlan_aware_bundles: - # Should be set to mpls for evpn-mpls scenario. - fabric_evpn_encapsulation: + # Should be set to mpls for evpn-mpls scenario. This overrides the evpn_encapsulation setting under node_type_keys. + fabric_evpn_encapsulation: ``` diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/overlay-settings.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/overlay-settings.md index 95b5ae2f2df..28d3f9d1332 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/overlay-settings.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/overlay-settings.md @@ -17,15 +17,15 @@ | [overlay_mlag_rfc5549](## "overlay_mlag_rfc5549") | Boolean | | `False` | | IPv6 Unnumbered for MLAG iBGP connections.
Requires "underlay_rfc5549: true".
| | [overlay_rd_type](## "overlay_rd_type") | Dictionary | | | | Configuration options for the Administrator subfield (first part of RD) and the Assigned Number subfield (second part of RD).

By default Route Distinguishers (RD) are set to:
- `:` for VLANs and VLAN-Aware Bundles with L2 vlans.
- `:` for VLAN-Aware Bundles with SVIs.
- `:` for VLAN-Aware Bundles defined under 'evpn_vlan_bundles'.
- `:` for VRFs.

Note:
RD is a 48-bit value which is split into <16-bit>:<32-bit> or <32-bit>:<16-bit>.
When using loopback or 32-bit ASN/number the assigned number can only be a 16-bit number. This may be a problem with large VNIs.
For 16-bit ASN/number the assigned number can be a 32-bit number.
| | [  admin_subfield](## "overlay_rd_type.admin_subfield") | String | | `router_id` | | The method for deriving RD Administrator subfield (first part of RD):
- 'router_id' means the IP address of Loopback0.
- 'vtep_loopback' means the IP address of the VTEP loopback interface.
- 'bgp_as' means the AS number of the device.
- 'switch_id' means the 'id' value of the device.
- Any without mask.
- Integer between <0-65535>.
- Integer between <0-4294967295>.
- 'overlay_loopback_ip' means the IP address of Loopback0. (deprecated - use 'router_id' instead)
| - | [  admin_subfield_offset](## "overlay_rd_type.admin_subfield_offset") | String | | | | Offset can only be used if admin_subfield is an integer between <0-4294967295> or 'switch_id'.
Total value of admin_subfield + admin_subfield_offset must be <= 4294967295.
| + | [  admin_subfield_offset](## "overlay_rd_type.admin_subfield_offset") | Integer | | `0` | | Offset can only be used if admin_subfield is an integer between <0-4294967295> or 'switch_id'.
Total value of admin_subfield + admin_subfield_offset must be <= 4294967295.
| | [  vrf_admin_subfield](## "overlay_rd_type.vrf_admin_subfield") | String | | | | The method for deriving RD Administrator subfield (first part of RD) for VRF services:
- 'router_id' means the IP address of Loopback0.
- 'vtep_loopback' means the IP address of the VTEP loopback interface.
- 'bgp_as' means the AS number of the device.
- 'switch_id' means the 'id' value of the device.
- Any without mask.
- Integer between <0-65535>.
- Integer between <0-4294967295>.
- 'overlay_loopback_ip' means the IP address of Loopback0. (deprecated - use 'router_id' instead)

'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used.
| - | [  vrf_admin_subfield_offset](## "overlay_rd_type.vrf_admin_subfield_offset") | String | | | | Offset can only be used if 'vrf_admin_subfield' is an integer between <0-4294967295> or 'switch_id'.
Total value of 'vrf_admin_subfield' + 'vrf_admin_subfield_offset' must be <= 4294967295.
| + | [  vrf_admin_subfield_offset](## "overlay_rd_type.vrf_admin_subfield_offset") | Integer | | | | Offset can only be used if 'vrf_admin_subfield' is an integer between <0-4294967295> or 'switch_id'.
Total value of 'vrf_admin_subfield' + 'vrf_admin_subfield_offset' must be <= 4294967295.

'vrf_admin_subfield_offset' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield_offset' value will be used.
| | [  vlan_assigned_number_subfield](## "overlay_rd_type.vlan_assigned_number_subfield") | String | | `mac_vrf_id` | Valid Values:
- mac_vrf_id
- mac_vrf_vni
- vlan_id | The method for deriving RD Assigned Number subfield for VLAN services (second part of RD):
- 'mac_vrf_id' means `(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id`.
- 'mac_vrf_vni' means `(mac_vrf_vni_base or mac_vrf_id_base) + vlan_id`.
- 'vlan_id' will only use the 'vlan_id' and ignores all base values.

These methods can be overridden per VLAN if either 'rd_override', 'rt_override' or 'vni_override' is set (preferred in this order).
| | [overlay_routing_protocol](## "overlay_routing_protocol") | String | | `ebgp` | Value is converted to lower case.
Valid Values:
- ebgp
- ibgp
- cvx
- her
- none | - The following overlay routing protocols are supported:
- eBGP: Configures fabric with eBGP, default for l3ls-evpn design.
- iBGP: Configured fabric with iBGP, only supported with OSPF or ISIS variants in underlay, default for mpls design.
- CVX: Configures fabric to leverage CloudVision eXchange as the overlay controller.
- HER: Configures fabric with Head-End Replication, configures static VXLAN flood-lists instead of using a dynamic overlay protocol.
- none: No overlay configuration will be generated, default for l2ls design.
| | [overlay_routing_protocol_address_family](## "overlay_routing_protocol_address_family") | String | | `ipv4` | Valid Values:
- ipv4
- ipv6 | When set to `ipv6`, enable overlay EVPN peering with IPv6 addresses.
This feature depends on underlay_ipv6 variable. As of today, only RFC5549 is capable to transport IPv6 in the underlay.
| | [overlay_rt_type](## "overlay_rt_type") | Dictionary | | | | Configuration options for the Administrator subfield (first part of RT) and the Assigned Number subfield (second part of RT).

By default Route Targets (RT) are set to:
- `<(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id>:<(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id>` for VLANs and VLAN-Aware Bundles with L2 vlans.
- `:` for VLAN-Aware Bundles with SVIs.
- `:` for VLAN-Aware Bundles defined under 'evpn_vlan_bundles'.
- `:` for VRFs.

Notes:
RT is a 48-bit value which is split into <16-bit>:<32-bit> or <32-bit>:<16-bit>.
When using 32-bit ASN/number the VNI can only be a 16-bit number. Alternatively use vlan_id/vrf_id as assigned number.
For 16-bit ASN/number the assigned number can be a 32-bit number.
| | [  admin_subfield](## "overlay_rt_type.admin_subfield") | String | | `vrf_id` | | The method for deriving RT Administrator subfield (first part of RT):
- 'vrf_id' means `(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id` for VLANs, `(vrf_id or vrf_vni)` for VRFs and `id` for bundles defined under 'evpn_vlan_bundles'.
- 'vrf_vni' means `(mac_vrf_vni_base or mac_vrf_id_base) + vlan_id` for VLANs, `(vrf_vni or vrf_id)` for VRFs and `id` for bundles defined under 'evpn_vlan_bundles'.
- 'id' means `vlan_id` for VLANs, `(vrf_id or vrf_vni)` for VRFs and `id` for bundles defined under 'evpn_vlan_bundles'.
- 'bgp_as' means the AS number of the device.
- Integer between <0-65535>.
- Integer between <0-4294967295>.

The 'vrf_id' and 'vrf_vni' methods can be overridden per VLAN if either 'rt_override' or 'vni_override' is set (preferred in this order).
The 'vrf_id', 'vrf_vni' and 'id' methods can be overridden per bundle defined under `evpn_vlan_bundles` using 'rt_override'.
| - | [  vrf_admin_subfield](## "overlay_rt_type.vrf_admin_subfield") | String | | `vrf_id` | | The method for deriving RT Administrator subfield (first part of RT) for VRF services:
- 'id' means `(vrf_id or vrf_vni)`.
- 'vrf_id' means `(vrf_id or vrf_vni)`.
- 'vrf_vni' means `(vrf_vni or vrf_id)`.
- 'bgp_as' means the AS number of the device.
- Integer between <0-65535>.
- Integer between <0-4294967295>.

'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used.
| + | [  vrf_admin_subfield](## "overlay_rt_type.vrf_admin_subfield") | String | | | | The method for deriving RT Administrator subfield (first part of RT) for VRF services:
- 'id' means `(vrf_id or vrf_vni)`.
- 'vrf_id' means `(vrf_id or vrf_vni)`.
- 'vrf_vni' means `(vrf_vni or vrf_id)`.
- 'bgp_as' means the AS number of the device.
- Integer between <0-65535>.
- Integer between <0-4294967295>.

'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used.
| | [  vlan_assigned_number_subfield](## "overlay_rt_type.vlan_assigned_number_subfield") | String | | `mac_vrf_id` | Valid Values:
- mac_vrf_id
- mac_vrf_vni
- vlan_id | The method for deriving RT Assigned Number subfield for VLAN services (second part of RT):
- 'mac_vrf_id' means `(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id`.
- 'mac_vrf_vni' means `(mac_vrf_vni_base or mac_vrf_id_base) + vlan_id`.
- 'vlan_id' will only use the 'vlan_id' and ignores all base values.

These methods can be overridden per VLAN if either 'rt_override' or 'vni_override' is set (preferred in this order).
| | [router_id_loopback_description](## "router_id_loopback_description") | String | | `ROUTER_ID` | | Customize the description on Router ID interface Loopback0. | | [vtep_loopback_description](## "vtep_loopback_description") | String | | `VXLAN_TUNNEL_SOURCE` | | Customize the description on the VTEP interface, typically Loopback1. | @@ -104,7 +104,7 @@ # Offset can only be used if admin_subfield is an integer between <0-4294967295> or 'switch_id'. # Total value of admin_subfield + admin_subfield_offset must be <= 4294967295. - admin_subfield_offset: + admin_subfield_offset: # The method for deriving RD Administrator subfield (first part of RD) for VRF services: # - 'router_id' means the IP address of Loopback0. @@ -121,7 +121,9 @@ # Offset can only be used if 'vrf_admin_subfield' is an integer between <0-4294967295> or 'switch_id'. # Total value of 'vrf_admin_subfield' + 'vrf_admin_subfield_offset' must be <= 4294967295. - vrf_admin_subfield_offset: + # + # 'vrf_admin_subfield_offset' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield_offset' value will be used. + vrf_admin_subfield_offset: # The method for deriving RD Assigned Number subfield for VLAN services (second part of RD): # - 'mac_vrf_id' means `(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id`. @@ -178,7 +180,7 @@ # - Integer between <0-4294967295>. # # 'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used. - vrf_admin_subfield: + vrf_admin_subfield: # The method for deriving RT Assigned Number subfield for VLAN services (second part of RT): # - 'mac_vrf_id' means `(mac_vrf_id_base or mac_vrf_vni_base) + vlan_id`. diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/mlag.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/mlag.py index 93541b9aac3..e533d7176f2 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/mlag.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/mlag.py @@ -56,7 +56,7 @@ def mlag_l3_ip(self: EosDesignsFacts) -> str | None: if ( self.shared_utils.mlag_l3 and self.shared_utils.mlag_peer_l3_vlan is not None - and not (self.shared_utils.underlay_rfc5549 and self.shared_utils.overlay_mlag_rfc5549) + and not (self.shared_utils.underlay_rfc5549 and self.inputs.overlay_mlag_rfc5549) ): return self.shared_utils.mlag_l3_ip return None diff --git a/python-avd/pyavd/_eos_designs/schema/__init__.py b/python-avd/pyavd/_eos_designs/schema/__init__.py index cd17dc86c13..f3141ab5c1a 100644 --- a/python-avd/pyavd/_eos_designs/schema/__init__.py +++ b/python-avd/pyavd/_eos_designs/schema/__init__.py @@ -8623,9 +8623,9 @@ class OverlayRdType(AvdModel): _fields: ClassVar[dict] = { "_custom_data": {"type": dict}, "admin_subfield": {"type": str, "default": "router_id"}, - "admin_subfield_offset": {"type": str}, + "admin_subfield_offset": {"type": int, "default": 0}, "vrf_admin_subfield": {"type": str}, - "vrf_admin_subfield_offset": {"type": str}, + "vrf_admin_subfield_offset": {"type": int}, "vlan_assigned_number_subfield": {"type": str, "default": "mac_vrf_id"}, } _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -8650,11 +8650,13 @@ class OverlayRdType(AvdModel): Default value: `"router_id"` """ - admin_subfield_offset: str | None + admin_subfield_offset: int """ Offset can only be used if admin_subfield is an integer between <0-4294967295> or 'switch_id'. Total value of admin_subfield + admin_subfield_offset must be <= 4294967295. + + Default value: `0` """ vrf_admin_subfield: str | None """ @@ -8676,10 +8678,12 @@ class OverlayRdType(AvdModel): 'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used. """ - vrf_admin_subfield_offset: str | None + vrf_admin_subfield_offset: int | None """ Offset can only be used if 'vrf_admin_subfield' is an integer between <0-4294967295> or 'switch_id'. Total value of 'vrf_admin_subfield' + 'vrf_admin_subfield_offset' must be <= 4294967295. + 'vrf_admin_subfield_offset' takes precedence for VRF RDs if set. Otherwise the + 'admin_subfield_offset' value will be used. """ vlan_assigned_number_subfield: str """ @@ -8702,9 +8706,9 @@ def __init__( *, _custom_data: dict[str, Any] | UndefinedType = Undefined, admin_subfield: str | UndefinedType = Undefined, - admin_subfield_offset: str | None | UndefinedType = Undefined, + admin_subfield_offset: int | UndefinedType = Undefined, vrf_admin_subfield: str | None | UndefinedType = Undefined, - vrf_admin_subfield_offset: str | None | UndefinedType = Undefined, + vrf_admin_subfield_offset: int | None | UndefinedType = Undefined, vlan_assigned_number_subfield: str | UndefinedType = Undefined, ) -> None: """ @@ -8752,6 +8756,8 @@ def __init__( vrf_admin_subfield_offset: Offset can only be used if 'vrf_admin_subfield' is an integer between <0-4294967295> or 'switch_id'. Total value of 'vrf_admin_subfield' + 'vrf_admin_subfield_offset' must be <= 4294967295. + 'vrf_admin_subfield_offset' takes precedence for VRF RDs if set. Otherwise the + 'admin_subfield_offset' value will be used. vlan_assigned_number_subfield: The method for deriving RD Assigned Number subfield for VLAN services (second part of RD): - @@ -8774,7 +8780,7 @@ class OverlayRtType(AvdModel): _fields: ClassVar[dict] = { "_custom_data": {"type": dict}, "admin_subfield": {"type": str, "default": "vrf_id"}, - "vrf_admin_subfield": {"type": str, "default": "vrf_id"}, + "vrf_admin_subfield": {"type": str}, "vlan_assigned_number_subfield": {"type": str, "default": "mac_vrf_id"}, } _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -8804,7 +8810,7 @@ class OverlayRtType(AvdModel): Default value: `"vrf_id"` """ - vrf_admin_subfield: str + vrf_admin_subfield: str | None """ The method for deriving RT Administrator subfield (first part of RT) for VRF services: - 'id' means @@ -8819,8 +8825,6 @@ class OverlayRtType(AvdModel): 'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used. - - Default value: `"vrf_id"` """ vlan_assigned_number_subfield: str """ @@ -8843,7 +8847,7 @@ def __init__( *, _custom_data: dict[str, Any] | UndefinedType = Undefined, admin_subfield: str | UndefinedType = Undefined, - vrf_admin_subfield: str | UndefinedType = Undefined, + vrf_admin_subfield: str | None | UndefinedType = Undefined, vlan_assigned_number_subfield: str | UndefinedType = Undefined, ) -> None: """ @@ -48530,7 +48534,7 @@ def __init__( "evpn_short_esi_prefix": {"type": str, "default": "0000:0000:"}, "evpn_vlan_aware_bundles": {"type": bool, "default": False}, "evpn_vlan_bundles": {"type": EvpnVlanBundles}, - "fabric_evpn_encapsulation": {"type": str, "default": "vxlan"}, + "fabric_evpn_encapsulation": {"type": str}, "fabric_flow_tracking": {"type": FabricFlowTracking}, "fabric_ip_addressing": {"type": FabricIpAddressing}, "fabric_name": {"type": str}, @@ -49382,11 +49386,10 @@ def __init__( Default value: `False` """ evpn_vlan_bundles: EvpnVlanBundles - fabric_evpn_encapsulation: str + fabric_evpn_encapsulation: str | None """ - Should be set to mpls for evpn-mpls scenario. - - Default value: `"vxlan"` + Should be set to mpls for evpn-mpls scenario. This overrides the evpn_encapsulation setting under + node_type_keys. """ fabric_flow_tracking: FabricFlowTracking """ @@ -50410,7 +50413,7 @@ def __init__( evpn_short_esi_prefix: str | UndefinedType = Undefined, evpn_vlan_aware_bundles: bool | UndefinedType = Undefined, evpn_vlan_bundles: EvpnVlanBundles | UndefinedType = Undefined, - fabric_evpn_encapsulation: str | UndefinedType = Undefined, + fabric_evpn_encapsulation: str | None | UndefinedType = Undefined, fabric_flow_tracking: FabricFlowTracking | UndefinedType = Undefined, fabric_ip_addressing: FabricIpAddressing | UndefinedType = Undefined, fabric_name: str | UndefinedType = Undefined, @@ -50954,7 +50957,9 @@ def __init__( `l2vlans` takes precedence and overrides this behavior. Per svi/l2vlan `evpn_vlan_bundle` also works when this setting is disabled which allow mixing vlan-aware-bundles with regular MAC-VRFs. evpn_vlan_bundles: evpn_vlan_bundles - fabric_evpn_encapsulation: Should be set to mpls for evpn-mpls scenario. + fabric_evpn_encapsulation: + Should be set to mpls for evpn-mpls scenario. This overrides the evpn_encapsulation setting under + node_type_keys. fabric_flow_tracking: Default enabling of flow-tracking(IPFIX) for various interface types across the fabric. Flow diff --git a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml index ee9f706b937..750e94e04e0 100644 --- a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml @@ -1381,11 +1381,11 @@ keys: fabric_evpn_encapsulation: documentation_options: table: evpn-settings - description: Should be set to mpls for evpn-mpls scenario. + description: Should be set to mpls for evpn-mpls scenario. This overrides the + evpn_encapsulation setting under node_type_keys. valid_values: - vxlan - mpls - default: vxlan type: str fabric_flow_tracking: documentation_options: @@ -2894,9 +2894,10 @@ keys: ' default: router_id admin_subfield_offset: - type: str + type: int + default: 0 convert_types: - - int + - str description: 'Offset can only be used if admin_subfield is an integer between <0-4294967295> or ''switch_id''. @@ -2933,15 +2934,19 @@ keys: ' vrf_admin_subfield_offset: - type: str + type: int convert_types: - - int + - str description: 'Offset can only be used if ''vrf_admin_subfield'' is an integer between <0-4294967295> or ''switch_id''. Total value of ''vrf_admin_subfield'' + ''vrf_admin_subfield_offset'' must be <= 4294967295. + + ''vrf_admin_subfield_offset'' takes precedence for VRF RDs if set. Otherwise + the ''admin_subfield_offset'' value will be used. + ' vlan_assigned_number_subfield: type: str @@ -3087,7 +3092,6 @@ keys: ''admin_subfield'' value will be used. ' - default: vrf_id vlan_assigned_number_subfield: type: str valid_values: diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/fabric_evpn_encapsulation.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/fabric_evpn_encapsulation.schema.yml index 53be7739a81..6bba5d2328d 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/fabric_evpn_encapsulation.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/fabric_evpn_encapsulation.schema.yml @@ -9,7 +9,6 @@ keys: fabric_evpn_encapsulation: documentation_options: table: evpn-settings - description: Should be set to mpls for evpn-mpls scenario. + description: Should be set to mpls for evpn-mpls scenario. This overrides the evpn_encapsulation setting under node_type_keys. valid_values: ["vxlan", "mpls"] - default: "vxlan" type: str diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rd_type.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rd_type.schema.yml index 5df3c89f969..75cc0f0d656 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rd_type.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rd_type.schema.yml @@ -40,9 +40,10 @@ keys: - 'overlay_loopback_ip' means the IP address of Loopback0. (deprecated - use 'router_id' instead) default: router_id admin_subfield_offset: - type: str + type: int + default: 0 convert_types: - - int + - str description: | Offset can only be used if admin_subfield is an integer between <0-4294967295> or 'switch_id'. Total value of admin_subfield + admin_subfield_offset must be <= 4294967295. @@ -63,12 +64,14 @@ keys: 'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used. vrf_admin_subfield_offset: - type: str + type: int convert_types: - - int + - str description: | Offset can only be used if 'vrf_admin_subfield' is an integer between <0-4294967295> or 'switch_id'. Total value of 'vrf_admin_subfield' + 'vrf_admin_subfield_offset' must be <= 4294967295. + + 'vrf_admin_subfield_offset' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield_offset' value will be used. vlan_assigned_number_subfield: type: str valid_values: diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rt_type.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rt_type.schema.yml index 8dfba945b9f..4a12c6006c2 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rt_type.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/overlay_rt_type.schema.yml @@ -54,7 +54,6 @@ keys: - Integer between <0-4294967295>. 'vrf_admin_subfield' takes precedence for VRF RDs if set. Otherwise the 'admin_subfield' value will be used. - default: vrf_id vlan_assigned_number_subfield: type: str valid_values: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/interface_descriptions.py b/python-avd/pyavd/_eos_designs/shared_utils/interface_descriptions.py index f5625f47717..7c044e30148 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/interface_descriptions.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/interface_descriptions.py @@ -12,8 +12,6 @@ if TYPE_CHECKING: from . import SharedUtils -DEFAULT_AVD_INTERFACE_DESCRIPTIONS_PYTHON_CLASS_NAME = "AvdInterfaceDescriptions" - class InterfaceDescriptionsMixin: """ diff --git a/python-avd/pyavd/_eos_designs/shared_utils/ip_addressing.py b/python-avd/pyavd/_eos_designs/shared_utils/ip_addressing.py index 08ce6ade94a..843b2e5e24d 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/ip_addressing.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/ip_addressing.py @@ -12,8 +12,6 @@ if TYPE_CHECKING: from . import SharedUtils -DEFAULT_AVD_IP_ADDRESSING_PYTHON_CLASS_NAME = "AvdIpAddressing" - class IpAddressingMixin: """ @@ -69,10 +67,6 @@ def vtep_ip(self: SharedUtils) -> str: return self.ip_addressing.vtep_ip() - @cached_property - def vtep_vvtep_ip(self: SharedUtils) -> str | None: - return get(self.hostvars, "vtep_vvtep_ip") - @cached_property def ip_addressing(self: SharedUtils) -> AvdIpAddressing: """ diff --git a/python-avd/pyavd/_eos_designs/shared_utils/l3_interfaces.py b/python-avd/pyavd/_eos_designs/shared_utils/l3_interfaces.py index 1d4a8f415d9..0d5e0f8ce0c 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/l3_interfaces.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/l3_interfaces.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get, get_item, merge +from pyavd._utils import get, merge from pyavd.api.interface_descriptions import InterfaceDescriptionData if TYPE_CHECKING: @@ -37,27 +37,23 @@ def apply_l3_interfaces_profile(self: SharedUtils, l3_interface: dict) -> dict: # Nothing to do return l3_interface - msg = f"Profile '{l3_interface['profile']}' applied under l3_interface '{l3_interface['name']}' does not exist in `l3_interface_profiles`." - profile = get_item(self.l3_interface_profiles, "profile", l3_interface["profile"], required=True, custom_error_msg=msg) + if l3_interface["profile"] not in self.inputs.l3_interface_profiles: + msg = f"Profile '{l3_interface['profile']}' applied under l3_interface '{l3_interface['name']}' does not exist in `l3_interface_profiles`." + raise AristaAvdInvalidInputsError(msg) + + profile = self.inputs.l3_interface_profiles[l3_interface["profile"]]._as_dict() merged_dict: dict = merge(profile, l3_interface, list_merge="replace", destructive_merge=False) merged_dict.pop("profile", None) return merged_dict - @cached_property - def l3_interface_profiles(self: SharedUtils) -> list: - return get(self.hostvars, "l3_interface_profiles", default=[]) - @cached_property def l3_interfaces(self: SharedUtils) -> list: """Returns the list of l3_interfaces, where any referenced profiles are applied.""" if not (l3_interfaces := get(self.switch_data_combined, "l3_interfaces")): return [] - # Apply l3_interfaces._profile if set. - if self.l3_interface_profiles: - l3_interfaces = [self.apply_l3_interfaces_profile(l3_interface) for l3_interface in l3_interfaces] - - return l3_interfaces + # Return after applying profile from l3_interfaces_profiles if set. + return [self.apply_l3_interfaces_profile(l3_interface) for l3_interface in l3_interfaces] @cached_property def l3_interfaces_bgp_neighbors(self: SharedUtils) -> list: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py b/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py index 5ef29601f15..cf131a83c5e 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py @@ -34,7 +34,7 @@ def mgmt_interface(self: SharedUtils) -> str | None: return default( get(self.switch_data_combined, "mgmt_interface"), self.platform_settings.get("management_interface"), - get(self.hostvars, "mgmt_interface"), + self.inputs._get("mgmt_interface", None), # Notice that we actually have a default value here, but the precedence order would break if we use it. get(self.cv_topology_config, "mgmt_interface"), "Management1", ) @@ -49,15 +49,15 @@ def mgmt_ip(self: SharedUtils) -> str | None: @cached_property def mgmt_interface_vrf(self: SharedUtils) -> str: - return get(self.hostvars, "mgmt_interface_vrf", default="MGMT") + return self.inputs.mgmt_interface_vrf @cached_property def mgmt_gateway(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "mgmt_gateway", default=get(self.hostvars, "mgmt_gateway")) + return get(self.switch_data_combined, "mgmt_gateway", default=self.inputs.mgmt_gateway) @cached_property def ipv6_mgmt_gateway(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "ipv6_mgmt_gateway", default=get(self.hostvars, "ipv6_mgmt_gateway")) + return get(self.switch_data_combined, "ipv6_mgmt_gateway", default=self.inputs.ipv6_mgmt_gateway) @cached_property def default_mgmt_method(self: SharedUtils) -> str | None: @@ -66,7 +66,7 @@ def default_mgmt_method(self: SharedUtils) -> str | None: The check for 'inband_mgmt_interface' relies on other indirect checks done in that code. """ - default_mgmt_method = get(self.hostvars, "default_mgmt_method", default="oob") + default_mgmt_method = self.inputs.default_mgmt_method if default_mgmt_method == "oob": if (self.mgmt_ip is None) and (self.ipv6_mgmt_ip is None): msg = "'default_mgmt_method: oob' requires either 'mgmt_ip' or 'ipv6_mgmt_ip' to bet set." diff --git a/python-avd/pyavd/_eos_designs/shared_utils/misc.py b/python-avd/pyavd/_eos_designs/shared_utils/misc.py index ca00b4659be..f68ac7f6e33 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/misc.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/misc.py @@ -3,16 +3,16 @@ # that can be found in the LICENSE file. from __future__ import annotations -from copy import deepcopy from functools import cached_property from typing import TYPE_CHECKING, Any -from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError +from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError, AristaAvdMissingVariableError from pyavd._utils import default, get -from pyavd.j2filters import natural_sort, range_expand +from pyavd.j2filters import range_expand if TYPE_CHECKING: from pyavd._eos_designs.eos_designs_facts import EosDesignsFacts + from pyavd._eos_designs.schema import EosDesigns from . import SharedUtils @@ -32,21 +32,13 @@ def all_fabric_devices(self: SharedUtils) -> list[str]: @cached_property def hostname(self: SharedUtils) -> str: - """Hostname set based on inventory_hostname variable.""" + """Hostname set based on inventory_hostname variable. TODO: Get a proper attribute on the class instead of gleaning from the regular inputs.""" return get(self.hostvars, "inventory_hostname", required=True) @cached_property def id(self: SharedUtils) -> int | None: return get(self.switch_data_combined, "id") - @cached_property - def trunk_groups(self: SharedUtils) -> dict: - return { - "mlag": {"name": get(self.hostvars, "trunk_groups.mlag.name", default="MLAG")}, - "mlag_l3": {"name": get(self.hostvars, "trunk_groups.mlag_l3.name", default="MLAG")}, - "uplink": {"name": get(self.hostvars, "trunk_groups.uplink.name", default="UPLINK")}, - } - @cached_property def filter_only_vlans_in_use(self: SharedUtils) -> bool: return get(self.switch_data_combined, "filter.only_vlans_in_use", default=False) @@ -77,12 +69,12 @@ def always_include_vrfs_in_tenants(self: SharedUtils) -> list: @cached_property def igmp_snooping_enabled(self: SharedUtils) -> bool: - default_igmp_snooping_enabled = get(self.hostvars, "default_igmp_snooping_enabled", default=True) + default_igmp_snooping_enabled = self.inputs.default_igmp_snooping_enabled return get(self.switch_data_combined, "igmp_snooping_enabled", default=default_igmp_snooping_enabled) is True @cached_property def only_local_vlan_trunk_groups(self: SharedUtils) -> bool: - return self.inputs.enable_trunk_groups and get(self.hostvars, "only_local_vlan_trunk_groups", default=False) + return self.inputs.enable_trunk_groups and self.inputs.only_local_vlan_trunk_groups @cached_property def system_mac_address(self: SharedUtils) -> str | None: @@ -93,7 +85,7 @@ def system_mac_address(self: SharedUtils) -> str | None: Fabric Topology data model system_mac_address -> Host variable var system_mac_address ->. """ - return default(get(self.switch_data_combined, "system_mac_address"), get(self.hostvars, "system_mac_address")) + return default(get(self.switch_data_combined, "system_mac_address"), self.inputs.system_mac_address) @cached_property def uplink_switches(self: SharedUtils) -> list: @@ -190,25 +182,16 @@ def p2p_uplinks_mtu(self: SharedUtils) -> int | None: @cached_property def fabric_name(self: SharedUtils) -> str: - return get(self.hostvars, "fabric_name", required=True) + if not self.inputs.fabric_name: + msg = "fabric_name" + raise AristaAvdMissingVariableError(msg) + + return self.inputs.fabric_name @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]: - """ - Return sorted network_services_keys filtered for invalid entries and unused keys. - - NOTE: This method is called _before_ any schema validation, since we need to resolve network_services_keys dynamically - """ - # Reading default value from schema - default_network_services_keys = self.schema.get_default_value(["network_services_keys"]) - network_services_keys = get(self.hostvars, "network_services_keys", default=default_network_services_keys) - network_services_keys = [entry for entry in network_services_keys if entry.get("name") is not None and self.hostvars.get(entry["name"]) is not None] - return natural_sort(network_services_keys, "name") - @cached_property def uplink_interface_speed(self: SharedUtils) -> str | None: return get(self.switch_data_combined, "uplink_interface_speed") @@ -249,35 +232,41 @@ def get_switch_fact(self: SharedUtils, key: str, required: bool = True) -> Any: def evpn_multicast(self: SharedUtils) -> bool: return self.get_switch_fact("evpn_multicast", required=False) is True - @cached_property - def ipv4_acls(self: SharedUtils) -> dict: - return {acl["name"]: acl for acl in get(self.hostvars, "ipv4_acls", default=[])} - - def get_ipv4_acl(self: SharedUtils, name: str, interface_name: str, *, interface_ip: str | None = None, peer_ip: str | None = None) -> dict: + def get_ipv4_acl( + self: SharedUtils, name: str, interface_name: str, *, interface_ip: str | None = None, peer_ip: str | None = None + ) -> EosDesigns.Ipv4AclsItem: """ Get one IPv4 ACL from "ipv4_acls" where fields have been substituted. If any substitution is done, the ACL name will get "_" appended. """ - org_ipv4_acl = get(self.ipv4_acls, name, required=True, org_key=f"ipv4_acls[name={name}]") + if name not in self.inputs.ipv4_acls: + msg = f"ipv4_acls[name={name}]" + raise AristaAvdMissingVariableError(msg) + org_ipv4_acl = self.inputs.ipv4_acls[name] # deepcopy to avoid inplace updates below from modifying the original. - ipv4_acl = deepcopy(org_ipv4_acl) + ipv4_acl = org_ipv4_acl._deepcopy() ip_replacements = { "interface_ip": interface_ip, "peer_ip": peer_ip, } - for index, entry in enumerate(get(ipv4_acl, "entries", default=[])): - if entry.get("remark") is not None: + for index, entry in enumerate(ipv4_acl.entries): + if entry._get("remark"): continue err_context = f"ipv4_acls[name={name}].entries[{index}]" - source_field = get(entry, "source", required=True, org_key=f"{err_context}.source") - destination_field = get(entry, "destination", required=True, org_key=f"{err_context}.destination") - entry["source"] = self._get_ipv4_acl_field_with_substitution(source_field, ip_replacements, f"{err_context}.source", interface_name) - entry["destination"] = self._get_ipv4_acl_field_with_substitution(destination_field, ip_replacements, f"{err_context}.destination", interface_name) + if not entry.source: + msg = f"{err_context}.source" + raise AristaAvdMissingVariableError + if not entry.destination: + msg = f"{err_context}.destination" + raise AristaAvdMissingVariableError + + entry.source = self._get_ipv4_acl_field_with_substitution(entry.source, ip_replacements, f"{err_context}.source", interface_name) + entry.destination = self._get_ipv4_acl_field_with_substitution(entry.destination, ip_replacements, f"{err_context}.destination", interface_name) if ipv4_acl != org_ipv4_acl: - ipv4_acl["name"] += f"_{self.sanitize_interface_name(interface_name)}" + ipv4_acl.name += f"_{self.sanitize_interface_name(interface_name)}" return ipv4_acl @staticmethod @@ -308,7 +297,3 @@ def _get_ipv4_acl_field_with_substitution(field_value: str, replacements: dict[s return value return field_value - - @cached_property - def ipv4_prefix_list_catalog(self: SharedUtils) -> list: - return get(self.hostvars, "ipv4_prefix_list_catalog", default=[]) diff --git a/python-avd/pyavd/_eos_designs/shared_utils/mlag.py b/python-avd/pyavd/_eos_designs/shared_utils/mlag.py index 756fde1cec7..0b2c8b01cc7 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/mlag.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/mlag.py @@ -210,7 +210,3 @@ def mlag_peer_ibgp_ip(self: SharedUtils) -> str: return self.mlag_peer_l3_ip return self.mlag_peer_ip - - @cached_property - def mlag_ibgp_peering_vrfs_base_vlan(self: SharedUtils) -> int: - return int(get(self.hostvars, "mlag_ibgp_peering_vrfs.base_vlan", default=3000)) diff --git a/python-avd/pyavd/_eos_designs/shared_utils/node_type.py b/python-avd/pyavd/_eos_designs/shared_utils/node_type.py index 86caae641a8..884fb808027 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/node_type.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/node_type.py @@ -25,7 +25,7 @@ class NodeTypeMixin: @cached_property def type(self: SharedUtils) -> str: """Type fact set based on type variable.""" - if (node_type := get(self.hostvars, "type")) is not None: + if (node_type := self.inputs.type) is not None: return node_type if self.default_node_type: return self.default_node_type @@ -36,12 +36,10 @@ def type(self: SharedUtils) -> str: @cached_property def default_node_type(self: SharedUtils) -> str | None: """default_node_type set based on hostname, returning first node type matching a regex in default_node_types.""" - default_node_types = get(self.hostvars, "default_node_types", default=[]) - - for default_node_type in default_node_types: - for hostname_regex in default_node_type["match_hostnames"]: + for default_node_type in self.inputs.default_node_types: + for hostname_regex in default_node_type.match_hostnames: if search(f"^{hostname_regex}$", self.hostname): - return default_node_type["node_type"] + return default_node_type.node_type return None diff --git a/python-avd/pyavd/_eos_designs/shared_utils/overlay.py b/python-avd/pyavd/_eos_designs/shared_utils/overlay.py index 675f504ddf4..1dfafb183ee 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/overlay.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/overlay.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError -from pyavd._utils import get +from pyavd._utils import default, get if TYPE_CHECKING: from . import SharedUtils @@ -43,39 +43,16 @@ def mpls_overlay_role(self: SharedUtils) -> str | None: return get(self.switch_data_combined, "mpls_overlay_role", default=default_mpls_overlay_role) return None - @cached_property - def overlay_rd_type(self: SharedUtils) -> dict: - overlay_rd_type = get(self.hostvars, "overlay_rd_type", default={}) - admin_subfield = get(overlay_rd_type, "admin_subfield", default="router_id") - admin_subfield_offset = int(get(overlay_rd_type, "admin_subfield_offset", default=0)) - return { - "admin_subfield": admin_subfield, - "admin_subfield_offset": admin_subfield_offset, - "vrf_admin_subfield": get(overlay_rd_type, "vrf_admin_subfield", default=admin_subfield), - "vrf_admin_subfield_offset": int(get(overlay_rd_type, "vrf_admin_subfield_offset", default=admin_subfield_offset)), - "vlan_assigned_number_subfield": get(overlay_rd_type, "vlan_assigned_number_subfield", default="mac_vrf_id"), - } - - @cached_property - def overlay_rt_type(self: SharedUtils) -> dict: - overlay_rt_type = get(self.hostvars, "overlay_rt_type", default={}) - admin_subfield = get(overlay_rt_type, "admin_subfield", default="vrf_id") - return { - "admin_subfield": admin_subfield, - "vrf_admin_subfield": get(overlay_rt_type, "vrf_admin_subfield", default=admin_subfield), - "vlan_assigned_number_subfield": get(overlay_rt_type, "vlan_assigned_number_subfield", default="mac_vrf_id"), - } - @cached_property def overlay_rd_type_admin_subfield(self: SharedUtils) -> str: - admin_subfield = self.overlay_rd_type["admin_subfield"] - admin_subfield_offset = self.overlay_rd_type["admin_subfield_offset"] + admin_subfield = self.inputs.overlay_rd_type.admin_subfield + admin_subfield_offset = self.inputs.overlay_rd_type.admin_subfield_offset return self.get_rd_admin_subfield_value(admin_subfield, admin_subfield_offset) @cached_property def overlay_rd_type_vrf_admin_subfield(self: SharedUtils) -> str: - vrf_admin_subfield = self.overlay_rd_type["vrf_admin_subfield"] - vrf_admin_subfield_offset = self.overlay_rd_type["vrf_admin_subfield_offset"] + vrf_admin_subfield: str = default(self.inputs.overlay_rd_type.vrf_admin_subfield, self.inputs.overlay_rd_type.admin_subfield) + vrf_admin_subfield_offset: int = default(self.inputs.overlay_rd_type.vrf_admin_subfield_offset, self.inputs.overlay_rd_type.admin_subfield_offset) return self.get_rd_admin_subfield_value(vrf_admin_subfield, vrf_admin_subfield_offset) def get_rd_admin_subfield_value(self: SharedUtils, admin_subfield: str, admin_subfield_offset: int) -> str: @@ -118,7 +95,7 @@ def evpn_gateway_vxlan_l3_inter_domain(self: SharedUtils) -> bool: @cached_property def overlay_routing_protocol_address_family(self: SharedUtils) -> str: - overlay_routing_protocol_address_family = get(self.hostvars, "overlay_routing_protocol_address_family", default="ipv4") + overlay_routing_protocol_address_family = self.inputs.overlay_routing_protocol_address_family if overlay_routing_protocol_address_family == "ipv6" and not (self.underlay_ipv6 is True and self.underlay_rfc5549): msg = "'overlay_routing_protocol_address_family: ipv6' is only supported in combination with 'underlay_ipv6: True' and 'underlay_rfc5549: True'" raise AristaAvdError( @@ -129,7 +106,7 @@ def overlay_routing_protocol_address_family(self: SharedUtils) -> str: @cached_property def evpn_encapsulation(self: SharedUtils) -> str: """EVPN encapsulation based on fabric_evpn_encapsulation and node default_evpn_encapsulation.""" - return get(self.hostvars, "fabric_evpn_encapsulation", default=self.node_type_key_data.default_evpn_encapsulation) + return default(self.inputs.fabric_evpn_encapsulation, self.node_type_key_data.default_evpn_encapsulation) @cached_property def evpn_soo(self: SharedUtils) -> str: @@ -244,7 +221,3 @@ def overlay_evpn_vxlan(self: SharedUtils) -> bool: @cached_property def overlay_evpn_mpls(self: SharedUtils) -> bool: return self.overlay_evpn and self.evpn_encapsulation == "mpls" - - @cached_property - def overlay_mlag_rfc5549(self: SharedUtils) -> bool: - return get(self.hostvars, "overlay_mlag_rfc5549") is True diff --git a/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py b/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py index 29162e270ab..0e38de8b5c8 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py @@ -4,15 +4,19 @@ from __future__ import annotations from functools import cached_property +from typing import TYPE_CHECKING from pyavd._eos_designs.avdfacts import AvdFacts -from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get, get_item, strip_empties_from_dict, strip_null_from_data +from pyavd._errors import AristaAvdInvalidInputsError, AristaAvdMissingVariableError +from pyavd._utils import get, strip_empties_from_dict, strip_null_from_data from pyavd.j2filters import natural_sort from .ntp import NtpMixin from .snmp_server import SnmpServerMixin +if TYPE_CHECKING: + from pyavd._eos_designs.schema import EosDesigns + class AvdStructuredConfigBase(AvdFacts, NtpMixin, SnmpServerMixin): """ @@ -747,19 +751,22 @@ def prefix_lists(self) -> list | None: prefix_lists_in_use = set() for neighbor in self.shared_utils.l3_interfaces_bgp_neighbors: if (prefix_list_in := get(neighbor, "ipv4_prefix_list_in")) and prefix_list_in not in prefix_lists_in_use: - pfx_list = self._get_prefix_list(prefix_list_in) + pfx_list = self._get_prefix_list(prefix_list_in)._as_dict() prefix_lists.append(pfx_list) prefix_lists_in_use.add(prefix_list_in) if (prefix_list_out := get(neighbor, "ipv4_prefix_list_out")) and prefix_list_out not in prefix_lists_in_use: - pfx_list = self._get_prefix_list(prefix_list_out) + pfx_list = self._get_prefix_list(prefix_list_out)._as_dict() prefix_lists.append(pfx_list) prefix_lists_in_use.add(prefix_list_out) return prefix_lists or None - def _get_prefix_list(self, name: str) -> dict: - return get_item(self.shared_utils.ipv4_prefix_list_catalog, "name", name, required=True, var_name=f"ipv4_prefix_list_catalog[name={name}]") + def _get_prefix_list(self, name: str) -> EosDesigns.Ipv4PrefixListCatalogItem: + if name not in self.inputs.ipv4_prefix_list_catalog: + msg = f"ipv4_prefix_list_catalog[name={name}]" + raise AristaAvdMissingVariableError(msg) + return self.inputs.ipv4_prefix_list_catalog[name] @cached_property def route_maps(self) -> list | None: diff --git a/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py b/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py index 5691525c425..93b92ba41c1 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py @@ -18,14 +18,6 @@ def render(self) -> dict: return super().render() return {} - @cached_property - def _trunk_groups_mlag_name(self) -> str: - return get(self.shared_utils.trunk_groups, "mlag.name", required=True) - - @cached_property - def _trunk_groups_mlag_l3_name(self) -> str: - return get(self.shared_utils.trunk_groups, "mlag_l3.name", required=True) - @cached_property def spanning_tree(self) -> dict: vlans = [self.shared_utils.mlag_peer_vlan] @@ -45,7 +37,7 @@ def vlans(self) -> list: "name": AvdStringFormatter().format( self.inputs.mlag_peer_l3_vlan_name, mlag_peer=self.shared_utils.mlag_peer, mlag_peer_l3_vlan=self.shared_utils.mlag_peer_l3_vlan ), - "trunk_groups": [self._trunk_groups_mlag_l3_name], + "trunk_groups": [self.inputs.trunk_groups.mlag_l3.name], }, ) @@ -56,7 +48,7 @@ def vlans(self) -> list: "name": AvdStringFormatter().format( self.inputs.mlag_peer_vlan_name, mlag_peer=self.shared_utils.mlag_peer, mlag_peer_vlan=self.shared_utils.mlag_peer_vlan ), - "trunk_groups": [self._trunk_groups_mlag_name], + "trunk_groups": [self.inputs.trunk_groups.mlag.name], }, ) return vlans @@ -164,7 +156,7 @@ def port_channel_interfaces(self) -> list: "enabled": True, "mode": "trunk", "trunk": { - "groups": [self._trunk_groups_mlag_name], + "groups": [self.inputs.trunk_groups.mlag.name], "allowed_vlan": get(self.shared_utils.switch_data_combined, "mlag_peer_link_allowed_vlans"), }, }, @@ -174,11 +166,11 @@ def port_channel_interfaces(self) -> list: "flow_tracker": self.shared_utils.get_flow_tracker(self.inputs.fabric_flow_tracking.mlag_interfaces), } - if self.shared_utils.mlag_l3 is True and self._trunk_groups_mlag_l3_name != self._trunk_groups_mlag_name: + if self.shared_utils.mlag_l3 is True and self.inputs.trunk_groups.mlag_l3.name != self.inputs.trunk_groups.mlag.name: # Add mlag_l3 trunk group even if we reuse the MLAG trunk group for underlay peering # since this trunk group is also used for overlay iBGP peerings # except in the case where the same trunk group name is defined. - port_channel_interface["switchport"]["trunk"]["groups"].append(self._trunk_groups_mlag_l3_name) + port_channel_interface["switchport"]["trunk"]["groups"].append(self.inputs.trunk_groups.mlag_l3.name) if (self.inputs.fabric_sflow.mlag_interfaces) is not None: port_channel_interface["sflow"] = {"enable": self.inputs.fabric_sflow.mlag_interfaces} diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_access_lists.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_access_lists.py index b002f1f9bf1..4d80433dad4 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_access_lists.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_access_lists.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Literal from pyavd._errors import AristaAvdError -from pyavd._utils import append_if_not_duplicate, get, get_ip_from_ip_prefix +from pyavd._utils import append_if_not_duplicate, get_ip_from_ip_prefix from pyavd.j2filters import natural_sort from .utils import UtilsMixin @@ -78,15 +78,15 @@ def _acl_internet_exit_direct(self: AvdStructuredConfigNetworkServices) -> dict def _acl_internet_exit_user_defined(self: AvdStructuredConfigNetworkServices, internet_exit_policy_type: Literal["zscaler", "direct"]) -> dict | None: acl_name = self.get_internet_exit_nat_acl_name(internet_exit_policy_type) - acl = get(self.shared_utils.ipv4_acls, acl_name) - if not acl: + if acl_name not in self.inputs.ipv4_acls: + # TODO: Evaluate if we should continue so we raise when there is no ACL. return None # pass substitution fields as anything to check if acl requires substitution or not acl = self.shared_utils.get_ipv4_acl(acl_name, "random", interface_ip="random", peer_ip="random") - if acl["name"] == acl_name: + if acl.name == acl_name: # ACL doesn't need replacement - return [acl] + return [acl._as_dict()] # TODO: We still have one nat for all interfaces, need to also add logic to make nat per interface # if acl needs substitution diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py index 2440bc5eb87..7112f321251 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py @@ -328,7 +328,7 @@ def _update_router_bgp_vrf_mlag_neighbor_cfg( bgp_vrf["redistribute"]["connected"] = {"enabled": True, "route_map": "RM-CONN-2-BGP-VRFS"} interface_name = f"Vlan{vlan_id}" - if self.shared_utils.underlay_rfc5549 and self.shared_utils.overlay_mlag_rfc5549: + if self.shared_utils.underlay_rfc5549 and self.inputs.overlay_mlag_rfc5549: bgp_vrf.setdefault("neighbor_interfaces", []).append( { "name": interface_name, diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py index 56aea312ab2..ffae5d1e4cd 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/utils.py @@ -28,18 +28,6 @@ class UtilsMixin(UtilsWanMixin, UtilsZscalerMixin): Class should only be used as Mixin to a AvdStructuredConfig class. """ - @cached_property - def _trunk_groups_mlag_name(self: AvdStructuredConfigNetworkServices) -> str: - return get(self.shared_utils.trunk_groups, "mlag.name", required=True) - - @cached_property - def _trunk_groups_mlag_l3_name(self: AvdStructuredConfigNetworkServices) -> str: - return get(self.shared_utils.trunk_groups, "mlag_l3.name", required=True) - - @cached_property - def _trunk_groups_uplink_name(self: AvdStructuredConfigNetworkServices) -> str: - return get(self.shared_utils.trunk_groups, "uplink.name", required=True) - @cached_property def _local_endpoint_trunk_groups(self: AvdStructuredConfigNetworkServices) -> set: return set(get(self._hostvars, "switch.local_endpoint_trunk_groups", default=[])) @@ -164,7 +152,7 @@ def _mlag_ibgp_peering_vlan_vrf( if (mlag_ibgp_peering_vlan := vrf.mlag_ibgp_peering_vlan) is not None: vlan_id = mlag_ibgp_peering_vlan else: - base_vlan = self.shared_utils.mlag_ibgp_peering_vrfs_base_vlan + base_vlan = self.inputs.mlag_ibgp_peering_vrfs.base_vlan vrf_id = default(vrf.vrf_id, vrf.vrf_vni) if vrf_id is None: msg = f"Unable to assign MLAG VRF Peering VLAN for vrf {vrf.name}.Set either 'mlag_ibgp_peering_vlan' or 'vrf_id' or 'vrf_vni' on the VRF" @@ -221,7 +209,7 @@ def _rt_admin_subfield(self: AvdStructuredConfigNetworkServices) -> str | None: Returns None if not set, since the calling functions will use per-vlan numbers by default. """ - admin_subfield = self.shared_utils.overlay_rt_type["admin_subfield"] + admin_subfield = self.inputs.overlay_rt_type.admin_subfield if admin_subfield is None: return None @@ -277,9 +265,9 @@ def get_vlan_rd( if rd_override is not None: assigned_number_subfield = rd_override - elif self.shared_utils.overlay_rd_type["vlan_assigned_number_subfield"] == "mac_vrf_vni": + elif self.inputs.overlay_rd_type.vlan_assigned_number_subfield == "mac_vrf_vni": assigned_number_subfield = self.get_vlan_mac_vrf_vni(vlan, tenant) - elif self.shared_utils.overlay_rd_type["vlan_assigned_number_subfield"] == "vlan_id": + elif self.inputs.overlay_rd_type.vlan_assigned_number_subfield == "vlan_id": assigned_number_subfield = vlan.id else: assigned_number_subfield = self.get_vlan_mac_vrf_id(vlan, tenant) @@ -302,18 +290,18 @@ def get_vlan_rt( admin_subfield = self._rt_admin_subfield elif rt_override is not None: admin_subfield = rt_override - elif self.shared_utils.overlay_rt_type["admin_subfield"] == "vrf_vni": + elif self.inputs.overlay_rt_type.admin_subfield == "vrf_vni": admin_subfield = self.get_vlan_mac_vrf_vni(vlan, tenant) - elif self.shared_utils.overlay_rt_type["admin_subfield"] == "id": + elif self.inputs.overlay_rt_type.admin_subfield == "id": admin_subfield = vlan.id else: admin_subfield = self.get_vlan_mac_vrf_id(vlan, tenant) if rt_override is not None: assigned_number_subfield = rt_override - elif self.shared_utils.overlay_rt_type["vlan_assigned_number_subfield"] == "mac_vrf_vni": + elif self.inputs.overlay_rt_type.vlan_assigned_number_subfield == "mac_vrf_vni": assigned_number_subfield = self.get_vlan_mac_vrf_vni(vlan, tenant) - elif self.shared_utils.overlay_rt_type["vlan_assigned_number_subfield"] == "vlan_id": + elif self.inputs.overlay_rt_type.vlan_assigned_number_subfield == "vlan_id": assigned_number_subfield = vlan.id else: assigned_number_subfield = self.get_vlan_mac_vrf_id(vlan, tenant) @@ -328,14 +316,11 @@ def _vrf_rt_admin_subfield(self: AvdStructuredConfigNetworkServices) -> str | No Returns None if not set, since the calling functions will use per-vrf numbers by default. """ - admin_subfield = self.shared_utils.overlay_rt_type["vrf_admin_subfield"] - if admin_subfield is None: - return None - + admin_subfield: str = default(self.inputs.overlay_rt_type.vrf_admin_subfield, self.inputs.overlay_rt_type.admin_subfield) if admin_subfield == "bgp_as": return self.shared_utils.bgp_as - if re_fullmatch(r"\d+", str(admin_subfield)): + if re_fullmatch(r"\d+", admin_subfield): return admin_subfield return None @@ -361,7 +346,7 @@ def get_vrf_rt(self: AvdStructuredConfigNetworkServices, vrf: EosDesigns._Dynami if self._vrf_rt_admin_subfield is not None: admin_subfield = self._vrf_rt_admin_subfield - elif self.shared_utils.overlay_rt_type["vrf_admin_subfield"] == "vrf_vni": + elif default(self.inputs.overlay_rt_type.vrf_admin_subfield, self.inputs.overlay_rt_type.admin_subfield) == "vrf_vni": admin_subfield = self.shared_utils.get_vrf_vni(vrf) else: # Both for 'id' and 'vrf_id' options. @@ -407,7 +392,7 @@ def get_vlan_aware_bundle_rt( if is_vrf and self._vrf_rt_admin_subfield is not None: admin_subfield = self._vrf_rt_admin_subfield - elif is_vrf and self.shared_utils.overlay_rt_type["vrf_admin_subfield"] == "vrf_vni": + elif is_vrf and default(self.inputs.overlay_rt_type.vrf_admin_subfield, self.inputs.overlay_rt_type.admin_subfield) == "vrf_vni": admin_subfield = vni else: # Both for 'id' and 'vrf_id' options. diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/utils_wan.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/utils_wan.py index 9c742921257..61e2253a3b2 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/utils_wan.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/utils_wan.py @@ -450,13 +450,13 @@ def _svi_acls(self: AvdStructuredConfigNetworkServices) -> dict[str, dict[str, d name=ipv4_acl_in, interface_name=interface_name, interface_ip=interface_ip, - ) + )._as_dict() if ipv4_acl_out is not None: svi_acls.setdefault(interface_name, {})["ipv4_acl_out"] = self.shared_utils.get_ipv4_acl( name=ipv4_acl_out, interface_name=interface_name, interface_ip=interface_ip, - ) + )._as_dict() return svi_acls @@ -555,13 +555,13 @@ def _l3_interface_acls(self: AvdStructuredConfigNetworkServices) -> dict | None: name=ipv4_acl_in, interface_name=interface_name, interface_ip=interface_ip, - ) + )._as_dict() if ipv4_acl_out is not None: l3_interface_acls.setdefault(interface_name, {})["ipv4_acl_out"] = self.shared_utils.get_ipv4_acl( name=ipv4_acl_out, interface_name=interface_name, interface_ip=interface_ip, - ) + )._as_dict() return l3_interface_acls @cached_property diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py index 86c11bc762e..9831227939e 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py @@ -184,7 +184,7 @@ def _get_vlan_ip_config_for_mlag_peering( Called from _get_vlan_interface_config_for_mlag_peering and prefix_lists. """ - if self.shared_utils.underlay_rfc5549 and self.shared_utils.overlay_mlag_rfc5549: + if self.shared_utils.underlay_rfc5549 and self.inputs.overlay_mlag_rfc5549: return {"ipv6_enable": True} if (mlag_ibgp_peering_ipv4_pool := vrf.mlag_ibgp_peering_ipv4_pool) is not None: diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/vlans.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/vlans.py index 15b868e37f7..9807dba8559 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/vlans.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/vlans.py @@ -61,7 +61,7 @@ def vlans(self: AvdStructuredConfigNetworkServices) -> list | None: "name": AvdStringFormatter().format( self.inputs.mlag_peer_l3_vrf_vlan_name, mlag_peer=self.shared_utils.mlag_peer, vlan=vlan_id, vrf=vrf.name ), - "trunk_groups": [self._trunk_groups_mlag_l3_name], + "trunk_groups": [self.inputs.trunk_groups.mlag_l3.name], "tenant": tenant.name, } append_if_not_duplicate( @@ -110,9 +110,9 @@ def _get_vlan_config( if self.shared_utils.only_local_vlan_trunk_groups: trunk_groups = list(self._local_endpoint_trunk_groups.intersection(trunk_groups)) if self.shared_utils.mlag: - trunk_groups.append(self._trunk_groups_mlag_name) + trunk_groups.append(self.inputs.trunk_groups.mlag.name) if self.shared_utils.uplink_type == "port-channel": - trunk_groups.append(self._trunk_groups_uplink_name) + trunk_groups.append(self.inputs.trunk_groups.uplink.name) vlans_vlan["trunk_groups"] = natural_sort(trunk_groups) return vlans_vlan diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/vrfs.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/vrfs.py index 700584af134..f6d2f979bde 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/vrfs.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/vrfs.py @@ -49,7 +49,7 @@ def vrfs(self: AvdStructuredConfigNetworkServices) -> list | None: } # MLAG IBGP Peering VLANs per VRF - if self.shared_utils.overlay_mlag_rfc5549 and self._mlag_ibgp_peering_enabled(vrf, tenant): + if self.inputs.overlay_mlag_rfc5549 and self._mlag_ibgp_peering_enabled(vrf, tenant): new_vrf["ip_routing_ipv6_interfaces"] = True new_vrf["ipv6_routing"] = True else: diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/loopback_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/loopback_interfaces.py index 47e2d9f1f13..ea310b7bb7a 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/loopback_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/loopback_interfaces.py @@ -83,8 +83,8 @@ def loopback_interfaces(self: AvdStructuredConfigUnderlay) -> list | None: "ip_address": f"{self.shared_utils.vtep_ip}/32", } - if self.shared_utils.network_services_l3 is True and self.shared_utils.vtep_vvtep_ip is not None: - vtep_loopback["ip_address_secondaries"] = [self.shared_utils.vtep_vvtep_ip] + if self.shared_utils.network_services_l3 is True and self.inputs.vtep_vvtep_ip is not None: + vtep_loopback["ip_address_secondaries"] = [self.inputs.vtep_vvtep_ip] if self.shared_utils.underlay_ospf is True: vtep_loopback["ospf_area"] = self.shared_utils.underlay_ospf_area diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/prefix_lists.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/prefix_lists.py index 9f2d08de9ba..8226c2b1a3b 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/prefix_lists.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/prefix_lists.py @@ -38,8 +38,8 @@ def prefix_lists(self: AvdStructuredConfigUnderlay) -> list | None: if self.shared_utils.overlay_vtep and self.shared_utils.vtep_loopback.lower() != "loopback0" and not self.shared_utils.is_wan_router: sequence_numbers.append({"sequence": 20, "action": f"permit {self.shared_utils.vtep_loopback_ipv4_pool} eq 32"}) - if self.shared_utils.vtep_vvtep_ip is not None and self.shared_utils.network_services_l3 is True and not self.shared_utils.is_wan_router: - sequence_numbers.append({"sequence": 30, "action": f"permit {self.shared_utils.vtep_vvtep_ip}"}) + if self.inputs.vtep_vvtep_ip is not None and self.shared_utils.network_services_l3 is True and not self.shared_utils.is_wan_router: + sequence_numbers.append({"sequence": 30, "action": f"permit {self.inputs.vtep_vvtep_ip}"}) prefix_lists = [{"name": "PL-LOOPBACKS-EVPN-OVERLAY", "sequence_numbers": sequence_numbers}] diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py index 2c65dfff229..b3ab5987a48 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py @@ -327,13 +327,13 @@ def _l3_interface_acls(self: AvdStructuredConfigUnderlay) -> dict[str, dict[str, interface_name=interface_name, interface_ip=interface_ip, peer_ip=peer_ip, - ) + )._as_dict() if ipv4_acl_out is not None: l3_interface_acls.setdefault(interface_name, {})["ipv4_acl_out"] = self.shared_utils.get_ipv4_acl( name=ipv4_acl_out, interface_name=interface_name, interface_ip=interface_ip, peer_ip=peer_ip, - ) + )._as_dict() return l3_interface_acls