diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-ipvpn-gateway-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-ipvpn-gateway-configuration.md index 378342b01ec..c08972daf95 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-ipvpn-gateway-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-ipvpn-gateway-configuration.md @@ -15,11 +15,11 @@ | [      ipvpn_domain_id](## ".defaults.ipvpn_gateway.ipvpn_domain_id") | String | | `65535:2` | | Domain ID to assign to IPVPN address families for use with D-path. Format :. | | [      enable_d_path](## ".defaults.ipvpn_gateway.enable_d_path") | Boolean | | `True` | | Enable D-path for use with BGP bestpath selection algorithm. | | [      maximum_routes](## ".defaults.ipvpn_gateway.maximum_routes") | Integer | | `0` | | Maximum routes to accept from IPVPN remote peers. | - | [      local_as](## ".defaults.ipvpn_gateway.local_as") | String | | `none` | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | + | [      local_as](## ".defaults.ipvpn_gateway.local_as") | String | | | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [      address_families](## ".defaults.ipvpn_gateway.address_families") | List, items: String | | `['vpn-ipv4']` | | IPVPN address families to enable for remote peers. | | [        - <str>](## ".defaults.ipvpn_gateway.address_families.[]") | String | | | | | | [      remote_peers](## ".defaults.ipvpn_gateway.remote_peers") | List, items: Dictionary | | | | | - | [        - hostname](## ".defaults.ipvpn_gateway.remote_peers.[].hostname") | String | Required | | | Hostname of remote IPVPN Peer. | + | [        - hostname](## ".defaults.ipvpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote IPVPN Peer. | | [          ip_address](## ".defaults.ipvpn_gateway.remote_peers.[].ip_address") | String | Required | | Format: ipv4 | Peering IP of remote IPVPN Peer. | | [          bgp_as](## ".defaults.ipvpn_gateway.remote_peers.[].bgp_as") | String | Required | | | Remote IPVPN Peer's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [  node_groups](## ".node_groups") | List, items: Dictionary | | | | Define variables related to all nodes part of this group. | @@ -32,11 +32,11 @@ | [            ipvpn_domain_id](## ".node_groups.[].nodes.[].ipvpn_gateway.ipvpn_domain_id") | String | | `65535:2` | | Domain ID to assign to IPVPN address families for use with D-path. Format :. | | [            enable_d_path](## ".node_groups.[].nodes.[].ipvpn_gateway.enable_d_path") | Boolean | | `True` | | Enable D-path for use with BGP bestpath selection algorithm. | | [            maximum_routes](## ".node_groups.[].nodes.[].ipvpn_gateway.maximum_routes") | Integer | | `0` | | Maximum routes to accept from IPVPN remote peers. | - | [            local_as](## ".node_groups.[].nodes.[].ipvpn_gateway.local_as") | String | | `none` | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | + | [            local_as](## ".node_groups.[].nodes.[].ipvpn_gateway.local_as") | String | | | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [            address_families](## ".node_groups.[].nodes.[].ipvpn_gateway.address_families") | List, items: String | | `['vpn-ipv4']` | | IPVPN address families to enable for remote peers. | | [              - <str>](## ".node_groups.[].nodes.[].ipvpn_gateway.address_families.[]") | String | | | | | | [            remote_peers](## ".node_groups.[].nodes.[].ipvpn_gateway.remote_peers") | List, items: Dictionary | | | | | - | [              - hostname](## ".node_groups.[].nodes.[].ipvpn_gateway.remote_peers.[].hostname") | String | Required | | | Hostname of remote IPVPN Peer. | + | [              - hostname](## ".node_groups.[].nodes.[].ipvpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote IPVPN Peer. | | [                ip_address](## ".node_groups.[].nodes.[].ipvpn_gateway.remote_peers.[].ip_address") | String | Required | | Format: ipv4 | Peering IP of remote IPVPN Peer. | | [                bgp_as](## ".node_groups.[].nodes.[].ipvpn_gateway.remote_peers.[].bgp_as") | String | Required | | | Remote IPVPN Peer's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [      ipvpn_gateway](## ".node_groups.[].ipvpn_gateway") | Dictionary | | | | Node is acting as IP-VPN Gateway for EVPN to MPLS-IP-VPN Interworking. The BGP peer group used for this is "bgp_peer_groups.ipvpn_gateway_peers".
L3 Reachability is required for this to work, the preferred method to establish underlay connectivity is to use core_interfaces.
| @@ -45,11 +45,11 @@ | [        ipvpn_domain_id](## ".node_groups.[].ipvpn_gateway.ipvpn_domain_id") | String | | `65535:2` | | Domain ID to assign to IPVPN address families for use with D-path. Format :. | | [        enable_d_path](## ".node_groups.[].ipvpn_gateway.enable_d_path") | Boolean | | `True` | | Enable D-path for use with BGP bestpath selection algorithm. | | [        maximum_routes](## ".node_groups.[].ipvpn_gateway.maximum_routes") | Integer | | `0` | | Maximum routes to accept from IPVPN remote peers. | - | [        local_as](## ".node_groups.[].ipvpn_gateway.local_as") | String | | `none` | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | + | [        local_as](## ".node_groups.[].ipvpn_gateway.local_as") | String | | | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [        address_families](## ".node_groups.[].ipvpn_gateway.address_families") | List, items: String | | `['vpn-ipv4']` | | IPVPN address families to enable for remote peers. | | [          - <str>](## ".node_groups.[].ipvpn_gateway.address_families.[]") | String | | | | | | [        remote_peers](## ".node_groups.[].ipvpn_gateway.remote_peers") | List, items: Dictionary | | | | | - | [          - hostname](## ".node_groups.[].ipvpn_gateway.remote_peers.[].hostname") | String | Required | | | Hostname of remote IPVPN Peer. | + | [          - hostname](## ".node_groups.[].ipvpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote IPVPN Peer. | | [            ip_address](## ".node_groups.[].ipvpn_gateway.remote_peers.[].ip_address") | String | Required | | Format: ipv4 | Peering IP of remote IPVPN Peer. | | [            bgp_as](## ".node_groups.[].ipvpn_gateway.remote_peers.[].bgp_as") | String | Required | | | Remote IPVPN Peer's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [  nodes](## ".nodes") | List, items: Dictionary | | | | Define variables per node. | @@ -60,11 +60,11 @@ | [        ipvpn_domain_id](## ".nodes.[].ipvpn_gateway.ipvpn_domain_id") | String | | `65535:2` | | Domain ID to assign to IPVPN address families for use with D-path. Format :. | | [        enable_d_path](## ".nodes.[].ipvpn_gateway.enable_d_path") | Boolean | | `True` | | Enable D-path for use with BGP bestpath selection algorithm. | | [        maximum_routes](## ".nodes.[].ipvpn_gateway.maximum_routes") | Integer | | `0` | | Maximum routes to accept from IPVPN remote peers. | - | [        local_as](## ".nodes.[].ipvpn_gateway.local_as") | String | | `none` | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | + | [        local_as](## ".nodes.[].ipvpn_gateway.local_as") | String | | | | Local BGP AS applied to peering with IPVPN remote peers.
BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [        address_families](## ".nodes.[].ipvpn_gateway.address_families") | List, items: String | | `['vpn-ipv4']` | | IPVPN address families to enable for remote peers. | | [          - <str>](## ".nodes.[].ipvpn_gateway.address_families.[]") | String | | | | | | [        remote_peers](## ".nodes.[].ipvpn_gateway.remote_peers") | List, items: Dictionary | | | | | - | [          - hostname](## ".nodes.[].ipvpn_gateway.remote_peers.[].hostname") | String | Required | | | Hostname of remote IPVPN Peer. | + | [          - hostname](## ".nodes.[].ipvpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote IPVPN Peer. | | [            ip_address](## ".nodes.[].ipvpn_gateway.remote_peers.[].ip_address") | String | Required | | Format: ipv4 | Peering IP of remote IPVPN Peer. | | [            bgp_as](## ".nodes.[].ipvpn_gateway.remote_peers.[].bgp_as") | String | Required | | | Remote IPVPN Peer's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | @@ -96,7 +96,7 @@ # Local BGP AS applied to peering with IPVPN remote peers. # BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". # For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - local_as: + local_as: # IPVPN address families to enable for remote peers. address_families: # default=['vpn-ipv4'] @@ -104,7 +104,7 @@ remote_peers: # Hostname of remote IPVPN Peer. - - hostname: + - hostname: # Peering IP of remote IPVPN Peer. ip_address: @@ -146,7 +146,7 @@ # Local BGP AS applied to peering with IPVPN remote peers. # BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". # For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - local_as: + local_as: # IPVPN address families to enable for remote peers. address_families: # default=['vpn-ipv4'] @@ -154,7 +154,7 @@ remote_peers: # Hostname of remote IPVPN Peer. - - hostname: + - hostname: # Peering IP of remote IPVPN Peer. ip_address: @@ -183,7 +183,7 @@ # Local BGP AS applied to peering with IPVPN remote peers. # BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". # For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - local_as: + local_as: # IPVPN address families to enable for remote peers. address_families: # default=['vpn-ipv4'] @@ -191,7 +191,7 @@ remote_peers: # Hostname of remote IPVPN Peer. - - hostname: + - hostname: # Peering IP of remote IPVPN Peer. ip_address: @@ -226,7 +226,7 @@ # Local BGP AS applied to peering with IPVPN remote peers. # BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". # For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - local_as: + local_as: # IPVPN address families to enable for remote peers. address_families: # default=['vpn-ipv4'] @@ -234,7 +234,7 @@ remote_peers: # Hostname of remote IPVPN Peer. - - hostname: + - hostname: # Peering IP of remote IPVPN Peer. ip_address: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-multi-domain-gateway-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-multi-domain-gateway-configuration.md index 79594afa298..be89fc61e0a 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-multi-domain-gateway-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-multi-domain-gateway-configuration.md @@ -11,7 +11,7 @@ | [  defaults](## ".defaults") | Dictionary | | | | Define variables for all nodes of this type. | | [    evpn_gateway](## ".defaults.evpn_gateway") | Dictionary | | | | Node is acting as EVPN Multi-Domain Gateway.
New BGP peer-group is generated between EVPN GWs in different domains or between GWs and Route Servers.
Name can be changed under "bgp_peer_groups.evpn_overlay_core" variable.
L3 rechability for different EVPN GWs must be already in place, it is recommended to use DCI & L3 Edge if Route Servers and GWs are not defined under the same Ansible inventory.
| | [      remote_peers](## ".defaults.evpn_gateway.remote_peers") | List, items: Dictionary | | | | Define remote peers of the EVPN VXLAN Gateway.
If the hostname can be found in the inventory, ip_address and BGP ASN will be automatically populated. Manual override takes precedence.
If the peer's hostname can not be found in the inventory, ip_address and bgp_as must be defined.
| - | [        - hostname](## ".defaults.evpn_gateway.remote_peers.[].hostname") | String | | | | Hostname of remote EVPN GW server. | + | [        - hostname](## ".defaults.evpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote EVPN GW server. | | [          ip_address](## ".defaults.evpn_gateway.remote_peers.[].ip_address") | String | | | Format: ipv4 | Peering IP of remote Route Server. | | [          bgp_as](## ".defaults.evpn_gateway.remote_peers.[].bgp_as") | String | | | | Remote Route Server's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [      evpn_l2](## ".defaults.evpn_gateway.evpn_l2") | Dictionary | | | | Enable EVPN Gateway functionality for route-types 2 (MAC-IP) and 3 (IMET). | @@ -25,7 +25,7 @@ | [        - name](## ".node_groups.[].nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [          evpn_gateway](## ".node_groups.[].nodes.[].evpn_gateway") | Dictionary | | | | Node is acting as EVPN Multi-Domain Gateway.
New BGP peer-group is generated between EVPN GWs in different domains or between GWs and Route Servers.
Name can be changed under "bgp_peer_groups.evpn_overlay_core" variable.
L3 rechability for different EVPN GWs must be already in place, it is recommended to use DCI & L3 Edge if Route Servers and GWs are not defined under the same Ansible inventory.
| | [            remote_peers](## ".node_groups.[].nodes.[].evpn_gateway.remote_peers") | List, items: Dictionary | | | | Define remote peers of the EVPN VXLAN Gateway.
If the hostname can be found in the inventory, ip_address and BGP ASN will be automatically populated. Manual override takes precedence.
If the peer's hostname can not be found in the inventory, ip_address and bgp_as must be defined.
| - | [              - hostname](## ".node_groups.[].nodes.[].evpn_gateway.remote_peers.[].hostname") | String | | | | Hostname of remote EVPN GW server. | + | [              - hostname](## ".node_groups.[].nodes.[].evpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote EVPN GW server. | | [                ip_address](## ".node_groups.[].nodes.[].evpn_gateway.remote_peers.[].ip_address") | String | | | Format: ipv4 | Peering IP of remote Route Server. | | [                bgp_as](## ".node_groups.[].nodes.[].evpn_gateway.remote_peers.[].bgp_as") | String | | | | Remote Route Server's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [            evpn_l2](## ".node_groups.[].nodes.[].evpn_gateway.evpn_l2") | Dictionary | | | | Enable EVPN Gateway functionality for route-types 2 (MAC-IP) and 3 (IMET). | @@ -35,7 +35,7 @@ | [              inter_domain](## ".node_groups.[].nodes.[].evpn_gateway.evpn_l3.inter_domain") | Boolean | | `True` | | | | [      evpn_gateway](## ".node_groups.[].evpn_gateway") | Dictionary | | | | Node is acting as EVPN Multi-Domain Gateway.
New BGP peer-group is generated between EVPN GWs in different domains or between GWs and Route Servers.
Name can be changed under "bgp_peer_groups.evpn_overlay_core" variable.
L3 rechability for different EVPN GWs must be already in place, it is recommended to use DCI & L3 Edge if Route Servers and GWs are not defined under the same Ansible inventory.
| | [        remote_peers](## ".node_groups.[].evpn_gateway.remote_peers") | List, items: Dictionary | | | | Define remote peers of the EVPN VXLAN Gateway.
If the hostname can be found in the inventory, ip_address and BGP ASN will be automatically populated. Manual override takes precedence.
If the peer's hostname can not be found in the inventory, ip_address and bgp_as must be defined.
| - | [          - hostname](## ".node_groups.[].evpn_gateway.remote_peers.[].hostname") | String | | | | Hostname of remote EVPN GW server. | + | [          - hostname](## ".node_groups.[].evpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote EVPN GW server. | | [            ip_address](## ".node_groups.[].evpn_gateway.remote_peers.[].ip_address") | String | | | Format: ipv4 | Peering IP of remote Route Server. | | [            bgp_as](## ".node_groups.[].evpn_gateway.remote_peers.[].bgp_as") | String | | | | Remote Route Server's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [        evpn_l2](## ".node_groups.[].evpn_gateway.evpn_l2") | Dictionary | | | | Enable EVPN Gateway functionality for route-types 2 (MAC-IP) and 3 (IMET). | @@ -47,7 +47,7 @@ | [    - name](## ".nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [      evpn_gateway](## ".nodes.[].evpn_gateway") | Dictionary | | | | Node is acting as EVPN Multi-Domain Gateway.
New BGP peer-group is generated between EVPN GWs in different domains or between GWs and Route Servers.
Name can be changed under "bgp_peer_groups.evpn_overlay_core" variable.
L3 rechability for different EVPN GWs must be already in place, it is recommended to use DCI & L3 Edge if Route Servers and GWs are not defined under the same Ansible inventory.
| | [        remote_peers](## ".nodes.[].evpn_gateway.remote_peers") | List, items: Dictionary | | | | Define remote peers of the EVPN VXLAN Gateway.
If the hostname can be found in the inventory, ip_address and BGP ASN will be automatically populated. Manual override takes precedence.
If the peer's hostname can not be found in the inventory, ip_address and bgp_as must be defined.
| - | [          - hostname](## ".nodes.[].evpn_gateway.remote_peers.[].hostname") | String | | | | Hostname of remote EVPN GW server. | + | [          - hostname](## ".nodes.[].evpn_gateway.remote_peers.[].hostname") | String | Required, Unique | | | Hostname of remote EVPN GW server. | | [            ip_address](## ".nodes.[].evpn_gateway.remote_peers.[].ip_address") | String | | | Format: ipv4 | Peering IP of remote Route Server. | | [            bgp_as](## ".nodes.[].evpn_gateway.remote_peers.[].bgp_as") | String | | | | Remote Route Server's BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>".
For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. | | [        evpn_l2](## ".nodes.[].evpn_gateway.evpn_l2") | Dictionary | | | | Enable EVPN Gateway functionality for route-types 2 (MAC-IP) and 3 (IMET). | @@ -76,7 +76,7 @@ remote_peers: # Hostname of remote EVPN GW server. - - hostname: + - hostname: # Peering IP of remote Route Server. ip_address: @@ -119,7 +119,7 @@ remote_peers: # Hostname of remote EVPN GW server. - - hostname: + - hostname: # Peering IP of remote Route Server. ip_address: @@ -149,7 +149,7 @@ remote_peers: # Hostname of remote EVPN GW server. - - hostname: + - hostname: # Peering IP of remote Route Server. ip_address: @@ -185,7 +185,7 @@ remote_peers: # Hostname of remote EVPN GW server. - - hostname: + - hostname: # Peering IP of remote Route Server. ip_address: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-services-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-services-configuration.md index e6b32aee472..48cae68a104 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-services-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-evpn-services-configuration.md @@ -22,7 +22,7 @@ | [      always_include_vrfs_in_tenants](## ".defaults.filter.always_include_vrfs_in_tenants") | List, items: String | | | | List of tenants where VRFs will be configured even if VLANs are not included in tags.
Useful for L3 "border" leaf.
| | [        - <str>](## ".defaults.filter.always_include_vrfs_in_tenants.[]") | String | | | | | | [      only_vlans_in_use](## ".defaults.filter.only_vlans_in_use") | Boolean | | `False` | | Only configure VLANs, SVIs, VRFs in use by connected endpoints or downstream L2 switches.
Note! This feature only considers configuration managed by eos_designs.
This excludes structured_config, custom_structured_configuration_, raw_eos_cli, eos_cli, custom templates, configlets etc.
| - | [    igmp_snooping_enabled](## ".defaults.igmp_snooping_enabled") | Boolean | | `True` | | Activate or deactivate IGMP snooping on device level. | + | [    igmp_snooping_enabled](## ".defaults.igmp_snooping_enabled") | Boolean | | | | Activate or deactivate IGMP snooping on device level. | | [  node_groups](## ".node_groups") | List, items: Dictionary | | | | Define variables related to all nodes part of this group. | | [    - group](## ".node_groups.[].group") | String | Required, Unique | | | The Node Group Name is used for MLAG domain unless set with 'mlag_domain_id'.
The Node Group Name is also used for peer description on downstream switches' uplinks.
| | [      nodes](## ".node_groups.[].nodes") | List, items: Dictionary | | | | Define variables per node. | @@ -40,7 +40,7 @@ | [            always_include_vrfs_in_tenants](## ".node_groups.[].nodes.[].filter.always_include_vrfs_in_tenants") | List, items: String | | | | List of tenants where VRFs will be configured even if VLANs are not included in tags.
Useful for L3 "border" leaf.
| | [              - <str>](## ".node_groups.[].nodes.[].filter.always_include_vrfs_in_tenants.[]") | String | | | | | | [            only_vlans_in_use](## ".node_groups.[].nodes.[].filter.only_vlans_in_use") | Boolean | | `False` | | Only configure VLANs, SVIs, VRFs in use by connected endpoints or downstream L2 switches.
Note! This feature only considers configuration managed by eos_designs.
This excludes structured_config, custom_structured_configuration_, raw_eos_cli, eos_cli, custom templates, configlets etc.
| - | [          igmp_snooping_enabled](## ".node_groups.[].nodes.[].igmp_snooping_enabled") | Boolean | | `True` | | Activate or deactivate IGMP snooping on device level. | + | [          igmp_snooping_enabled](## ".node_groups.[].nodes.[].igmp_snooping_enabled") | Boolean | | | | Activate or deactivate IGMP snooping on device level. | | [      evpn_services_l2_only](## ".node_groups.[].evpn_services_l2_only") | Boolean | | `False` | | Possibility to prevent configuration of Tenant VRFs and SVIs.
Override node definition "network_services_l3" from node_type_keys.
This allows support for centralized routing.
| | [      filter](## ".node_groups.[].filter") | Dictionary | | | | Filter L3 and L2 network services based on tenant and tags (and operation filter).
If filter is not defined it will default to all.
| | [        tenants](## ".node_groups.[].filter.tenants") | List, items: String | | `['all']` | | Limit configured Network Services to those defined under these Tenants. Set to ['all'] for all Tenants (default).
This list also limits Tenants included by `always_include_vrfs_in_tenants`. | @@ -54,7 +54,7 @@ | [        always_include_vrfs_in_tenants](## ".node_groups.[].filter.always_include_vrfs_in_tenants") | List, items: String | | | | List of tenants where VRFs will be configured even if VLANs are not included in tags.
Useful for L3 "border" leaf.
| | [          - <str>](## ".node_groups.[].filter.always_include_vrfs_in_tenants.[]") | String | | | | | | [        only_vlans_in_use](## ".node_groups.[].filter.only_vlans_in_use") | Boolean | | `False` | | Only configure VLANs, SVIs, VRFs in use by connected endpoints or downstream L2 switches.
Note! This feature only considers configuration managed by eos_designs.
This excludes structured_config, custom_structured_configuration_, raw_eos_cli, eos_cli, custom templates, configlets etc.
| - | [      igmp_snooping_enabled](## ".node_groups.[].igmp_snooping_enabled") | Boolean | | `True` | | Activate or deactivate IGMP snooping on device level. | + | [      igmp_snooping_enabled](## ".node_groups.[].igmp_snooping_enabled") | Boolean | | | | Activate or deactivate IGMP snooping on device level. | | [  nodes](## ".nodes") | List, items: Dictionary | | | | Define variables per node. | | [    - name](## ".nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [      evpn_services_l2_only](## ".nodes.[].evpn_services_l2_only") | Boolean | | `False` | | Possibility to prevent configuration of Tenant VRFs and SVIs.
Override node definition "network_services_l3" from node_type_keys.
This allows support for centralized routing.
| @@ -70,7 +70,7 @@ | [        always_include_vrfs_in_tenants](## ".nodes.[].filter.always_include_vrfs_in_tenants") | List, items: String | | | | List of tenants where VRFs will be configured even if VLANs are not included in tags.
Useful for L3 "border" leaf.
| | [          - <str>](## ".nodes.[].filter.always_include_vrfs_in_tenants.[]") | String | | | | | | [        only_vlans_in_use](## ".nodes.[].filter.only_vlans_in_use") | Boolean | | `False` | | Only configure VLANs, SVIs, VRFs in use by connected endpoints or downstream L2 switches.
Note! This feature only considers configuration managed by eos_designs.
This excludes structured_config, custom_structured_configuration_, raw_eos_cli, eos_cli, custom templates, configlets etc.
| - | [      igmp_snooping_enabled](## ".nodes.[].igmp_snooping_enabled") | Boolean | | `True` | | Activate or deactivate IGMP snooping on device level. | + | [      igmp_snooping_enabled](## ".nodes.[].igmp_snooping_enabled") | Boolean | | | | Activate or deactivate IGMP snooping on device level. | === "YAML" @@ -119,7 +119,7 @@ only_vlans_in_use: # Activate or deactivate IGMP snooping on device level. - igmp_snooping_enabled: + igmp_snooping_enabled: # Define variables related to all nodes part of this group. node_groups: @@ -173,7 +173,7 @@ only_vlans_in_use: # Activate or deactivate IGMP snooping on device level. - igmp_snooping_enabled: + igmp_snooping_enabled: # Possibility to prevent configuration of Tenant VRFs and SVIs. # Override node definition "network_services_l3" from node_type_keys. @@ -214,7 +214,7 @@ only_vlans_in_use: # Activate or deactivate IGMP snooping on device level. - igmp_snooping_enabled: + igmp_snooping_enabled: # Define variables per node. nodes: @@ -261,5 +261,5 @@ only_vlans_in_use: # Activate or deactivate IGMP snooping on device level. - igmp_snooping_enabled: + igmp_snooping_enabled: ``` diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-inband-management-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-inband-management-configuration.md index b20d99bd1c8..c7533bc36b5 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-inband-management-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-inband-management-configuration.md @@ -18,7 +18,7 @@ | [    inband_mgmt_ipv6_subnet](## ".defaults.inband_mgmt_ipv6_subnet") | String | | | Format: ipv6_cidr | Optional IPv6 prefix assigned to inband management SVIs on L2 switches (switches using port-channels as uplinks).
Parent l3leafs will have SVI with "ipv6 virtual-router" and host-route injection based on ARP.
This allows all l3leafs to reuse the same subnet across multiple racks without VXLAN extension.
SVI IP address will be assigned as follows:
virtual-router: + 1
l3leaf A : + 2 (same IP on all l3leaf A)
l3leaf B : + 3 (same IP on all l3leaf B)
l2leafs : + 3 +
GW on l2leafs : + 1
Assign range larger than total l2leafs + 5

Setting is ignored if 'inband_mgmt_ipv6_address' is set.

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [    inband_mgmt_ipv6_gateway](## ".defaults.inband_mgmt_ipv6_gateway") | String | | | Format: ipv6 | Default gateway configured in the 'inband_mgmt_vrf'.
Used when `inband_mgmt_ipv6_address` is set.
Ignored when 'inband_mgmt_ipv6_subnet' is set (first IP in subnet used as gateway).

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [    inband_mgmt_description](## ".defaults.inband_mgmt_description") | String | | `Inband Management` | | Description configured on the Inband Management SVI.

This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | - | [    inband_mgmt_vlan_name](## ".defaults.inband_mgmt_vlan_name") | String | | `Inband Management` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | + | [    inband_mgmt_vlan_name](## ".defaults.inband_mgmt_vlan_name") | String | | `INBAND_MGMT` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [    inband_mgmt_vrf](## ".defaults.inband_mgmt_vrf") | String | | `default` | | VRF configured on the Inband Management Interface.
The VRF is created if not already created by other means.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [    inband_mgmt_mtu](## ".defaults.inband_mgmt_mtu") | Integer | | `1500` | | MTU configured on the Inband Management Interface.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [    inband_ztp](## ".defaults.inband_ztp") | Boolean | | `False` | | Enable to configure upstream device with proper configuration to allow downstream devices to ZTP inband.
This setting also requires that the `inband_mgmt_vlan` is set for the node. | @@ -38,7 +38,7 @@ | [          inband_mgmt_ipv6_subnet](## ".node_groups.[].nodes.[].inband_mgmt_ipv6_subnet") | String | | | Format: ipv6_cidr | Optional IPv6 prefix assigned to inband management SVIs on L2 switches (switches using port-channels as uplinks).
Parent l3leafs will have SVI with "ipv6 virtual-router" and host-route injection based on ARP.
This allows all l3leafs to reuse the same subnet across multiple racks without VXLAN extension.
SVI IP address will be assigned as follows:
virtual-router: + 1
l3leaf A : + 2 (same IP on all l3leaf A)
l3leaf B : + 3 (same IP on all l3leaf B)
l2leafs : + 3 +
GW on l2leafs : + 1
Assign range larger than total l2leafs + 5

Setting is ignored if 'inband_mgmt_ipv6_address' is set.

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [          inband_mgmt_ipv6_gateway](## ".node_groups.[].nodes.[].inband_mgmt_ipv6_gateway") | String | | | Format: ipv6 | Default gateway configured in the 'inband_mgmt_vrf'.
Used when `inband_mgmt_ipv6_address` is set.
Ignored when 'inband_mgmt_ipv6_subnet' is set (first IP in subnet used as gateway).

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [          inband_mgmt_description](## ".node_groups.[].nodes.[].inband_mgmt_description") | String | | `Inband Management` | | Description configured on the Inband Management SVI.

This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | - | [          inband_mgmt_vlan_name](## ".node_groups.[].nodes.[].inband_mgmt_vlan_name") | String | | `Inband Management` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | + | [          inband_mgmt_vlan_name](## ".node_groups.[].nodes.[].inband_mgmt_vlan_name") | String | | `INBAND_MGMT` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [          inband_mgmt_vrf](## ".node_groups.[].nodes.[].inband_mgmt_vrf") | String | | `default` | | VRF configured on the Inband Management Interface.
The VRF is created if not already created by other means.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [          inband_mgmt_mtu](## ".node_groups.[].nodes.[].inband_mgmt_mtu") | Integer | | `1500` | | MTU configured on the Inband Management Interface.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [          inband_ztp](## ".node_groups.[].nodes.[].inband_ztp") | Boolean | | `False` | | Enable to configure upstream device with proper configuration to allow downstream devices to ZTP inband.
This setting also requires that the `inband_mgmt_vlan` is set for the node. | @@ -54,7 +54,7 @@ | [      inband_mgmt_ipv6_subnet](## ".node_groups.[].inband_mgmt_ipv6_subnet") | String | | | Format: ipv6_cidr | Optional IPv6 prefix assigned to inband management SVIs on L2 switches (switches using port-channels as uplinks).
Parent l3leafs will have SVI with "ipv6 virtual-router" and host-route injection based on ARP.
This allows all l3leafs to reuse the same subnet across multiple racks without VXLAN extension.
SVI IP address will be assigned as follows:
virtual-router: + 1
l3leaf A : + 2 (same IP on all l3leaf A)
l3leaf B : + 3 (same IP on all l3leaf B)
l2leafs : + 3 +
GW on l2leafs : + 1
Assign range larger than total l2leafs + 5

Setting is ignored if 'inband_mgmt_ipv6_address' is set.

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [      inband_mgmt_ipv6_gateway](## ".node_groups.[].inband_mgmt_ipv6_gateway") | String | | | Format: ipv6 | Default gateway configured in the 'inband_mgmt_vrf'.
Used when `inband_mgmt_ipv6_address` is set.
Ignored when 'inband_mgmt_ipv6_subnet' is set (first IP in subnet used as gateway).

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [      inband_mgmt_description](## ".node_groups.[].inband_mgmt_description") | String | | `Inband Management` | | Description configured on the Inband Management SVI.

This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | - | [      inband_mgmt_vlan_name](## ".node_groups.[].inband_mgmt_vlan_name") | String | | `Inband Management` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | + | [      inband_mgmt_vlan_name](## ".node_groups.[].inband_mgmt_vlan_name") | String | | `INBAND_MGMT` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [      inband_mgmt_vrf](## ".node_groups.[].inband_mgmt_vrf") | String | | `default` | | VRF configured on the Inband Management Interface.
The VRF is created if not already created by other means.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [      inband_mgmt_mtu](## ".node_groups.[].inband_mgmt_mtu") | Integer | | `1500` | | MTU configured on the Inband Management Interface.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [      inband_ztp](## ".node_groups.[].inband_ztp") | Boolean | | `False` | | Enable to configure upstream device with proper configuration to allow downstream devices to ZTP inband.
This setting also requires that the `inband_mgmt_vlan` is set for the node. | @@ -72,7 +72,7 @@ | [      inband_mgmt_ipv6_subnet](## ".nodes.[].inband_mgmt_ipv6_subnet") | String | | | Format: ipv6_cidr | Optional IPv6 prefix assigned to inband management SVIs on L2 switches (switches using port-channels as uplinks).
Parent l3leafs will have SVI with "ipv6 virtual-router" and host-route injection based on ARP.
This allows all l3leafs to reuse the same subnet across multiple racks without VXLAN extension.
SVI IP address will be assigned as follows:
virtual-router: + 1
l3leaf A : + 2 (same IP on all l3leaf A)
l3leaf B : + 3 (same IP on all l3leaf B)
l2leafs : + 3 +
GW on l2leafs : + 1
Assign range larger than total l2leafs + 5

Setting is ignored if 'inband_mgmt_ipv6_address' is set.

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [      inband_mgmt_ipv6_gateway](## ".nodes.[].inband_mgmt_ipv6_gateway") | String | | | Format: ipv6 | Default gateway configured in the 'inband_mgmt_vrf'.
Used when `inband_mgmt_ipv6_address` is set.
Ignored when 'inband_mgmt_ipv6_subnet' is set (first IP in subnet used as gateway).

This setting is applicable to L2 switches (switches using port-channel trunks as uplinks).
| | [      inband_mgmt_description](## ".nodes.[].inband_mgmt_description") | String | | `Inband Management` | | Description configured on the Inband Management SVI.

This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | - | [      inband_mgmt_vlan_name](## ".nodes.[].inband_mgmt_vlan_name") | String | | `Inband Management` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | + | [      inband_mgmt_vlan_name](## ".nodes.[].inband_mgmt_vlan_name") | String | | `INBAND_MGMT` | | Name configured on the Inband Management VLAN.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [      inband_mgmt_vrf](## ".nodes.[].inband_mgmt_vrf") | String | | `default` | | VRF configured on the Inband Management Interface.
The VRF is created if not already created by other means.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [      inband_mgmt_mtu](## ".nodes.[].inband_mgmt_mtu") | Integer | | `1500` | | MTU configured on the Inband Management Interface.
This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. | | [      inband_ztp](## ".nodes.[].inband_ztp") | Boolean | | `False` | | Enable to configure upstream device with proper configuration to allow downstream devices to ZTP inband.
This setting also requires that the `inband_mgmt_vlan` is set for the node. | @@ -169,7 +169,7 @@ # Name configured on the Inband Management VLAN. # This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - inband_mgmt_vlan_name: + inband_mgmt_vlan_name: # VRF configured on the Inband Management Interface. # The VRF is created if not already created by other means. @@ -282,7 +282,7 @@ # Name configured on the Inband Management VLAN. # This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - inband_mgmt_vlan_name: + inband_mgmt_vlan_name: # VRF configured on the Inband Management Interface. # The VRF is created if not already created by other means. @@ -382,7 +382,7 @@ # Name configured on the Inband Management VLAN. # This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - inband_mgmt_vlan_name: + inband_mgmt_vlan_name: # VRF configured on the Inband Management Interface. # The VRF is created if not already created by other means. @@ -488,7 +488,7 @@ # Name configured on the Inband Management VLAN. # This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - inband_mgmt_vlan_name: + inband_mgmt_vlan_name: # VRF configured on the Inband Management Interface. # The VRF is created if not already created by other means. diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-isis-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-isis-configuration.md index d27941faeb4..c8d5c311cd7 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-isis-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-isis-configuration.md @@ -11,7 +11,7 @@ | [  defaults](## ".defaults") | Dictionary | | | | Define variables for all nodes of this type. | | [    isis_system_id_prefix](## ".defaults.isis_system_id_prefix") | String | | | Pattern: `[0-9a-f]{4}\.[0-9a-f]{4}` | (4.4 hexadecimal). | | [    isis_maximum_paths](## ".defaults.isis_maximum_paths") | Integer | | | | Number of path to configure in ECMP for ISIS. | - | [    is_type](## ".defaults.is_type") | String | | `level-2` | Valid Values:
- level-1-2
- level-1
- level-2 | | + | [    is_type](## ".defaults.is_type") | String | | | Valid Values:
- level-1-2
- level-1
- level-2 | | | [    node_sid_base](## ".defaults.node_sid_base") | Integer | | `0` | | Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. | | [  node_groups](## ".node_groups") | List, items: Dictionary | | | | Define variables related to all nodes part of this group. | | [    - group](## ".node_groups.[].group") | String | Required, Unique | | | The Node Group Name is used for MLAG domain unless set with 'mlag_domain_id'.
The Node Group Name is also used for peer description on downstream switches' uplinks.
| @@ -19,17 +19,17 @@ | [        - name](## ".node_groups.[].nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [          isis_system_id_prefix](## ".node_groups.[].nodes.[].isis_system_id_prefix") | String | | | Pattern: `[0-9a-f]{4}\.[0-9a-f]{4}` | (4.4 hexadecimal). | | [          isis_maximum_paths](## ".node_groups.[].nodes.[].isis_maximum_paths") | Integer | | | | Number of path to configure in ECMP for ISIS. | - | [          is_type](## ".node_groups.[].nodes.[].is_type") | String | | `level-2` | Valid Values:
- level-1-2
- level-1
- level-2 | | + | [          is_type](## ".node_groups.[].nodes.[].is_type") | String | | | Valid Values:
- level-1-2
- level-1
- level-2 | | | [          node_sid_base](## ".node_groups.[].nodes.[].node_sid_base") | Integer | | `0` | | Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. | | [      isis_system_id_prefix](## ".node_groups.[].isis_system_id_prefix") | String | | | Pattern: `[0-9a-f]{4}\.[0-9a-f]{4}` | (4.4 hexadecimal). | | [      isis_maximum_paths](## ".node_groups.[].isis_maximum_paths") | Integer | | | | Number of path to configure in ECMP for ISIS. | - | [      is_type](## ".node_groups.[].is_type") | String | | `level-2` | Valid Values:
- level-1-2
- level-1
- level-2 | | + | [      is_type](## ".node_groups.[].is_type") | String | | | Valid Values:
- level-1-2
- level-1
- level-2 | | | [      node_sid_base](## ".node_groups.[].node_sid_base") | Integer | | `0` | | Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. | | [  nodes](## ".nodes") | List, items: Dictionary | | | | Define variables per node. | | [    - name](## ".nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [      isis_system_id_prefix](## ".nodes.[].isis_system_id_prefix") | String | | | Pattern: `[0-9a-f]{4}\.[0-9a-f]{4}` | (4.4 hexadecimal). | | [      isis_maximum_paths](## ".nodes.[].isis_maximum_paths") | Integer | | | | Number of path to configure in ECMP for ISIS. | - | [      is_type](## ".nodes.[].is_type") | String | | `level-2` | Valid Values:
- level-1-2
- level-1
- level-2 | | + | [      is_type](## ".nodes.[].is_type") | String | | | Valid Values:
- level-1-2
- level-1
- level-2 | | | [      node_sid_base](## ".nodes.[].node_sid_base") | Integer | | `0` | | Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. | === "YAML" @@ -45,7 +45,7 @@ # Number of path to configure in ECMP for ISIS. isis_maximum_paths: - is_type: + is_type: # Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. node_sid_base: @@ -68,7 +68,7 @@ # Number of path to configure in ECMP for ISIS. isis_maximum_paths: - is_type: + is_type: # Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. node_sid_base: @@ -78,7 +78,7 @@ # Number of path to configure in ECMP for ISIS. isis_maximum_paths: - is_type: + is_type: # Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. node_sid_base: @@ -94,7 +94,7 @@ # Number of path to configure in ECMP for ISIS. isis_maximum_paths: - is_type: + is_type: # Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. node_sid_base: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-ptp-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-ptp-configuration.md index a49156bf73b..7aee673bf4a 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-ptp-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-ptp-configuration.md @@ -10,14 +10,14 @@ | [<node_type_keys.key>](## "") | Dictionary | | | | | | [  defaults](## ".defaults") | Dictionary | | | | Define variables for all nodes of this type. | | [    ptp](## ".defaults.ptp") | Dictionary | | | | | - | [      enabled](## ".defaults.ptp.enabled") | Boolean | | `False` | | | - | [      profile](## ".defaults.ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | + | [      enabled](## ".defaults.ptp.enabled") | Boolean | | | | | + | [      profile](## ".defaults.ptp.profile") | String | | | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [      mlag](## ".defaults.ptp.mlag") | Boolean | | `False` | | Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. | | [      domain](## ".defaults.ptp.domain") | Integer | | `127` | Min: 0
Max: 255 | | | [      priority1](## ".defaults.ptp.priority1") | Integer | | | Min: 0
Max: 255 | default -> automatically set based on node_type.
| | [      priority2](## ".defaults.ptp.priority2") | Integer | | | Min: 0
Max: 255 | default -> (node_id modulus 256).
| - | [      auto_clock_identity](## ".defaults.ptp.auto_clock_identity") | Boolean | | `True` | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| - | [      clock_identity_prefix](## ".defaults.ptp.clock_identity_prefix") | String | | | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| + | [      auto_clock_identity](## ".defaults.ptp.auto_clock_identity") | Boolean | | | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| + | [      clock_identity_prefix](## ".defaults.ptp.clock_identity_prefix") | String | | `00:1C:73` | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| | [      clock_identity](## ".defaults.ptp.clock_identity") | String | | | | Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".
| | [      source_ip](## ".defaults.ptp.source_ip") | String | | | | By default in EOS, PTP packets are sourced with an IP address from the routed port or from the relevant SVI, which is the recommended behaviour.
This can be set manually if required, for example, to a value of "10.1.2.3".
| | [      mode](## ".defaults.ptp.mode") | String | | `boundary` | Valid Values:
- boundary | | @@ -51,14 +51,14 @@ | [      nodes](## ".node_groups.[].nodes") | List, items: Dictionary | | | | Define variables per node. | | [        - name](## ".node_groups.[].nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [          ptp](## ".node_groups.[].nodes.[].ptp") | Dictionary | | | | | - | [            enabled](## ".node_groups.[].nodes.[].ptp.enabled") | Boolean | | `False` | | | - | [            profile](## ".node_groups.[].nodes.[].ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | + | [            enabled](## ".node_groups.[].nodes.[].ptp.enabled") | Boolean | | | | | + | [            profile](## ".node_groups.[].nodes.[].ptp.profile") | String | | | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [            mlag](## ".node_groups.[].nodes.[].ptp.mlag") | Boolean | | `False` | | Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. | | [            domain](## ".node_groups.[].nodes.[].ptp.domain") | Integer | | `127` | Min: 0
Max: 255 | | | [            priority1](## ".node_groups.[].nodes.[].ptp.priority1") | Integer | | | Min: 0
Max: 255 | default -> automatically set based on node_type.
| | [            priority2](## ".node_groups.[].nodes.[].ptp.priority2") | Integer | | | Min: 0
Max: 255 | default -> (node_id modulus 256).
| - | [            auto_clock_identity](## ".node_groups.[].nodes.[].ptp.auto_clock_identity") | Boolean | | `True` | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| - | [            clock_identity_prefix](## ".node_groups.[].nodes.[].ptp.clock_identity_prefix") | String | | | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| + | [            auto_clock_identity](## ".node_groups.[].nodes.[].ptp.auto_clock_identity") | Boolean | | | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| + | [            clock_identity_prefix](## ".node_groups.[].nodes.[].ptp.clock_identity_prefix") | String | | `00:1C:73` | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| | [            clock_identity](## ".node_groups.[].nodes.[].ptp.clock_identity") | String | | | | Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".
| | [            source_ip](## ".node_groups.[].nodes.[].ptp.source_ip") | String | | | | By default in EOS, PTP packets are sourced with an IP address from the routed port or from the relevant SVI, which is the recommended behaviour.
This can be set manually if required, for example, to a value of "10.1.2.3".
| | [            mode](## ".node_groups.[].nodes.[].ptp.mode") | String | | `boundary` | Valid Values:
- boundary | | @@ -88,14 +88,14 @@ | [                  follow_up](## ".node_groups.[].nodes.[].ptp.monitor.missing_message.sequence_ids.follow_up") | Integer | | `3` | Min: 2
Max: 255 | | | [                  sync](## ".node_groups.[].nodes.[].ptp.monitor.missing_message.sequence_ids.sync") | Integer | | `3` | Min: 2
Max: 255 | | | [      ptp](## ".node_groups.[].ptp") | Dictionary | | | | | - | [        enabled](## ".node_groups.[].ptp.enabled") | Boolean | | `False` | | | - | [        profile](## ".node_groups.[].ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | + | [        enabled](## ".node_groups.[].ptp.enabled") | Boolean | | | | | + | [        profile](## ".node_groups.[].ptp.profile") | String | | | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [        mlag](## ".node_groups.[].ptp.mlag") | Boolean | | `False` | | Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. | | [        domain](## ".node_groups.[].ptp.domain") | Integer | | `127` | Min: 0
Max: 255 | | | [        priority1](## ".node_groups.[].ptp.priority1") | Integer | | | Min: 0
Max: 255 | default -> automatically set based on node_type.
| | [        priority2](## ".node_groups.[].ptp.priority2") | Integer | | | Min: 0
Max: 255 | default -> (node_id modulus 256).
| - | [        auto_clock_identity](## ".node_groups.[].ptp.auto_clock_identity") | Boolean | | `True` | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| - | [        clock_identity_prefix](## ".node_groups.[].ptp.clock_identity_prefix") | String | | | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| + | [        auto_clock_identity](## ".node_groups.[].ptp.auto_clock_identity") | Boolean | | | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| + | [        clock_identity_prefix](## ".node_groups.[].ptp.clock_identity_prefix") | String | | `00:1C:73` | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| | [        clock_identity](## ".node_groups.[].ptp.clock_identity") | String | | | | Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".
| | [        source_ip](## ".node_groups.[].ptp.source_ip") | String | | | | By default in EOS, PTP packets are sourced with an IP address from the routed port or from the relevant SVI, which is the recommended behaviour.
This can be set manually if required, for example, to a value of "10.1.2.3".
| | [        mode](## ".node_groups.[].ptp.mode") | String | | `boundary` | Valid Values:
- boundary | | @@ -127,14 +127,14 @@ | [  nodes](## ".nodes") | List, items: Dictionary | | | | Define variables per node. | | [    - name](## ".nodes.[].name") | String | Required, Unique | | | The Node Name is used as "hostname". | | [      ptp](## ".nodes.[].ptp") | Dictionary | | | | | - | [        enabled](## ".nodes.[].ptp.enabled") | Boolean | | `False` | | | - | [        profile](## ".nodes.[].ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | + | [        enabled](## ".nodes.[].ptp.enabled") | Boolean | | | | | + | [        profile](## ".nodes.[].ptp.profile") | String | | | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [        mlag](## ".nodes.[].ptp.mlag") | Boolean | | `False` | | Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. | | [        domain](## ".nodes.[].ptp.domain") | Integer | | `127` | Min: 0
Max: 255 | | | [        priority1](## ".nodes.[].ptp.priority1") | Integer | | | Min: 0
Max: 255 | default -> automatically set based on node_type.
| | [        priority2](## ".nodes.[].ptp.priority2") | Integer | | | Min: 0
Max: 255 | default -> (node_id modulus 256).
| - | [        auto_clock_identity](## ".nodes.[].ptp.auto_clock_identity") | Boolean | | `True` | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| - | [        clock_identity_prefix](## ".nodes.[].ptp.clock_identity_prefix") | String | | | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| + | [        auto_clock_identity](## ".nodes.[].ptp.auto_clock_identity") | Boolean | | | | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity.
default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX).
| + | [        clock_identity_prefix](## ".nodes.[].ptp.clock_identity_prefix") | String | | `00:1C:73` | | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03".
By default the 3-byte prefix is "00:1C:73".
This can be overridden if auto_clock_identity is set to true (which is the default).
| | [        clock_identity](## ".nodes.[].ptp.clock_identity") | String | | | | Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".
| | [        source_ip](## ".nodes.[].ptp.source_ip") | String | | | | By default in EOS, PTP packets are sourced with an IP address from the routed port or from the relevant SVI, which is the recommended behaviour.
This can be set manually if required, for example, to a value of "10.1.2.3".
| | [        mode](## ".nodes.[].ptp.mode") | String | | `boundary` | Valid Values:
- boundary | | @@ -172,13 +172,13 @@ # Define variables for all nodes of this type. defaults: ptp: - enabled: + enabled: # Default available profiles are: # - "aes67" # - "aes67-r16-2016" # - "smpte2059-2" - profile: + profile: # Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. mlag: @@ -192,12 +192,12 @@ # If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. # default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - auto_clock_identity: + auto_clock_identity: # PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". # By default the 3-byte prefix is "00:1C:73". # This can be overridden if auto_clock_identity is set to true (which is the default). - clock_identity_prefix: + clock_identity_prefix: # Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06". clock_identity: @@ -247,13 +247,13 @@ # The Node Name is used as "hostname". - name: ptp: - enabled: + enabled: # Default available profiles are: # - "aes67" # - "aes67-r16-2016" # - "smpte2059-2" - profile: + profile: # Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. mlag: @@ -267,12 +267,12 @@ # If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. # default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - auto_clock_identity: + auto_clock_identity: # PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". # By default the 3-byte prefix is "00:1C:73". # This can be overridden if auto_clock_identity is set to true (which is the default). - clock_identity_prefix: + clock_identity_prefix: # Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06". clock_identity: @@ -309,13 +309,13 @@ follow_up: sync: ptp: - enabled: + enabled: # Default available profiles are: # - "aes67" # - "aes67-r16-2016" # - "smpte2059-2" - profile: + profile: # Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. mlag: @@ -329,12 +329,12 @@ # If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. # default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - auto_clock_identity: + auto_clock_identity: # PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". # By default the 3-byte prefix is "00:1C:73". # This can be overridden if auto_clock_identity is set to true (which is the default). - clock_identity_prefix: + clock_identity_prefix: # Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06". clock_identity: @@ -377,13 +377,13 @@ # The Node Name is used as "hostname". - name: ptp: - enabled: + enabled: # Default available profiles are: # - "aes67" # - "aes67-r16-2016" # - "smpte2059-2" - profile: + profile: # Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. mlag: @@ -397,12 +397,12 @@ # If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. # default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - auto_clock_identity: + auto_clock_identity: # PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". # By default the 3-byte prefix is "00:1C:73". # This can be overridden if auto_clock_identity is set to true (which is the default). - clock_identity_prefix: + clock_identity_prefix: # Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06". clock_identity: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-uplink-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-uplink-configuration.md index 3b170ee80d2..da5f450011b 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-uplink-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-uplink-configuration.md @@ -15,7 +15,7 @@ | [        - name](## ".defaults.link_tracking.groups.[].name") | String | | | | Tracking group name. | | [          recovery_delay](## ".defaults.link_tracking.groups.[].recovery_delay") | Integer | | | Min: 0
Max: 3600 | default -> platform_settings_mlag_reload_delay -> 300. | | [          links_minimum](## ".defaults.link_tracking.groups.[].links_minimum") | Integer | | | Min: 1
Max: 100000 | | - | [    uplink_type](## ".defaults.uplink_type") | String | | `p2p` | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | + | [    uplink_type](## ".defaults.uplink_type") | String | | | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | | [    uplink_ipv4_pool](## ".defaults.uplink_ipv4_pool") | String | | | Format: ipv4_cidr | IPv4 subnet to use to connect to uplink switches. | | [    uplink_interfaces](## ".defaults.uplink_interfaces") | List, items: String | | | | Local uplink interfaces.
Each list item supports range syntax that can be expanded into a list of interfaces.
If uplink_interfaces is not defined, platform-specific defaults (defined under default_interfaces) will be used instead.
Please note that default_interfaces are not defined by default, you should define these yourself.
| | [      - <str>](## ".defaults.uplink_interfaces.[]") | String | | | Pattern: `Ethernet[\d/]+` | | @@ -52,7 +52,7 @@ | [              - name](## ".node_groups.[].nodes.[].link_tracking.groups.[].name") | String | | | | Tracking group name. | | [                recovery_delay](## ".node_groups.[].nodes.[].link_tracking.groups.[].recovery_delay") | Integer | | | Min: 0
Max: 3600 | default -> platform_settings_mlag_reload_delay -> 300. | | [                links_minimum](## ".node_groups.[].nodes.[].link_tracking.groups.[].links_minimum") | Integer | | | Min: 1
Max: 100000 | | - | [          uplink_type](## ".node_groups.[].nodes.[].uplink_type") | String | | `p2p` | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | + | [          uplink_type](## ".node_groups.[].nodes.[].uplink_type") | String | | | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | | [          uplink_ipv4_pool](## ".node_groups.[].nodes.[].uplink_ipv4_pool") | String | | | Format: ipv4_cidr | IPv4 subnet to use to connect to uplink switches. | | [          uplink_interfaces](## ".node_groups.[].nodes.[].uplink_interfaces") | List, items: String | | | | Local uplink interfaces.
Each list item supports range syntax that can be expanded into a list of interfaces.
If uplink_interfaces is not defined, platform-specific defaults (defined under default_interfaces) will be used instead.
Please note that default_interfaces are not defined by default, you should define these yourself.
| | [            - <str>](## ".node_groups.[].nodes.[].uplink_interfaces.[]") | String | | | Pattern: `Ethernet[\d/]+` | | @@ -81,7 +81,7 @@ | [          - name](## ".node_groups.[].link_tracking.groups.[].name") | String | | | | Tracking group name. | | [            recovery_delay](## ".node_groups.[].link_tracking.groups.[].recovery_delay") | Integer | | | Min: 0
Max: 3600 | default -> platform_settings_mlag_reload_delay -> 300. | | [            links_minimum](## ".node_groups.[].link_tracking.groups.[].links_minimum") | Integer | | | Min: 1
Max: 100000 | | - | [      uplink_type](## ".node_groups.[].uplink_type") | String | | `p2p` | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | + | [      uplink_type](## ".node_groups.[].uplink_type") | String | | | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | | [      uplink_ipv4_pool](## ".node_groups.[].uplink_ipv4_pool") | String | | | Format: ipv4_cidr | IPv4 subnet to use to connect to uplink switches. | | [      uplink_interfaces](## ".node_groups.[].uplink_interfaces") | List, items: String | | | | Local uplink interfaces.
Each list item supports range syntax that can be expanded into a list of interfaces.
If uplink_interfaces is not defined, platform-specific defaults (defined under default_interfaces) will be used instead.
Please note that default_interfaces are not defined by default, you should define these yourself.
| | [        - <str>](## ".node_groups.[].uplink_interfaces.[]") | String | | | Pattern: `Ethernet[\d/]+` | | @@ -116,7 +116,7 @@ | [          - name](## ".nodes.[].link_tracking.groups.[].name") | String | | | | Tracking group name. | | [            recovery_delay](## ".nodes.[].link_tracking.groups.[].recovery_delay") | Integer | | | Min: 0
Max: 3600 | default -> platform_settings_mlag_reload_delay -> 300. | | [            links_minimum](## ".nodes.[].link_tracking.groups.[].links_minimum") | Integer | | | Min: 1
Max: 100000 | | - | [      uplink_type](## ".nodes.[].uplink_type") | String | | `p2p` | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | + | [      uplink_type](## ".nodes.[].uplink_type") | String | | | Valid Values:
- p2p
- port-channel
- p2p-vrfs
- lan | Override the default `uplink_type` set at the `node_type_key` level.
`uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. | | [      uplink_ipv4_pool](## ".nodes.[].uplink_ipv4_pool") | String | | | Format: ipv4_cidr | IPv4 subnet to use to connect to uplink switches. | | [      uplink_interfaces](## ".nodes.[].uplink_interfaces") | List, items: String | | | | Local uplink interfaces.
Each list item supports range syntax that can be expanded into a list of interfaces.
If uplink_interfaces is not defined, platform-specific defaults (defined under default_interfaces) will be used instead.
Please note that default_interfaces are not defined by default, you should define these yourself.
| | [        - <str>](## ".nodes.[].uplink_interfaces.[]") | String | | | Pattern: `Ethernet[\d/]+` | | @@ -167,7 +167,7 @@ # Override the default `uplink_type` set at the `node_type_key` level. # `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - uplink_type: + uplink_type: # IPv4 subnet to use to connect to uplink switches. uplink_ipv4_pool: @@ -303,7 +303,7 @@ # Override the default `uplink_type` set at the `node_type_key` level. # `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - uplink_type: + uplink_type: # IPv4 subnet to use to connect to uplink switches. uplink_ipv4_pool: @@ -416,7 +416,7 @@ # Override the default `uplink_type` set at the `node_type_key` level. # `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - uplink_type: + uplink_type: # IPv4 subnet to use to connect to uplink switches. uplink_ipv4_pool: @@ -545,7 +545,7 @@ # Override the default `uplink_type` set at the `node_type_key` level. # `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - uplink_type: + uplink_type: # IPv4 subnet to use to connect to uplink switches. uplink_ipv4_pool: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-wan-configuration.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-wan-configuration.md index 6ead910b46a..0242bae6ba6 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-wan-configuration.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/node-type-wan-configuration.md @@ -14,7 +14,7 @@ | [    cv_pathfinder_region](## ".defaults.cv_pathfinder_region") | String | | | | The CV Pathfinder region name.
This key is required for WAN routers but optional for pathfinders.
The region name must be defined under 'cv_pathfinder_regions'. | | [    cv_pathfinder_site](## ".defaults.cv_pathfinder_site") | String | | | | The CV Pathfinder site name.
This key is required for WAN routers but optional for pathfinders.
For WAN routers and pathfinders with `cv_pathfinder_region`, the site name must be defined for the relevant region under 'cv_pathfinder_regions'.
For pathfinders without `cv_pathfinder_region` set, the site must be defined under `cv_pathfinder_global_sites`. | | [    wan_ha](## ".defaults.wan_ha") | Dictionary | | | | PREVIEW: This key is currently not supported

The key is supported only if `wan_mode` == `cv-pathfinder`.
AutoVPN support is still to be determined.

Maximum 2 devices supported by group for HA. | - | [      enabled](## ".defaults.wan_ha.enabled") | Boolean | | `True` | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | + | [      enabled](## ".defaults.wan_ha.enabled") | Boolean | | | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | | [      ipsec](## ".defaults.wan_ha.ipsec") | Boolean | | `True` | | Enable / Disable IPsec over HA path-group when HA is enabled. | | [      mtu](## ".defaults.wan_ha.mtu") | Integer | | `9194` | Min: 68
Max: 65535 | Set MTU on WAN HA interfaces. | | [      ha_interfaces](## ".defaults.wan_ha.ha_interfaces") | List, items: String | | | | Local WAN HA interfaces
Overwrite the default behavior which is to pick all the `uplink_interfaces`.
Can be used to filter uplink interfaces when there are multiple uplinks.
Limitations:
Either all interfaces must be uplinks or all interfaces must not be uplinks.
Only one interface is supported for non uplinks. | @@ -36,7 +36,7 @@ | [          cv_pathfinder_region](## ".node_groups.[].nodes.[].cv_pathfinder_region") | String | | | | The CV Pathfinder region name.
This key is required for WAN routers but optional for pathfinders.
The region name must be defined under 'cv_pathfinder_regions'. | | [          cv_pathfinder_site](## ".node_groups.[].nodes.[].cv_pathfinder_site") | String | | | | The CV Pathfinder site name.
This key is required for WAN routers but optional for pathfinders.
For WAN routers and pathfinders with `cv_pathfinder_region`, the site name must be defined for the relevant region under 'cv_pathfinder_regions'.
For pathfinders without `cv_pathfinder_region` set, the site must be defined under `cv_pathfinder_global_sites`. | | [          wan_ha](## ".node_groups.[].nodes.[].wan_ha") | Dictionary | | | | PREVIEW: This key is currently not supported

The key is supported only if `wan_mode` == `cv-pathfinder`.
AutoVPN support is still to be determined.

Maximum 2 devices supported by group for HA. | - | [            enabled](## ".node_groups.[].nodes.[].wan_ha.enabled") | Boolean | | `True` | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | + | [            enabled](## ".node_groups.[].nodes.[].wan_ha.enabled") | Boolean | | | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | | [            ipsec](## ".node_groups.[].nodes.[].wan_ha.ipsec") | Boolean | | `True` | | Enable / Disable IPsec over HA path-group when HA is enabled. | | [            mtu](## ".node_groups.[].nodes.[].wan_ha.mtu") | Integer | | `9194` | Min: 68
Max: 65535 | Set MTU on WAN HA interfaces. | | [            ha_interfaces](## ".node_groups.[].nodes.[].wan_ha.ha_interfaces") | List, items: String | | | | Local WAN HA interfaces
Overwrite the default behavior which is to pick all the `uplink_interfaces`.
Can be used to filter uplink interfaces when there are multiple uplinks.
Limitations:
Either all interfaces must be uplinks or all interfaces must not be uplinks.
Only one interface is supported for non uplinks. | @@ -54,7 +54,7 @@ | [      cv_pathfinder_region](## ".node_groups.[].cv_pathfinder_region") | String | | | | The CV Pathfinder region name.
This key is required for WAN routers but optional for pathfinders.
The region name must be defined under 'cv_pathfinder_regions'. | | [      cv_pathfinder_site](## ".node_groups.[].cv_pathfinder_site") | String | | | | The CV Pathfinder site name.
This key is required for WAN routers but optional for pathfinders.
For WAN routers and pathfinders with `cv_pathfinder_region`, the site name must be defined for the relevant region under 'cv_pathfinder_regions'.
For pathfinders without `cv_pathfinder_region` set, the site must be defined under `cv_pathfinder_global_sites`. | | [      wan_ha](## ".node_groups.[].wan_ha") | Dictionary | | | | PREVIEW: This key is currently not supported

The key is supported only if `wan_mode` == `cv-pathfinder`.
AutoVPN support is still to be determined.

Maximum 2 devices supported by group for HA. | - | [        enabled](## ".node_groups.[].wan_ha.enabled") | Boolean | | `True` | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | + | [        enabled](## ".node_groups.[].wan_ha.enabled") | Boolean | | | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | | [        ipsec](## ".node_groups.[].wan_ha.ipsec") | Boolean | | `True` | | Enable / Disable IPsec over HA path-group when HA is enabled. | | [        mtu](## ".node_groups.[].wan_ha.mtu") | Integer | | `9194` | Min: 68
Max: 65535 | Set MTU on WAN HA interfaces. | | [        ha_interfaces](## ".node_groups.[].wan_ha.ha_interfaces") | List, items: String | | | | Local WAN HA interfaces
Overwrite the default behavior which is to pick all the `uplink_interfaces`.
Can be used to filter uplink interfaces when there are multiple uplinks.
Limitations:
Either all interfaces must be uplinks or all interfaces must not be uplinks.
Only one interface is supported for non uplinks. | @@ -74,7 +74,7 @@ | [      cv_pathfinder_region](## ".nodes.[].cv_pathfinder_region") | String | | | | The CV Pathfinder region name.
This key is required for WAN routers but optional for pathfinders.
The region name must be defined under 'cv_pathfinder_regions'. | | [      cv_pathfinder_site](## ".nodes.[].cv_pathfinder_site") | String | | | | The CV Pathfinder site name.
This key is required for WAN routers but optional for pathfinders.
For WAN routers and pathfinders with `cv_pathfinder_region`, the site name must be defined for the relevant region under 'cv_pathfinder_regions'.
For pathfinders without `cv_pathfinder_region` set, the site must be defined under `cv_pathfinder_global_sites`. | | [      wan_ha](## ".nodes.[].wan_ha") | Dictionary | | | | PREVIEW: This key is currently not supported

The key is supported only if `wan_mode` == `cv-pathfinder`.
AutoVPN support is still to be determined.

Maximum 2 devices supported by group for HA. | - | [        enabled](## ".nodes.[].wan_ha.enabled") | Boolean | | `True` | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | + | [        enabled](## ".nodes.[].wan_ha.enabled") | Boolean | | | | Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. | | [        ipsec](## ".nodes.[].wan_ha.ipsec") | Boolean | | `True` | | Enable / Disable IPsec over HA path-group when HA is enabled. | | [        mtu](## ".nodes.[].wan_ha.mtu") | Integer | | `9194` | Min: 68
Max: 65535 | Set MTU on WAN HA interfaces. | | [        ha_interfaces](## ".nodes.[].wan_ha.ha_interfaces") | List, items: String | | | | Local WAN HA interfaces
Overwrite the default behavior which is to pick all the `uplink_interfaces`.
Can be used to filter uplink interfaces when there are multiple uplinks.
Limitations:
Either all interfaces must be uplinks or all interfaces must not be uplinks.
Only one interface is supported for non uplinks. | @@ -131,7 +131,7 @@ wan_ha: # Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - enabled: + enabled: # Enable / Disable IPsec over HA path-group when HA is enabled. ipsec: @@ -222,7 +222,7 @@ wan_ha: # Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - enabled: + enabled: # Enable / Disable IPsec over HA path-group when HA is enabled. ipsec: @@ -300,7 +300,7 @@ wan_ha: # Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - enabled: + enabled: # Enable / Disable IPsec over HA path-group when HA is enabled. ipsec: @@ -384,7 +384,7 @@ wan_ha: # Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - enabled: + enabled: # Enable / Disable IPsec over HA path-group when HA is enabled. ipsec: diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/__init__.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/__init__.py index 1c699963719..50f7c36db4b 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/__init__.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/__init__.py @@ -107,10 +107,10 @@ def uplink_ipv4_pool(self) -> str | None: return None @cached_property - def downlink_pools(self) -> dict | None: + def downlink_pools(self) -> list | None: """Exposed in avd_switch_facts.""" if self.shared_utils.underlay_router: - return self.shared_utils.downlink_pools + return [downlink_pool._as_dict() for downlink_pool in self.shared_utils.node_config.downlink_pools] return None @cached_property diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/overlay.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/overlay.py index e3f25e0f698..b9c22fa0d4f 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/overlay.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/overlay.py @@ -6,8 +6,6 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import get - if TYPE_CHECKING: from . import EosDesignsFacts @@ -40,8 +38,8 @@ def evpn_route_servers(self: EosDesignsFacts) -> list: """ if self.shared_utils.underlay_router is True: if self.evpn_role == "client": - return get(self.shared_utils.switch_data_combined, "evpn_route_servers", default=self.shared_utils.uplink_switches) - return get(self.shared_utils.switch_data_combined, "evpn_route_servers") + return self.shared_utils.node_config.evpn_route_servers or self.shared_utils.uplink_switches + return self.shared_utils.node_config.evpn_route_servers return [] @cached_property @@ -50,7 +48,7 @@ def mpls_route_reflectors(self: EosDesignsFacts) -> list | None: if self.shared_utils.underlay_router is True and ( self.mpls_overlay_role in ["client", "server"] or (self.evpn_role in ["client", "server"] and self.overlay["evpn_mpls"]) ): - return get(self.shared_utils.switch_data_combined, "mpls_route_reflectors") + return self.shared_utils.node_config.mpls_route_reflectors return None @cached_property diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/short_esi.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/short_esi.py index 1ce8b8f2e0a..7ce205e696b 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/short_esi.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/short_esi.py @@ -8,7 +8,7 @@ from hashlib import sha256 from typing import TYPE_CHECKING -from pyavd._utils import default, get +from pyavd._utils import default if TYPE_CHECKING: from . import EosDesignsFacts @@ -23,7 +23,7 @@ class ShortEsiMixin: """ @cached_property - def _short_esi(self: EosDesignsFacts) -> str: + def _short_esi(self: EosDesignsFacts) -> str | None: """ If short_esi is set to "auto" we will use sha256 to create a unique short_esi value based on various uplink information. @@ -33,11 +33,11 @@ def _short_esi(self: EosDesignsFacts) -> str: # On the MLAG Secondary use short-esi from MLAG primary if self.shared_utils.mlag_role == "secondary" and (peer_short_esi := self.shared_utils.mlag_peer_facts._short_esi) is not None: return peer_short_esi - short_esi = get(self.shared_utils.switch_data_combined, "short_esi") + short_esi = self.shared_utils.node_config.short_esi if short_esi == "auto": esi_seed_1 = "".join(self.shared_utils.uplink_switches[:2]) esi_seed_2 = "".join(self.shared_utils.uplink_switch_interfaces[:2]) - esi_seed_3 = "".join(default(self.shared_utils.uplink_interfaces, [])[:2]) + esi_seed_3 = "".join(self.shared_utils.uplink_interfaces[:2]) esi_seed_4 = default(self.shared_utils.group, "") esi_hash = sha256(f"{esi_seed_1}{esi_seed_2}{esi_seed_3}{esi_seed_4}".encode()).hexdigest() short_esi = re.sub(r"([0-9a-f]{4})", r"\1:", esi_hash)[:14] diff --git a/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py b/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py index f47f95bf66a..75256e3e434 100644 --- a/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py +++ b/python-avd/pyavd/_eos_designs/eos_designs_facts/uplinks.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdError -from pyavd._utils import append_if_not_duplicate, get +from pyavd._utils import append_if_not_duplicate from pyavd.j2filters import list_compress, natural_sort, range_expand if TYPE_CHECKING: @@ -45,7 +45,7 @@ def _uplink_port_channel_id(self: EosDesignsFacts) -> int: For MLAG primary validate that the port-channel id falls within 1-2000 since we also use this ID as MLAG ID. """ - uplink_port_channel_id = get(self.shared_utils.switch_data_combined, "uplink_port_channel_id") + uplink_port_channel_id = self.shared_utils.node_config.uplink_port_channel_id if self.shared_utils.mlag_role == "secondary": # MLAG Secondary @@ -84,7 +84,7 @@ def _uplink_switch_port_channel_id(self: EosDesignsFacts) -> int: If the *uplink_switch* is in MLAG, validate that the port-channel id falls within 1-2000 since we also use this ID as MLAG ID on the *uplink switch*. """ - uplink_switch_port_channel_id = get(self.shared_utils.switch_data_combined, "uplink_switch_port_channel_id") + uplink_switch_port_channel_id = self.shared_utils.node_config.uplink_switch_port_channel_id if self.shared_utils.mlag_role == "secondary": # MLAG Secondary @@ -284,7 +284,7 @@ def _get_l2_uplink( uplink["vlans"] = list_compress(list(uplink_vlans)) if uplink_vlans else "none" - if uplink_native_vlan := get(self.shared_utils.switch_data_combined, "uplink_native_vlan"): + if uplink_native_vlan := self.shared_utils.node_config.uplink_native_vlan: uplink["native_vlan"] = uplink_native_vlan if self._short_esi is not None: diff --git a/python-avd/pyavd/_eos_designs/schema/__init__.py b/python-avd/pyavd/_eos_designs/schema/__init__.py index c05df2f49af..a5ae9958949 100644 --- a/python-avd/pyavd/_eos_designs/schema/__init__.py +++ b/python-avd/pyavd/_eos_designs/schema/__init__.py @@ -17702,10 +17702,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -17720,7 +17720,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -17744,6 +17744,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -17806,7 +17811,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -17814,7 +17819,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -17831,7 +17836,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -17909,15 +17914,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -17948,15 +17958,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -17964,7 +17972,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -17975,9 +17983,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -18281,14 +18289,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -18302,16 +18310,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -18326,22 +18331,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -18370,14 +18375,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -18470,7 +18475,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -18484,12 +18489,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -18545,7 +18546,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -19085,7 +19086,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -19108,7 +19109,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -19125,7 +19126,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -19155,7 +19156,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -19250,13 +19251,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -19415,8 +19414,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -19503,12 +19501,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -19755,7 +19749,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -19898,7 +19892,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -19921,7 +19915,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -19938,7 +19932,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -20840,10 +20834,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -20858,7 +20852,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -20882,6 +20876,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -20946,7 +20945,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -20954,7 +20953,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -20971,7 +20970,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -21049,15 +21048,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -21088,15 +21092,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -21104,7 +21106,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -21115,9 +21117,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -21421,14 +21423,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -21442,16 +21444,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -21466,22 +21465,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -21510,14 +21509,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -21610,7 +21609,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -21624,12 +21623,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -21685,7 +21680,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -22229,7 +22224,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -22252,7 +22247,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -22269,7 +22264,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -22299,7 +22294,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -22401,13 +22396,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -22566,8 +22559,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -22654,12 +22646,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -22906,7 +22894,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -23051,7 +23039,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -23074,7 +23062,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -23091,7 +23079,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -23958,10 +23946,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -23976,7 +23964,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -24000,6 +23988,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -24062,7 +24055,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -24070,7 +24063,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -24087,7 +24080,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -24165,15 +24158,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -24204,15 +24202,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -24220,7 +24216,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -24231,9 +24227,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -24537,14 +24533,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -24558,16 +24554,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -24582,22 +24575,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -24626,14 +24619,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -24726,7 +24719,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -24740,12 +24733,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -24801,7 +24790,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -25343,7 +25332,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -25366,7 +25355,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -25383,7 +25372,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -25413,7 +25402,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -25516,13 +25505,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -25681,8 +25668,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -25769,12 +25755,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -26021,7 +26003,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -26166,7 +26148,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -26189,7 +26171,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -26206,7 +26188,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -27117,10 +27099,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -27135,7 +27117,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -27159,6 +27141,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -27221,7 +27208,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -27229,7 +27216,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -27246,7 +27233,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -27324,15 +27311,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -27363,15 +27355,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -27379,7 +27369,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -27390,9 +27380,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -27696,14 +27686,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -27717,16 +27707,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -27741,22 +27728,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -27785,14 +27772,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -27885,7 +27872,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -27899,12 +27886,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -27960,7 +27943,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -28502,7 +28485,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -28525,7 +28508,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -28542,7 +28525,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -28572,7 +28555,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -28674,13 +28657,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -28839,8 +28820,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -28927,12 +28907,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -29179,7 +29155,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -29324,7 +29300,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -29347,7 +29323,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -29364,7 +29340,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -36129,10 +36105,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -36147,7 +36123,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -36171,6 +36147,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -36233,7 +36214,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -36241,7 +36222,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -36258,7 +36239,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -36336,15 +36317,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -36375,15 +36361,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -36391,7 +36375,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -36402,9 +36386,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -36708,14 +36692,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -36729,16 +36713,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -36753,22 +36734,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -36797,14 +36778,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -36897,7 +36878,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -36911,12 +36892,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -36972,7 +36949,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -37512,7 +37489,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -37535,7 +37512,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -37552,7 +37529,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -37582,7 +37559,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -37677,13 +37654,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -37842,8 +37817,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -37930,12 +37904,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -38182,7 +38152,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -38325,7 +38295,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -38348,7 +38318,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -38365,7 +38335,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -39267,10 +39237,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -39285,7 +39255,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -39309,6 +39279,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -39373,7 +39348,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -39381,7 +39356,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -39398,7 +39373,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -39476,15 +39451,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -39515,15 +39495,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -39531,7 +39509,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -39542,9 +39520,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -39848,14 +39826,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -39869,16 +39847,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -39893,22 +39868,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -39937,14 +39912,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -40037,7 +40012,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -40051,12 +40026,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -40112,7 +40083,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -40656,7 +40627,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -40679,7 +40650,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -40696,7 +40667,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -40726,7 +40697,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -40828,13 +40799,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -40993,8 +40962,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -41081,12 +41049,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -41333,7 +41297,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -41478,7 +41442,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -41501,7 +41465,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -41518,7 +41482,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -42385,10 +42349,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -42403,7 +42367,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -42427,6 +42391,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -42489,7 +42458,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -42497,7 +42466,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -42514,7 +42483,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -42592,15 +42561,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -42631,15 +42605,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -42647,7 +42619,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -42658,9 +42630,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -42964,14 +42936,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -42985,16 +42957,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -43009,22 +42978,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -43053,14 +43022,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -43153,7 +43122,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -43167,12 +43136,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -43228,7 +43193,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -43770,7 +43735,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -43793,7 +43758,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -43810,7 +43775,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -43840,7 +43805,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -43943,13 +43908,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -44108,8 +44071,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -44196,12 +44158,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -44448,7 +44406,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -44593,7 +44551,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -44616,7 +44574,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -44633,7 +44591,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, @@ -45544,10 +45502,10 @@ class RemotePeersItem(AvdModel): "bgp_as": {"type": str}, "_custom_data": {"type": dict}, } - _required_fields: ClassVar[tuple] = ("_custom_data",) + _required_fields: ClassVar[tuple] = ("hostname", "_custom_data") _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - hostname: str | None + hostname: str """Hostname of remote EVPN GW server.""" ip_address: str | None """Peering IP of remote Route Server.""" @@ -45562,7 +45520,7 @@ class RemotePeersItem(AvdModel): def __init__( self, *, - hostname: str | None | UndefinedType = Undefined, + hostname: str | UndefinedType = Undefined, ip_address: str | None | UndefinedType = Undefined, bgp_as: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -45586,6 +45544,11 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + class EvpnL2(AvdModel): _fields: ClassVar[dict] = {"enabled": {"type": bool, "default": False}, "_custom_data": {"type": dict}} _required_fields: ClassVar[tuple] = ("_custom_data",) @@ -45648,7 +45611,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "evpn_l2": {"type": EvpnL2}, "evpn_l3": {"type": EvpnL3}, "_custom_data": {"type": dict}, @@ -45656,7 +45619,7 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers """ Define remote peers of the EVPN VXLAN Gateway. If the hostname can be found in the inventory, @@ -45673,7 +45636,7 @@ def __init__( def __init__( self, *, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, evpn_l2: EvpnL2 | UndefinedType = Undefined, evpn_l3: EvpnL3 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, @@ -45751,15 +45714,20 @@ def __init__( continue setattr(self, arg, arg_value) + class RemotePeers(AvdIndexedList[str, RemotePeersItem]): + _primary_key: ClassVar[str] = "hostname" + + RemotePeers._item_type = RemotePeersItem + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "evpn_domain_id": {"type": str, "default": "65535:1"}, "ipvpn_domain_id": {"type": str, "default": "65535:2"}, "enable_d_path": {"type": bool, "default": True}, "maximum_routes": {"type": int, "default": 0}, - "local_as": {"type": str, "default": "none"}, + "local_as": {"type": str}, "address_families": {"type": list, "items": str, "default": ["vpn-ipv4"]}, - "remote_peers": {"type": list, "items": RemotePeersItem}, + "remote_peers": {"type": RemotePeers}, "_custom_data": {"type": dict}, } _required_fields: ClassVar[tuple] = ("enabled", "_custom_data") @@ -45790,15 +45758,13 @@ def __init__( Default value: `0` """ - local_as: str + local_as: str | None """ Local BGP AS applied to peering with IPVPN remote peers. BGP AS <1-4294967295> or AS number in asdot notation "<1-65535>.<0-65535>". For asdot notation in YAML inputs, the value must be put in quotes, to prevent it from being interpreted as a float number. - - Default value: `"none"` """ address_families: list[str] """ @@ -45806,7 +45772,7 @@ def __init__( Default value: `["vpn-ipv4"]` """ - remote_peers: list[RemotePeersItem] + remote_peers: RemotePeers _custom_data: dict[str, Any] def __init__( @@ -45817,9 +45783,9 @@ def __init__( ipvpn_domain_id: str | UndefinedType = Undefined, enable_d_path: bool | UndefinedType = Undefined, maximum_routes: int | UndefinedType = Undefined, - local_as: str | UndefinedType = Undefined, + local_as: str | None | UndefinedType = Undefined, address_families: list[str] | UndefinedType = Undefined, - remote_peers: list[RemotePeersItem] | UndefinedType = Undefined, + remote_peers: RemotePeers | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -46123,14 +46089,14 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": False}, - "profile": {"type": str, "default": "aes67-r16-2016"}, + "enabled": {"type": bool}, + "profile": {"type": str}, "mlag": {"type": bool, "default": False}, "domain": {"type": int, "default": 127}, "priority1": {"type": int}, "priority2": {"type": int}, - "auto_clock_identity": {"type": bool, "default": True}, - "clock_identity_prefix": {"type": str}, + "auto_clock_identity": {"type": bool}, + "clock_identity_prefix": {"type": str, "default": "00:1C:73"}, "clock_identity": {"type": str}, "source_ip": {"type": str}, "mode": {"type": str, "default": "boundary"}, @@ -46144,16 +46110,13 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """Default value: `False`""" - profile: str + enabled: bool | None + profile: str | None """ Default available profiles are: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - - Default value: `"aes67-r16-2016"` """ mlag: bool """ @@ -46168,22 +46131,22 @@ def __init__( """default -> automatically set based on node_type.""" priority2: int | None """default -> (node_id modulus 256).""" - auto_clock_identity: bool + auto_clock_identity: bool | None """ If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). - - Default value: `True` """ - clock_identity_prefix: str | None + clock_identity_prefix: str """ PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". This can be overridden if auto_clock_identity is set to true (which is the default). + + Default value: `"00:1C:73"` """ clock_identity: str | None """Set PTP clock identity manually. 6-byte value i.e. "01:02:03:04:05:06".""" @@ -46212,14 +46175,14 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, - profile: str | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + profile: str | None | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, domain: int | UndefinedType = Undefined, priority1: int | None | UndefinedType = Undefined, priority2: int | None | UndefinedType = Undefined, - auto_clock_identity: bool | UndefinedType = Undefined, - clock_identity_prefix: str | None | UndefinedType = Undefined, + auto_clock_identity: bool | None | UndefinedType = Undefined, + clock_identity_prefix: str | UndefinedType = Undefined, clock_identity: str | None | UndefinedType = Undefined, source_ip: str | None | UndefinedType = Undefined, mode: str | UndefinedType = Undefined, @@ -46312,7 +46275,7 @@ def __init__( setattr(self, arg, arg_value) _fields: ClassVar[dict] = { - "enabled": {"type": bool, "default": True}, + "enabled": {"type": bool}, "ipsec": {"type": bool, "default": True}, "mtu": {"type": int, "default": 9194}, "ha_interfaces": {"type": list, "items": str}, @@ -46326,12 +46289,8 @@ def __init__( _required_fields: ClassVar[tuple] = ("_custom_data",) _field_to_key_map: ClassVar[dict] = {} _key_to_field_map: ClassVar[dict] = {} - enabled: bool - """ - Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. - - Default value: `True` - """ + enabled: bool | None + """Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group.""" ipsec: bool """ Enable / Disable IPsec over HA path-group when HA is enabled. @@ -46387,7 +46346,7 @@ def __init__( def __init__( self, *, - enabled: bool | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, ipsec: bool | UndefinedType = Undefined, mtu: int | UndefinedType = Undefined, ha_interfaces: list[str] | UndefinedType = Undefined, @@ -46929,7 +46888,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "always_configure_ip_routing": {"type": bool, "default": False}, "raw_eos_cli": {"type": str}, "structured_config": {"type": StructuredConfig}, - "uplink_type": {"type": str, "default": "p2p"}, + "uplink_type": {"type": str}, "uplink_ipv4_pool": {"type": str}, "uplink_interfaces": {"type": list, "items": str}, "uplink_switch_interfaces": {"type": list, "items": str}, @@ -46952,7 +46911,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "short_esi": {"type": str}, "isis_system_id_prefix": {"type": str}, "isis_maximum_paths": {"type": int}, - "is_type": {"type": str, "default": "level-2"}, + "is_type": {"type": str}, "node_sid_base": {"type": int, "default": 0}, "loopback_ipv4_pool": {"type": str}, "loopback_ipv4_address": {"type": str}, @@ -46969,7 +46928,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "evpn_route_servers": {"type": list, "items": str}, "evpn_services_l2_only": {"type": bool, "default": False}, "filter": {"type": Filter}, - "igmp_snooping_enabled": {"type": bool, "default": True}, + "igmp_snooping_enabled": {"type": bool}, "evpn_gateway": {"type": EvpnGateway}, "ipvpn_gateway": {"type": IpvpnGateway}, "mlag": {"type": bool, "default": True}, @@ -46999,7 +46958,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): "inband_mgmt_ipv6_subnet": {"type": str}, "inband_mgmt_ipv6_gateway": {"type": str}, "inband_mgmt_description": {"type": str, "default": "Inband Management"}, - "inband_mgmt_vlan_name": {"type": str, "default": "Inband Management"}, + "inband_mgmt_vlan_name": {"type": str, "default": "INBAND_MGMT"}, "inband_mgmt_vrf": {"type": str, "default": "default"}, "inband_mgmt_mtu": {"type": int, "default": 1500}, "inband_ztp": {"type": bool, "default": False}, @@ -47101,13 +47060,11 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """EOS CLI rendered directly on the root level of the final EOS configuration.""" structured_config: StructuredConfig """Custom structured config for eos_cli_config_gen.""" - uplink_type: str + uplink_type: str | None """ Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. - - Default value: `"p2p"` """ uplink_ipv4_pool: str | None """IPv4 subnet to use to connect to uplink switches.""" @@ -47266,8 +47223,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): """(4.4 hexadecimal).""" isis_maximum_paths: int | None """Number of path to configure in ECMP for ISIS.""" - is_type: str - """Default value: `"level-2"`""" + is_type: str | None node_sid_base: int """ Node-SID base for isis-sr underlay variants. Combined with node id to generate ISIS-SR node-SID. @@ -47354,12 +47310,8 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): If filter is not defined it will default to all. """ - igmp_snooping_enabled: bool - """ - Activate or deactivate IGMP snooping on device level. - - Default value: `True` - """ + igmp_snooping_enabled: bool | None + """Activate or deactivate IGMP snooping on device level.""" evpn_gateway: EvpnGateway """ Node is acting as EVPN Multi-Domain Gateway. @@ -47606,7 +47558,7 @@ class L3Interfaces(AvdIndexedList[str, L3InterfacesItem]): is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. - Default value: `"Inband Management"` + Default value: `"INBAND_MGMT"` """ inband_mgmt_vrf: str """ @@ -47751,7 +47703,7 @@ def __init__( always_configure_ip_routing: bool | UndefinedType = Undefined, raw_eos_cli: str | None | UndefinedType = Undefined, structured_config: StructuredConfig | UndefinedType = Undefined, - uplink_type: str | UndefinedType = Undefined, + uplink_type: str | None | UndefinedType = Undefined, uplink_ipv4_pool: str | None | UndefinedType = Undefined, uplink_interfaces: list[str] | UndefinedType = Undefined, uplink_switch_interfaces: list[str] | UndefinedType = Undefined, @@ -47774,7 +47726,7 @@ def __init__( short_esi: str | None | UndefinedType = Undefined, isis_system_id_prefix: str | None | UndefinedType = Undefined, isis_maximum_paths: int | None | UndefinedType = Undefined, - is_type: str | UndefinedType = Undefined, + is_type: str | None | UndefinedType = Undefined, node_sid_base: int | UndefinedType = Undefined, loopback_ipv4_pool: str | None | UndefinedType = Undefined, loopback_ipv4_address: str | None | UndefinedType = Undefined, @@ -47791,7 +47743,7 @@ def __init__( evpn_route_servers: list[str] | UndefinedType = Undefined, evpn_services_l2_only: bool | UndefinedType = Undefined, filter: Filter | UndefinedType = Undefined, - igmp_snooping_enabled: bool | UndefinedType = Undefined, + igmp_snooping_enabled: bool | None | UndefinedType = Undefined, evpn_gateway: EvpnGateway | UndefinedType = Undefined, ipvpn_gateway: IpvpnGateway | UndefinedType = Undefined, mlag: bool | UndefinedType = Undefined, 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 995dc6a3d28..7da309734e1 100644 --- a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml @@ -7691,7 +7691,11 @@ $defs: documentation_options: table: node-type-uplink-configuration type: str - $ref: eos_designs#/keys/node_type_keys/items/keys/uplink_type + valid_values: + - p2p + - port-channel + - p2p-vrfs + - lan description: 'Override the default `uplink_type` set at the `node_type_key` level. @@ -7992,7 +7996,6 @@ $defs: - level-1-2 - level-1 - level-2 - default: level-2 node_sid_base: documentation_options: table: node-type-isis-configuration @@ -8221,7 +8224,6 @@ $defs: table: node-type-evpn-services-configuration description: Activate or deactivate IGMP snooping on device level. type: bool - default: true evpn_gateway: documentation_options: table: node-type-evpn-multi-domain-gateway-configuration @@ -8250,6 +8252,7 @@ $defs: ' type: list + primary_key: hostname items: type: dict keys: @@ -8333,7 +8336,6 @@ $defs: type: str convert_types: - int - default: none address_families: description: IPVPN address families to enable for remote peers. type: list @@ -8343,13 +8345,13 @@ $defs: - vpn-ipv4 remote_peers: type: list + primary_key: hostname items: type: dict keys: hostname: description: Hostname of remote IPVPN Peer. type: str - required: true ip_address: description: Peering IP of remote IPVPN Peer. type: str @@ -8741,7 +8743,7 @@ $defs: not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed.' type: str - default: Inband Management + default: INBAND_MGMT inband_mgmt_vrf: documentation_options: table: node-type-inband-management-configuration @@ -8835,12 +8837,10 @@ $defs: keys: enabled: type: bool - default: false profile: type: str description: "Default available profiles are:\n - \"aes67\"\n - \"aes67-r16-2016\"\n - \"smpte2059-2\"" - default: aes67-r16-2016 mlag: description: Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG @@ -8874,7 +8874,6 @@ $defs: ' auto_clock_identity: type: bool - default: true description: 'If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. @@ -8885,6 +8884,7 @@ $defs: ' clock_identity_prefix: type: str + default: 00:1C:73 description: 'PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". @@ -9101,7 +9101,6 @@ $defs: keys: enabled: type: bool - default: true description: Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. ipsec: diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml index f4ecb733760..c8e5c61c167 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_node_type.schema.yml @@ -169,7 +169,11 @@ $defs: documentation_options: table: node-type-uplink-configuration type: str - $ref: "eos_designs#/keys/node_type_keys/items/keys/uplink_type" + valid_values: + - p2p + - port-channel + - p2p-vrfs + - lan description: |- Override the default `uplink_type` set at the `node_type_key` level. `uplink_type` must be "p2p" if `vtep` or `underlay_router` is true for the `node_type_key` definition. @@ -405,7 +409,6 @@ $defs: - level-1-2 - level-1 - level-2 - default: level-2 node_sid_base: documentation_options: table: node-type-isis-configuration @@ -595,7 +598,6 @@ $defs: table: node-type-evpn-services-configuration description: Activate or deactivate IGMP snooping on device level. type: bool - default: true evpn_gateway: documentation_options: table: node-type-evpn-multi-domain-gateway-configuration @@ -612,6 +614,7 @@ $defs: If the hostname can be found in the inventory, ip_address and BGP ASN will be automatically populated. Manual override takes precedence. If the peer's hostname can not be found in the inventory, ip_address and bgp_as must be defined. type: list + primary_key: hostname items: type: dict keys: @@ -684,7 +687,6 @@ $defs: type: str convert_types: - int - default: none address_families: description: IPVPN address families to enable for remote peers. type: list @@ -693,13 +695,13 @@ $defs: default: [vpn-ipv4] remote_peers: type: list + primary_key: hostname items: type: dict keys: hostname: description: Hostname of remote IPVPN Peer. type: str - required: true ip_address: description: Peering IP of remote IPVPN Peer. type: str @@ -981,7 +983,7 @@ $defs: Name configured on the Inband Management VLAN. This setting is only applied on the devices where it is set, it does not automatically affect any parent/child devices configuration, so it must be set on each applicable node/node-group/node-type as needed. type: str - default: Inband Management + default: INBAND_MGMT inband_mgmt_vrf: documentation_options: table: node-type-inband-management-configuration @@ -1064,7 +1066,6 @@ $defs: keys: enabled: type: bool - default: false profile: type: str description: |- @@ -1072,7 +1073,6 @@ $defs: - "aes67" - "aes67-r16-2016" - "smpte2059-2" - default: "aes67-r16-2016" mlag: description: Configure PTP on the MLAG peer-link port-channel when PTP is enabled. By default PTP will not be configured on the MLAG peer-link port-channel. type: bool @@ -1102,12 +1102,12 @@ $defs: default -> (node_id modulus 256). auto_clock_identity: type: bool - default: true description: | If you prefer to have PTP clock identity be the system MAC-address of the switch, which is the default EOS behaviour, simply disable the automatic PTP clock identity. default -> (clock_identity_prefix = 00:1C:73 (default)) + (PTP priority 1 as HEX) + ":00:" + (PTP priority 2 as HEX). clock_identity_prefix: type: str + default: "00:1C:73" description: | PTP clock idetentiy 3-byte prefix. i.e. "01:02:03". By default the 3-byte prefix is "00:1C:73". @@ -1302,7 +1302,6 @@ $defs: keys: enabled: type: bool - default: true description: Enable / Disable auto CV-Pathfinder HA, when two nodes are defined in the same node_group. ipsec: type: bool diff --git a/python-avd/pyavd/_eos_designs/shared_utils/flow_tracking.py b/python-avd/pyavd/_eos_designs/shared_utils/flow_tracking.py index f138d0bc759..aeb043eb602 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/flow_tracking.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/flow_tracking.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Literal from pyavd._eos_designs.schema import EosDesigns -from pyavd._utils import default, get +from pyavd._utils import default if TYPE_CHECKING: from . import SharedUtils @@ -37,7 +37,7 @@ class FlowTrackingMixin: @cached_property def flow_tracking_type(self: SharedUtils) -> Literal["sampled", "hardware"]: default_flow_tracker_type = self.node_type_key_data.default_flow_tracker_type - return get(self.switch_data_combined, "flow_tracker_type", default=default_flow_tracker_type) + return self.node_config.flow_tracker_type or default_flow_tracker_type def get_flow_tracker(self: SharedUtils, flow_tracking: FlowTracking) -> dict[str, str] | None: """Return flow_tracking settings for a link, falling back to the fabric flow_tracking_settings if not defined.""" diff --git a/python-avd/pyavd/_eos_designs/shared_utils/inband_management.py b/python-avd/pyavd/_eos_designs/shared_utils/inband_management.py index 2059d6e1069..dd48b80654d 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/inband_management.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/inband_management.py @@ -8,7 +8,6 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError -from pyavd._utils import default, get if TYPE_CHECKING: from . import SharedUtils @@ -32,42 +31,42 @@ def configure_inband_mgmt_ipv6(self: SharedUtils) -> bool: @cached_property def configure_parent_for_inband_mgmt(self: SharedUtils) -> bool: - return self.configure_inband_mgmt and get(self.switch_data_combined, "inband_mgmt_ip") is None + return self.configure_inband_mgmt and not self.node_config.inband_mgmt_ip @cached_property def configure_parent_for_inband_mgmt_ipv6(self: SharedUtils) -> bool: - return self.configure_inband_mgmt_ipv6 and get(self.switch_data_combined, "inband_mgmt_ipv6_address") is None + return self.configure_inband_mgmt_ipv6 and not self.node_config.inband_mgmt_ipv6_address @cached_property def inband_mgmt_subnet(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "inband_mgmt_subnet") + return self.node_config.inband_mgmt_subnet @cached_property def inband_mgmt_ipv6_subnet(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "inband_mgmt_ipv6_subnet") + return self.node_config.inband_mgmt_ipv6_subnet @cached_property def inband_mgmt_vlan(self: SharedUtils) -> int: - return int(get(self.switch_data_combined, "inband_mgmt_vlan", default=4092)) + return self.node_config.inband_mgmt_vlan @cached_property def inband_mgmt_description(self: SharedUtils) -> str: - return get(self.switch_data_combined, "inband_mgmt_description", default="Inband Management") + return self.node_config.inband_mgmt_description @cached_property def inband_mgmt_mtu(self: SharedUtils) -> int | None: if not self.platform_settings.feature_support.per_interface_mtu: return None - return get(self.switch_data_combined, "inband_mgmt_mtu", default=1500) + return self.node_config.inband_mgmt_mtu @cached_property def inband_mgmt_vlan_name(self: SharedUtils) -> str: - return get(self.switch_data_combined, "inband_mgmt_vlan_name", default="INBAND_MGMT") + return self.node_config.inband_mgmt_vlan_name @cached_property def inband_mgmt_vrf(self: SharedUtils) -> str | None: - if (inband_mgmt_vrf := get(self.switch_data_combined, "inband_mgmt_vrf")) != "default": + if (inband_mgmt_vrf := self.node_config.inband_mgmt_vrf) != "default": return inband_mgmt_vrf return None @@ -83,11 +82,11 @@ def inband_mgmt_gateway(self: SharedUtils) -> str | None: Otherwise return None """ - if self.inband_mgmt_ip is None: + if not self.inband_mgmt_ip: return None if not self.configure_parent_for_inband_mgmt: - return get(self.switch_data_combined, "inband_mgmt_gateway") + return self.node_config.inband_mgmt_gateway subnet = ip_network(self.inband_mgmt_subnet, strict=False) return f"{subnet[1]!s}" @@ -103,11 +102,11 @@ def inband_mgmt_ipv6_gateway(self: SharedUtils) -> str | None: Otherwise return None """ - if self.inband_mgmt_ipv6_address is None: + if not self.inband_mgmt_ipv6_address: return None if not self.configure_parent_for_inband_mgmt_ipv6: - return get(self.switch_data_combined, "inband_mgmt_ipv6_gateway") + return self.node_config.inband_mgmt_ipv6_gateway subnet = ip_network(self.inband_mgmt_ipv6_subnet, strict=False) return f"{subnet[1]!s}" @@ -122,10 +121,10 @@ def inband_mgmt_ip(self: SharedUtils) -> str | None: - deducted IP from inband_mgmt_subnet & id - None. """ - if (inband_mgmt_ip := get(self.switch_data_combined, "inband_mgmt_ip")) is not None: + if inband_mgmt_ip := self.node_config.inband_mgmt_ip: return inband_mgmt_ip - if self.inband_mgmt_subnet is None: + if not self.inband_mgmt_subnet: return None if self.id is None: @@ -146,10 +145,10 @@ def inband_mgmt_ipv6_address(self: SharedUtils) -> str | None: - deduced IP from inband_mgmt_ipv6_subnet & id - None. """ - if (inband_mgmt_ipv6_address := get(self.switch_data_combined, "inband_mgmt_ipv6_address")) is not None: + if inband_mgmt_ipv6_address := self.node_config.inband_mgmt_ipv6_address: return inband_mgmt_ipv6_address - if self.inband_mgmt_ipv6_subnet is None: + if not self.inband_mgmt_ipv6_subnet: return None if self.id is None: @@ -168,7 +167,7 @@ def inband_mgmt_interface(self: SharedUtils) -> str | None: For L2 switches defaults to Vlan For all other devices set to value of inband_mgmt_interface or None """ - if (inband_mgmt_interface := get(self.switch_data_combined, "inband_mgmt_interface")) is not None: + if inband_mgmt_interface := self.node_config.inband_mgmt_interface: return inband_mgmt_interface if self.configure_inband_mgmt or self.configure_inband_mgmt_ipv6: @@ -177,8 +176,8 @@ def inband_mgmt_interface(self: SharedUtils) -> str | None: return None @cached_property - def inband_ztp(self: SharedUtils) -> bool | None: - inband_ztp = get(self.switch_data_combined, "inband_ztp") + def inband_ztp(self: SharedUtils) -> bool: + inband_ztp = self.node_config.inband_ztp if inband_ztp and self.uplink_type != "port-channel": msg = "'inband_ztp' is currently only supported for L2 switches ('uplink_type: port-channel')." raise AristaAvdError(msg) @@ -186,4 +185,4 @@ def inband_ztp(self: SharedUtils) -> bool | None: @cached_property def inband_ztp_lacp_fallback_delay(self: SharedUtils) -> int | None: - return default(get(self.switch_data_combined, "inband_ztp_lacp_fallback_delay"), 30) + return self.node_config.inband_ztp_lacp_fallback_delay 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 843b2e5e24d..aa2faec8089 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/ip_addressing.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/ip_addressing.py @@ -6,7 +6,8 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import get, load_python_class +from pyavd._errors import AristaAvdMissingVariableError +from pyavd._utils import load_python_class from pyavd.api.ip_addressing import AvdIpAddressing if TYPE_CHECKING: @@ -23,41 +24,49 @@ class IpAddressingMixin: @cached_property def loopback_ipv4_offset(self: SharedUtils) -> int: - return get(self.switch_data_combined, "loopback_ipv4_offset", default=0) + return self.node_config.loopback_ipv4_offset @cached_property def loopback_ipv6_offset(self: SharedUtils) -> int: - return get(self.switch_data_combined, "loopback_ipv6_offset", default=0) + return self.node_config.loopback_ipv6_offset @cached_property def loopback_ipv6_pool(self: SharedUtils) -> str: - return get(self.switch_data_combined, "loopback_ipv6_pool", required=True) + if not self.node_config.loopback_ipv6_pool: + msg = "loopback_ipv6_pool" + raise AristaAvdMissingVariableError(msg) - @cached_property - def uplink_ipv4_pool(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "uplink_ipv4_pool") + return self.node_config.loopback_ipv6_pool @cached_property - def downlink_pools(self: SharedUtils) -> list | None: - return get(self.switch_data_combined, "downlink_pools") + def uplink_ipv4_pool(self: SharedUtils) -> str | None: + return self.node_config.uplink_ipv4_pool @cached_property def loopback_ipv4_pool(self: SharedUtils) -> str: - return get(self.switch_data_combined, "loopback_ipv4_pool", required=True) + if not self.node_config.loopback_ipv4_pool: + msg = "loopback_ipv4_pool" + raise AristaAvdMissingVariableError(msg) + + return self.node_config.loopback_ipv4_pool @cached_property - def loopback_ipv4_address(self: SharedUtils) -> str: + def loopback_ipv4_address(self: SharedUtils) -> str | None: """Set the loopback IPv4 for this host, takes precedence over loopback_ipv4_pool.""" - return get(self.switch_data_combined, "loopback_ipv4_address") + return self.node_config.loopback_ipv4_address @cached_property def vtep_loopback_ipv4_pool(self: SharedUtils) -> str: - return get(self.switch_data_combined, "vtep_loopback_ipv4_pool", required=True) + if not self.node_config.vtep_loopback_ipv4_pool: + msg = "vtep_loopback_ipv4_pool" + raise AristaAvdMissingVariableError(msg) + + return self.node_config.vtep_loopback_ipv4_pool @cached_property - def vtep_loopback_ipv4_address(self: SharedUtils) -> str: + def vtep_loopback_ipv4_address(self: SharedUtils) -> str | None: """Set the VTEP loopback IPv4 for this host, takes precedence over vtep_loopback_ipv4_pool.""" - return get(self.switch_data_combined, "vtep_loopback_ipv4_address") + return self.node_config.vtep_loopback_ipv4_address @cached_property def vtep_ip(self: SharedUtils) -> str: 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 0d5e0f8ce0c..61537193d91 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/l3_interfaces.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/l3_interfaces.py @@ -6,8 +6,8 @@ from functools import cached_property from typing import TYPE_CHECKING +from pyavd._eos_designs.schema import EosDesigns from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get, merge from pyavd.api.interface_descriptions import InterfaceDescriptionData if TYPE_CHECKING: @@ -31,72 +31,69 @@ def sanitize_interface_name(self: SharedUtils, interface_name: str) -> str: """ return interface_name.replace("/", "_") - def apply_l3_interfaces_profile(self: SharedUtils, l3_interface: dict) -> dict: + def apply_l3_interfaces_profile( + self: SharedUtils, l3_interface: EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3InterfacesItem + ) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3InterfacesItem: """Apply a profile to an l3_interface.""" - if "profile" not in l3_interface: + if not l3_interface.profile: # Nothing to do return l3_interface - 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`." + 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 + profile_as_interface = self.inputs.l3_interface_profiles[l3_interface.profile]._cast_as( + EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3InterfacesItem + ) + return l3_interface._deepinherited(profile_as_interface) @cached_property - def l3_interfaces(self: SharedUtils) -> list: + def l3_interfaces(self: SharedUtils) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces: """Returns the list of l3_interfaces, where any referenced profiles are applied.""" - if not (l3_interfaces := get(self.switch_data_combined, "l3_interfaces")): - return [] - - # Return after applying profile from l3_interfaces_profiles if set. - return [self.apply_l3_interfaces_profile(l3_interface) for l3_interface in l3_interfaces] + return EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces( + [self.apply_l3_interfaces_profile(l3_interface) for l3_interface in self.node_config.l3_interfaces] + ) @cached_property def l3_interfaces_bgp_neighbors(self: SharedUtils) -> list: neighbors = [] for interface in self.l3_interfaces: - peer_ip = get(interface, "peer_ip") - bgp = get(interface, "bgp") - if not (peer_ip and bgp): + if not (interface.peer_ip and interface.bgp): continue - peer_as = get(bgp, "peer_as") + peer_as = interface.bgp.peer_as if peer_as is None: - msg = f"'l3_interfaces[{interface['name']}].bgp.peer_as' needs to be set to enable BGP." + msg = f"'l3_interfaces[{interface.name}].bgp.peer_as' needs to be set to enable BGP." raise AristaAvdInvalidInputsError(msg) - is_intf_wan = get(interface, "wan_carrier") is not None + is_intf_wan = bool(interface.wan_carrier) - prefix_list_in = get(bgp, "ipv4_prefix_list_in") - if prefix_list_in is None and is_intf_wan: - msg = f"BGP is enabled but 'bgp.ipv4_prefix_list_in' is not configured for l3_interfaces[{interface['name']}]" + if not interface.bgp.ipv4_prefix_list_in and is_intf_wan: + msg = f"BGP is enabled but 'bgp.ipv4_prefix_list_in' is not configured for l3_interfaces[{interface.name}]" raise AristaAvdInvalidInputsError(msg) - description = interface.get("description") + description = interface.description if not description: description = self.interface_descriptions.underlay_ethernet_interface( InterfaceDescriptionData( shared_utils=self, - interface=interface["name"], - peer=interface.get("peer"), - peer_interface=interface.get("peer_interface"), - wan_carrier=interface.get("wan_carrier"), - wan_circuit_id=interface.get("wan_circuit_id"), + interface=interface.name, + peer=interface.peer, + peer_interface=interface.peer_interface, + wan_carrier=interface.wan_carrier, + wan_circuit_id=interface.wan_circuit_id, ), ) neighbor = { - "ip_address": peer_ip, + "ip_address": interface.peer_ip, "remote_as": peer_as, "description": description, } - neighbor["ipv4_prefix_list_in"] = prefix_list_in - neighbor["ipv4_prefix_list_out"] = get(bgp, "ipv4_prefix_list_out") + neighbor["ipv4_prefix_list_in"] = interface.bgp.ipv4_prefix_list_in + neighbor["ipv4_prefix_list_out"] = interface.bgp.ipv4_prefix_list_out if is_intf_wan: neighbor["set_no_advertise"] = True diff --git a/python-avd/pyavd/_eos_designs/shared_utils/link_tracking_groups.py b/python-avd/pyavd/_eos_designs/shared_utils/link_tracking_groups.py index 2f9c1938e83..0782dd521af 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/link_tracking_groups.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/link_tracking_groups.py @@ -6,7 +6,7 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import default, get +from pyavd._utils import default if TYPE_CHECKING: from . import SharedUtils @@ -22,15 +22,14 @@ class LinkTrackingGroupsMixin: @cached_property def link_tracking_groups(self: SharedUtils) -> list | None: - if get(self.switch_data_combined, "link_tracking.enabled") is True: + if self.node_config.link_tracking.enabled: link_tracking_groups = [] default_recovery_delay = default(self.platform_settings.reload_delay.mlag, 300) - lt_groups = get(self.switch_data_combined, "link_tracking.groups", default=[]) - - if len(lt_groups) > 0: - for lt_group in lt_groups: - lt_group["recovery_delay"] = lt_group.get("recovery_delay", default_recovery_delay) - link_tracking_groups.append(lt_group) + if len(self.node_config.link_tracking.groups) > 0: + for lt_group in self.node_config.link_tracking.groups: + lt_group_dict = lt_group._as_dict(include_default_values=True) + lt_group_dict["recovery_delay"] = default(lt_group.recovery_delay, default_recovery_delay) + link_tracking_groups.append(lt_group_dict) else: link_tracking_groups.append({"name": "LT_GROUP1", "recovery_delay": default_recovery_delay}) diff --git a/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py b/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py index 116d92ef1bc..6ad831d4b26 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/mgmt.py @@ -32,7 +32,7 @@ def mgmt_interface(self: SharedUtils) -> str | None: Fabric Topology data model mgmt_interface. """ return default( - get(self.switch_data_combined, "mgmt_interface"), + self.node_config.mgmt_interface, # Notice that we actually have a default value for the next two, but the precedence order would break if we use it. # TODO: Evaluate if we should remove the default values from either or both. self.platform_settings._get("management_interface", None), @@ -43,11 +43,11 @@ def mgmt_interface(self: SharedUtils) -> str | None: @cached_property def ipv6_mgmt_ip(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "ipv6_mgmt_ip") + return self.node_config.ipv6_mgmt_ip @cached_property def mgmt_ip(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "mgmt_ip") + return self.node_config.mgmt_ip @cached_property def mgmt_interface_vrf(self: SharedUtils) -> str: @@ -55,11 +55,11 @@ def mgmt_interface_vrf(self: SharedUtils) -> str: @cached_property def mgmt_gateway(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "mgmt_gateway", default=self.inputs.mgmt_gateway) + return default(self.node_config.mgmt_gateway, self.inputs.mgmt_gateway) @cached_property def ipv6_mgmt_gateway(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "ipv6_mgmt_gateway", default=self.inputs.ipv6_mgmt_gateway) + return default(self.node_config.ipv6_mgmt_gateway, self.inputs.ipv6_mgmt_gateway) @cached_property def default_mgmt_method(self: SharedUtils) -> str | None: @@ -71,7 +71,7 @@ def default_mgmt_method(self: SharedUtils) -> str | None: 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." + msg = "'default_mgmt_method: oob' requires either 'mgmt_ip' or 'ipv6_mgmt_ip' to be set." raise AristaAvdInvalidInputsError(msg) return default_mgmt_method diff --git a/python-avd/pyavd/_eos_designs/shared_utils/misc.py b/python-avd/pyavd/_eos_designs/shared_utils/misc.py index 1b743b60696..f42e7ce4e3c 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/misc.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/misc.py @@ -37,40 +37,40 @@ def hostname(self: SharedUtils) -> str: @cached_property def id(self: SharedUtils) -> int | None: - return get(self.switch_data_combined, "id") + return self.node_config.id @cached_property def filter_only_vlans_in_use(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "filter.only_vlans_in_use", default=False) + return self.node_config.filter.only_vlans_in_use @cached_property def filter_tags(self: SharedUtils) -> list: """Return filter.tags + group if defined.""" - filter_tags = get(self.switch_data_combined, "filter.tags", default=["all"]) + filter_tags = self.node_config.filter.tags if self.group is not None: filter_tags.append(self.group) return filter_tags @cached_property def filter_allow_vrfs(self: SharedUtils) -> list: - return get(self.switch_data_combined, "filter.allow_vrfs", default=["all"]) + return self.node_config.filter.allow_vrfs @cached_property def filter_deny_vrfs(self: SharedUtils) -> list: - return get(self.switch_data_combined, "filter.deny_vrfs", default=[]) + return self.node_config.filter.deny_vrfs @cached_property def filter_tenants(self: SharedUtils) -> list: - return get(self.switch_data_combined, "filter.tenants", default=["all"]) + return self.node_config.filter.tenants @cached_property def always_include_vrfs_in_tenants(self: SharedUtils) -> list: - return get(self.switch_data_combined, "filter.always_include_vrfs_in_tenants", default=[]) + return self.node_config.filter.always_include_vrfs_in_tenants @cached_property def igmp_snooping_enabled(self: SharedUtils) -> bool: 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 + return bool(default(self.node_config.igmp_snooping_enabled, default_igmp_snooping_enabled)) @cached_property def only_local_vlan_trunk_groups(self: SharedUtils) -> bool: @@ -85,35 +85,22 @@ 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"), self.inputs.system_mac_address) + return default(self.node_config.system_mac_address, self.inputs.system_mac_address) @cached_property def uplink_switches(self: SharedUtils) -> list: - return ( - default( - get(self.switch_data_combined, "uplink_switches"), - get(self.cv_topology_config, "uplink_switches"), - ) - or [] - ) + return self.node_config.uplink_switches or get(self.cv_topology_config, "uplink_switches") or [] @cached_property def uplink_interfaces(self: SharedUtils) -> list: return range_expand( - default( - get(self.switch_data_combined, "uplink_interfaces"), - get(self.cv_topology_config, "uplink_interfaces"), - self.default_interfaces.uplink_interfaces, - ), + self.node_config.uplink_interfaces or get(self.cv_topology_config, "uplink_interfaces") or self.default_interfaces.uplink_interfaces, ) @cached_property def uplink_switch_interfaces(self: SharedUtils) -> list: - uplink_switch_interfaces = default( - get(self.switch_data_combined, "uplink_switch_interfaces"), - get(self.cv_topology_config, "uplink_switch_interfaces"), - ) - if uplink_switch_interfaces is not None: + uplink_switch_interfaces = self.node_config.uplink_switch_interfaces or get(self.cv_topology_config, "uplink_switch_interfaces") or [] + if uplink_switch_interfaces: return range_expand(uplink_switch_interfaces) if not self.uplink_switches: @@ -151,7 +138,7 @@ def uplink_switch_interfaces(self: SharedUtils) -> list: @cached_property def virtual_router_mac_address(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "virtual_router_mac_address") + return self.node_config.virtual_router_mac_address @cached_property def serial_number(self: SharedUtils) -> str | None: @@ -162,24 +149,24 @@ def serial_number(self: SharedUtils) -> str | None: Fabric Topology data model serial_number -> Host variable var serial_number. """ - return default(get(self.switch_data_combined, "serial_number"), self.inputs.serial_number) + return default(self.node_config.serial_number, self.inputs.serial_number) @cached_property def max_parallel_uplinks(self: SharedUtils) -> int: """Exposed in avd_switch_facts.""" - return get(self.switch_data_combined, "max_parallel_uplinks", default=1) + return default(self.node_config.max_parallel_uplinks, 1) @cached_property def max_uplink_switches(self: SharedUtils) -> int: """max_uplink_switches will default to the length of uplink_switches.""" - return get(self.switch_data_combined, "max_uplink_switches", default=len(self.uplink_switches)) + return default(self.node_config.max_uplink_switches, len(self.uplink_switches)) @cached_property def p2p_uplinks_mtu(self: SharedUtils) -> int | None: if not self.platform_settings.feature_support.per_interface_mtu: return None p2p_uplinks_mtu = default(self.platform_settings.p2p_uplinks_mtu, self.inputs.p2p_uplinks_mtu) - return get(self.switch_data_combined, "uplink_mtu", default=p2p_uplinks_mtu) + return default(self.node_config.uplink_mtu, p2p_uplinks_mtu) @cached_property def fabric_name(self: SharedUtils) -> str: @@ -191,31 +178,31 @@ def fabric_name(self: SharedUtils) -> str: @cached_property def rack(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "rack") + return self.node_config.rack @cached_property def uplink_interface_speed(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "uplink_interface_speed") + return self.node_config.uplink_interface_speed @cached_property def uplink_switch_interface_speed(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "uplink_switch_interface_speed") + return self.node_config.uplink_switch_interface_speed @cached_property def uplink_bfd(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "uplink_bfd") is True + return self.node_config.uplink_bfd @cached_property def uplink_ptp(self: SharedUtils) -> dict | None: - return get(self.switch_data_combined, "uplink_ptp") + return self.node_config.uplink_ptp._as_dict() or None @cached_property def uplink_macsec(self: SharedUtils) -> dict | None: - return get(self.switch_data_combined, "uplink_macsec") + return self.node_config.uplink_macsec._as_dict() or None @cached_property def uplink_structured_config(self: SharedUtils) -> dict | None: - return get(self.switch_data_combined, "uplink_structured_config") + return self.node_config.uplink_structured_config or None @cached_property def default_interface_mtu(self: SharedUtils) -> int | None: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/mlag.py b/python-avd/pyavd/_eos_designs/shared_utils/mlag.py index 16376ae8415..1321e09a89b 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/mlag.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/mlag.py @@ -7,11 +7,13 @@ from re import findall 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, get_ip_from_ip_prefix from pyavd.j2filters import range_expand if TYPE_CHECKING: + from typing import Literal + from pyavd._eos_designs.eos_designs_facts import EosDesignsFacts from . import SharedUtils @@ -27,72 +29,67 @@ class MlagMixin: @cached_property def mlag(self: SharedUtils) -> bool: - return ( - self.node_type_key_data.mlag_support - and get(self.switch_data_combined, "mlag", default=True) is True - and len(self.switch_data_node_group_nodes) == 2 - ) + return self.node_type_key_data.mlag_support and self.node_config.mlag and len(self.node_group_config.nodes) == 2 @cached_property def group(self: SharedUtils) -> str | None: """Group set to "node_group" name or None.""" - return get(self.switch_data, "group") + return self.node_group_config.group or None @cached_property def mlag_ibgp_origin_incomplete(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "mlag_ibgp_origin_incomplete", default=True) + return self.node_config.mlag_ibgp_origin_incomplete @cached_property def mlag_peer_vlan(self: SharedUtils) -> int: - return get(self.switch_data_combined, "mlag_peer_vlan", default=4094) + return self.node_config.mlag_peer_vlan @cached_property def mlag_interfaces(self: SharedUtils) -> list: - return range_expand( - default( - get(self.switch_data_combined, "mlag_interfaces"), - get(self.cv_topology_config, "mlag_interfaces"), - self.default_interfaces.mlag_interfaces, - ), - ) + return range_expand(self.node_config.mlag_interfaces or get(self.cv_topology_config, "mlag_interfaces") or self.default_interfaces.mlag_interfaces) @cached_property def mlag_interfaces_speed(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "mlag_interfaces_speed") + return self.node_config.mlag_interfaces_speed @cached_property def mlag_peer_address_family(self: SharedUtils) -> str: - return get(self.switch_data_combined, "mlag_peer_address_family", default="ipv4") + return self.node_config.mlag_peer_address_family @cached_property def mlag_peer_ipv4_pool(self: SharedUtils) -> str: - return get(self.switch_data_combined, "mlag_peer_ipv4_pool", required=True) + if not self.node_config.mlag_peer_ipv4_pool: + msg = "mlag_peer_ipv4_pool" + raise AristaAvdMissingVariableError(msg) + return self.node_config.mlag_peer_ipv4_pool @cached_property def mlag_peer_ipv6_pool(self: SharedUtils) -> str: - return get(self.switch_data_combined, "mlag_peer_ipv6_pool", required=True) + if not self.node_config.mlag_peer_ipv6_pool: + msg = "mlag_peer_ipv6_pool" + raise AristaAvdMissingVariableError(msg) + return self.node_config.mlag_peer_ipv6_pool @cached_property def mlag_peer_l3_ipv4_pool(self: SharedUtils) -> str: - return get(self.switch_data_combined, "mlag_peer_l3_ipv4_pool", required=True) + if not self.node_config.mlag_peer_l3_ipv4_pool: + msg = "mlag_peer_l3_ipv4_pool" + raise AristaAvdMissingVariableError(msg) + return self.node_config.mlag_peer_l3_ipv4_pool @cached_property - def mlag_role(self: SharedUtils) -> str | None: + def mlag_role(self: SharedUtils) -> Literal["primary", "secondary"] | None: if self.mlag: - if self.switch_data_node_group_nodes[0]["name"] == self.hostname: - return "primary" - if self.switch_data_node_group_nodes[1]["name"] == self.hostname: - return "secondary" - msg = "Unable to detect MLAG role" - raise AristaAvdError(msg) + return "secondary" if bool(list(self.node_group_config.nodes.keys()).index(self.hostname)) else "primary" + return None @cached_property def mlag_peer(self: SharedUtils) -> str: if self.mlag_role == "primary": - return self.switch_data_node_group_nodes[1]["name"] + return list(self.node_group_config.nodes.keys())[1] if self.mlag_role == "secondary": - return self.switch_data_node_group_nodes[0]["name"] + return list(self.node_group_config.nodes.keys())[0] # noqa: RUF015 msg = "Unable to find MLAG peer within same node group" raise AristaAvdError(msg) @@ -104,7 +101,7 @@ def mlag_l3(self: SharedUtils) -> bool: def mlag_peer_l3_vlan(self: SharedUtils) -> int | None: if self.mlag_l3: mlag_peer_vlan = self.mlag_peer_vlan - mlag_peer_l3_vlan = get(self.switch_data_combined, "mlag_peer_l3_vlan", default=4093) + mlag_peer_l3_vlan = self.node_config.mlag_peer_l3_vlan if mlag_peer_l3_vlan not in [None, False, mlag_peer_vlan]: return mlag_peer_l3_vlan return None @@ -182,7 +179,7 @@ def mlag_port_channel_id(self: SharedUtils) -> int: msg = f"'mlag_interfaces' not set on '{self.hostname}." raise AristaAvdInvalidInputsError(msg) default_mlag_port_channel_id = int("".join(findall(r"\d", self.mlag_interfaces[0]))) - return get(self.switch_data_combined, "mlag_port_channel_id", default_mlag_port_channel_id) + return default(self.node_config.mlag_port_channel_id, default_mlag_port_channel_id) @cached_property def mlag_peer_port_channel_id(self: SharedUtils) -> int: @@ -194,7 +191,7 @@ def mlag_peer_interfaces(self: SharedUtils) -> list: @cached_property def mlag_peer_vlan_structured_config(self: SharedUtils) -> dict | None: - return get(self.switch_data_combined, "mlag_peer_vlan_structured_config") + return self.node_config.mlag_peer_vlan_structured_config._as_dict() @cached_property def mlag_ibgp_ip(self: SharedUtils) -> str: 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 884fb808027..c46f3fdb3d6 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/node_type.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/node_type.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get +from pyavd._utils import default if TYPE_CHECKING: from . import SharedUtils @@ -71,7 +71,7 @@ def uplink_type(self: SharedUtils) -> str: uplink_type set based on .nodes.[].uplink_type and node_type_keys..uplink_type. """ default_uplink_type = self.node_type_key_data.uplink_type - return get(self.switch_data_combined, "uplink_type", default=default_uplink_type) + return default(self.node_config.uplink_type, default_uplink_type) @cached_property def network_services_l1(self: SharedUtils) -> bool: @@ -100,7 +100,7 @@ def network_services_l3(self: SharedUtils) -> bool: and . | nodes.<> >.evpn_services_l2_only. """ # network_services_l3 override based on evpn_services_l2_only - if self.vtep is True and get(self.switch_data_combined, "evpn_services_l2_only") is True: + if self.vtep and self.node_config.evpn_services_l2_only: return False return self.node_type_key_data.network_services.l3 @@ -139,4 +139,4 @@ def vtep(self: SharedUtils) -> bool: node_type_keys..vtep. """ default_vtep = self.node_type_key_data.vtep - return get(self.switch_data_combined, "vtep", default=default_vtep) is True + return default(self.node_config.vtep, default_vtep) diff --git a/python-avd/pyavd/_eos_designs/shared_utils/overlay.py b/python-avd/pyavd/_eos_designs/shared_utils/overlay.py index 6124e7a8478..f7638f1aff0 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 default, get +from pyavd._utils import default if TYPE_CHECKING: from . import SharedUtils @@ -27,20 +27,20 @@ class OverlayMixin: def vtep_loopback(self: SharedUtils) -> str: """The default is Loopback1 except for WAN devices where the default is Dps1.""" default_vtep_loopback = "Dps1" if self.is_wan_router else "Loopback1" - return get(self.switch_data_combined, "vtep_loopback", default=default_vtep_loopback) + return default(self.node_config.vtep_loopback, default_vtep_loopback) @cached_property def evpn_role(self: SharedUtils) -> str | None: - if self.underlay_router is True: + if self.underlay_router: default_evpn_role = self.node_type_key_data.default_evpn_role - return get(self.switch_data_combined, "evpn_role", default=default_evpn_role) + return default(self.node_config.evpn_role, default_evpn_role) return None @cached_property def mpls_overlay_role(self: SharedUtils) -> str | None: - if self.underlay_router is True: + if self.underlay_router: default_mpls_overlay_role = self.node_type_key_data.default_mpls_overlay_role - return get(self.switch_data_combined, "mpls_overlay_role", default=default_mpls_overlay_role) + return default(self.node_config.mpls_overlay_role, default_mpls_overlay_role) return None @cached_property @@ -83,15 +83,15 @@ def get_rd_admin_subfield_value(self: SharedUtils, admin_subfield: str, admin_su @cached_property def evpn_gateway_vxlan_l2(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "evpn_gateway.evpn_l2.enabled", default=False) + return self.node_config.evpn_gateway.evpn_l2.enabled @cached_property def evpn_gateway_vxlan_l3(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "evpn_gateway.evpn_l3.enabled", default=False) + return self.node_config.evpn_gateway.evpn_l3.enabled @cached_property def evpn_gateway_vxlan_l3_inter_domain(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "evpn_gateway.evpn_l3.inter_domain", default=True) + return self.node_config.evpn_gateway.evpn_l3.inter_domain @cached_property def overlay_routing_protocol_address_family(self: SharedUtils) -> str: @@ -160,7 +160,7 @@ def overlay_mpls(self: SharedUtils) -> bool: @cached_property def overlay_ipvpn_gateway(self: SharedUtils) -> bool: # Set overlay_ipvpn_gateway to trigger ipvpn interworking configuration. - return self.overlay_evpn and get(self.switch_data_combined, "ipvpn_gateway.enabled", default=False) + return self.overlay_evpn and self.node_config.ipvpn_gateway.enabled @cached_property def overlay_ler(self: SharedUtils) -> bool: @@ -184,7 +184,7 @@ def overlay_vpn_ipv4(self: SharedUtils) -> bool: return False return (self.overlay_routing_protocol == "ibgp" and "vpn-ipv4" in self.overlay_address_families) or ( - "vpn-ipv4" in get(self.switch_data_combined, "ipvpn_gateway.address_families", default=["vpn-ipv4"]) and self.overlay_ipvpn_gateway + "vpn-ipv4" in self.node_config.ipvpn_gateway.address_families and self.overlay_ipvpn_gateway ) @cached_property @@ -194,7 +194,7 @@ def overlay_vpn_ipv6(self: SharedUtils) -> bool: return False return (self.overlay_routing_protocol == "ibgp" and "vpn-ipv6" in self.overlay_address_families) or ( - "vpn-ipv6" in get(self.switch_data_combined, "ipvpn_gateway.address_families", default=["vpn-ipv4"]) and self.overlay_ipvpn_gateway + "vpn-ipv6" in self.node_config.ipvpn_gateway.address_families and self.overlay_ipvpn_gateway ) @cached_property @@ -218,7 +218,7 @@ def overlay_her(self: SharedUtils) -> bool: @cached_property def overlay_dpath(self: SharedUtils) -> bool: # Set dpath based on ipvpn_gateway parameters - return self.overlay_ipvpn_gateway and get(self.switch_data_combined, "ipvpn_gateway.enable_d_path", default=True) + return self.overlay_ipvpn_gateway and self.node_config.ipvpn_gateway.enable_d_path @cached_property def overlay_evpn_vxlan(self: SharedUtils) -> bool: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/platform.py b/python-avd/pyavd/_eos_designs/shared_utils/platform.py index b6caab205b0..6713cdf3bc2 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/platform.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/platform.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from pyavd._eos_designs.schema import EosDesigns -from pyavd._utils import get +from pyavd._utils import default if TYPE_CHECKING: from . import SharedUtils @@ -24,7 +24,7 @@ class PlatformMixin: @cached_property def platform(self: SharedUtils) -> str | None: - return get(self.switch_data_combined, "platform", default=self.cv_topology_platform) + return default(self.node_config.platform, self.cv_topology_platform) @cached_property def platform_settings(self: SharedUtils) -> EosDesigns.PlatformSettingsItem | EosDesigns.CustomPlatformSettingsItem: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/ptp.py b/python-avd/pyavd/_eos_designs/shared_utils/ptp.py index e28484642cf..708ef88ed5d 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/ptp.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/ptp.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get +from pyavd._utils import default if TYPE_CHECKING: from pyavd._eos_designs.schema import EosDesigns @@ -26,16 +26,16 @@ class PtpMixin: @cached_property def ptp_enabled(self: SharedUtils) -> bool: default_ptp_enabled = self.inputs.ptp_settings.enabled - return get(self.switch_data_combined, "ptp.enabled", default=default_ptp_enabled) is True + return bool(default(self.node_config.ptp.enabled, default_ptp_enabled)) @cached_property def ptp_mlag(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "ptp.mlag") is True + return self.node_config.ptp.mlag @cached_property def ptp_profile_name(self: SharedUtils) -> str: default_ptp_profile = self.inputs.ptp_settings.profile - return get(self.switch_data_combined, "ptp.profile", default=default_ptp_profile) + return self.node_config.ptp.profile or default_ptp_profile @cached_property def ptp_profile(self: SharedUtils) -> EosDesigns.PtpProfilesItem: diff --git a/python-avd/pyavd/_eos_designs/shared_utils/routing.py b/python-avd/pyavd/_eos_designs/shared_utils/routing.py index 93062f55ef7..f9db83a1a24 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/routing.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/routing.py @@ -6,8 +6,7 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError -from pyavd._utils import get +from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError, AristaAvdMissingVariableError from pyavd.j2filters import range_expand if TYPE_CHECKING: @@ -36,7 +35,7 @@ def overlay_routing_protocol(self: SharedUtils) -> str: def overlay_address_families(self: SharedUtils) -> list: if self.overlay_routing_protocol in ["ebgp", "ibgp"]: default_overlay_address_families = self.node_type_key_data.default_overlay_address_families - return get(self.switch_data_combined, "overlay_address_families", default=default_overlay_address_families) + return self.node_config.overlay_address_families or default_overlay_address_families return [] @cached_property @@ -96,7 +95,11 @@ def bgp_as(self: SharedUtils) -> str | None: if self.inputs.bgp_as: return self.inputs.bgp_as - bgp_as_range_expanded = range_expand(str(get(self.switch_data_combined, "bgp_as", required=True))) + if self.node_config.bgp_as is None: + msg = "bgp_as" + raise AristaAvdMissingVariableError(msg) + + bgp_as_range_expanded = range_expand(self.node_config.bgp_as) try: if len(bgp_as_range_expanded) == 1: return bgp_as_range_expanded[0] @@ -114,4 +117,4 @@ def bgp_as(self: SharedUtils) -> str | None: @cached_property def always_configure_ip_routing(self: SharedUtils) -> bool: - return get(self.switch_data_combined, "always_configure_ip_routing") + return self.node_config.always_configure_ip_routing diff --git a/python-avd/pyavd/_eos_designs/shared_utils/switch_data.py b/python-avd/pyavd/_eos_designs/shared_utils/switch_data.py index 07105b0ea54..942cf9d57f4 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/switch_data.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/switch_data.py @@ -6,12 +6,10 @@ from functools import cached_property from typing import TYPE_CHECKING +from pyavd._eos_designs.schema import EosDesigns from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get, merge if TYPE_CHECKING: - from pyavd._eos_designs.schema import EosDesigns - from . import SharedUtils @@ -26,11 +24,11 @@ class SwitchDataMixin: @cached_property def node_type_config( self: SharedUtils, - ) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes | EosDesigns._DynamicKeys.DynamicCustomNodeTypesItem.CustomNodeTypes: + ) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes: node_type_key = self.node_type_key_data.key if node_type_key in self.inputs._dynamic_keys.custom_node_types: - return self.inputs._dynamic_keys.custom_node_types[node_type_key].value + return self.inputs._dynamic_keys.custom_node_types[node_type_key].value._cast_as(EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes) if node_type_key in self.inputs._dynamic_keys.node_types: return self.inputs._dynamic_keys.node_types[node_type_key].value @@ -39,66 +37,45 @@ def node_type_config( raise AristaAvdInvalidInputsError(msg) @cached_property - def switch_data(self: SharedUtils) -> dict: + def node_group_config( + self: SharedUtils, + ) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodeGroupsItem: + for node_group in self.node_type_config.node_groups: + if self.hostname in node_group.nodes: + return node_group + + return EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodeGroupsItem() + + @cached_property + def node_config( + self: SharedUtils, + ) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem: """ - internal _switch_data containing inherited vars from fabric_topology data model. + NodesItem object containing the fully inherited node config. Vars are inherited like: .defaults -> .node_groups.[] -> .node_groups.[].nodes.[] -> .nodes.[] - - Returns: - ------- - dict - node_group : dict - Configuration set at the node_group level - including the "nodes" list. - Empty dict if the node is not defined under a node_group. - group : str - Optional - Name of the matching node_group. Not set if the node is not defined under a node_group. - combined : dict - Combined configuration after inheritance from all levels """ - switch_data = {"node_group": {}} - node_config = {} - hostname = self.hostname - - node_type_config = self.node_type_config._as_dict(include_default_values=True) # TODO: refactor this - nodes = node_type_config.get("nodes", []) - - for node in nodes: - if hostname == node["name"]: - node_config = node - break - if not node_config: - node_groups = node_type_config.get("node_groups", []) - for node_group in node_groups: - nodes = node_group.get("nodes", []) - node_group["nodes"] = nodes - for node in nodes: - if hostname == node["name"]: - node_config = node - switch_data["node_group"] = node_group - switch_data["group"] = node_group["group"] - break - if node_config: - break - - # Load defaults - defaults_config = get(node_type_config, "defaults", default={}) - - # Merge node data -> node_group data -> defaults into combined - switch_data["combined"] = merge(defaults_config, switch_data["node_group"], node_config, list_merge="replace", destructive_merge=False) - - return switch_data - - @property - def switch_data_combined(self: SharedUtils) -> dict: - """switch_data_combined containing self._switch_data['combined'] for easier reference.""" - return self.switch_data["combined"] - - @cached_property - def switch_data_node_group_nodes(self: SharedUtils) -> list: - """switch_data_node_group_nodes pointing to self.switch_data['node_group']['nodes'] for easier reference.""" - return get(self.switch_data, "node_group.nodes", default=[]) + node_config = ( + self.node_type_config.nodes[self.hostname] + if self.hostname in self.node_type_config.nodes + else EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem() + ) + + if self.node_group_config is not None: + if self.hostname in self.node_group_config.nodes: + node_config._deepinherit( + self.node_group_config.nodes[self.hostname]._cast_as( + EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem, ignore_extra_keys=True + ) + ) + node_config._deepinherit(self.node_group_config._cast_as(EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem, ignore_extra_keys=True)) + + node_config._deepinherit( + self.node_type_config.defaults._cast_as(EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem, ignore_extra_keys=True) + ) + + return node_config diff --git a/python-avd/pyavd/_eos_designs/shared_utils/wan.py b/python-avd/pyavd/_eos_designs/shared_utils/wan.py index 84473158fa2..7340d7d8ea2 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/wan.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/wan.py @@ -30,7 +30,7 @@ def wan_role(self: SharedUtils) -> str | None: return None default_wan_role = self.node_type_key_data.default_wan_role - wan_role = get(self.switch_data_combined, "wan_role", default=default_wan_role) + wan_role = self.node_config.wan_role or default_wan_role if wan_role is not None and self.overlay_routing_protocol != "ibgp": msg = "Only 'ibgp' is supported as 'overlay_routing_protocol' for WAN nodes." raise AristaAvdError(msg) @@ -67,10 +67,10 @@ def cv_pathfinder_transit_mode(self: SharedUtils) -> Literal["region", "zone"] | if not self.is_cv_pathfinder_client: return None - return get(self.switch_data_combined, "cv_pathfinder_transit_mode") + return self.node_config.cv_pathfinder_transit_mode @cached_property - def wan_interfaces(self: SharedUtils) -> list: + def wan_interfaces(self: SharedUtils) -> EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces: """ As a first approach, only interfaces under node config l3_interfaces can be considered as WAN interfaces. @@ -78,14 +78,14 @@ def wan_interfaces(self: SharedUtils) -> list: This also may require a different format for the dictionaries inside the list. """ if not self.is_wan_router: - return [] + return EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces() - wan_interfaces = [interface for interface in self.l3_interfaces if get(interface, "wan_carrier") is not None] + wan_interfaces = EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces( + [interface for interface in self.l3_interfaces if interface.wan_carrier] + ) if not wan_interfaces: msg = "At least one WAN interface must be configured on a WAN router. Add WAN interfaces under `l3_interfaces` node setting with `wan_carrier` set." - raise AristaAvdError( - msg, - ) + raise AristaAvdError(msg) return wan_interfaces @cached_property @@ -102,10 +102,10 @@ def wan_local_carriers(self: SharedUtils) -> list: local_carriers_dict = {} for interface in self.wan_interfaces: - interface_carrier = interface["wan_carrier"] + interface_carrier: str = interface.wan_carrier if interface_carrier not in local_carriers_dict: if interface_carrier not in self.inputs.wan_carriers: - msg = f"WAN carrier {interface['wan_carrier']} is not in the available carriers defined in `wan_carriers`" + msg = f"WAN carrier {interface_carrier} is not in the available carriers defined in `wan_carriers`" raise AristaAvdInvalidInputsError(msg) local_carriers_dict[interface_carrier] = self.inputs.wan_carriers[interface_carrier]._as_dict(include_default_values=True) local_carriers_dict[interface_carrier]["interfaces"] = [] @@ -113,10 +113,10 @@ def wan_local_carriers(self: SharedUtils) -> list: local_carriers_dict[interface_carrier]["interfaces"].append( strip_empties_from_dict( { - "name": get(interface, "name", required=True), + "name": interface.name, "public_ip": self.get_public_ip_for_wan_interface(interface), - "connected_to_pathfinder": get(interface, "connected_to_pathfinder", default=True), - "wan_circuit_id": get(interface, "wan_circuit_id"), + "connected_to_pathfinder": interface.connected_to_pathfinder, + "wan_circuit_id": interface.wan_circuit_id, }, ), ) @@ -170,7 +170,7 @@ def wan_ha_peer_path_group_names(self: SharedUtils) -> list: """Return a list of wan_ha_peer_path_group names.""" return [path_group["name"] for path_group in self.wan_ha_peer_path_groups] - def get_public_ip_for_wan_interface(self: SharedUtils, interface: dict) -> str: + def get_public_ip_for_wan_interface(self: SharedUtils, interface: EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3InterfacesItem) -> str: """ Takes a dict which looks like `l3_interface` from node config. @@ -181,30 +181,30 @@ def get_public_ip_for_wan_interface(self: SharedUtils, interface: dict) -> str: If there is no public_ip and if ip_address is "dhcp" we raise an error. """ if not self.is_wan_server: - return default(interface.get("public_ip"), get_ip_from_ip_prefix(interface["ip_address"])) + return default(interface.public_ip, get_ip_from_ip_prefix(interface.ip_address)) if self.hostname in self.inputs.wan_route_servers: for path_group in self.inputs.wan_route_servers[self.hostname].path_groups: - if interface["name"] not in path_group.interfaces: + if interface.name not in path_group.interfaces: continue - if public_ip := path_group.interfaces[interface["name"]].public_ip: + if public_ip := path_group.interfaces[interface.name].public_ip: return public_ip # TODO: Evaluate if this should be moved above the wan_route_servers check. It would change precedence order and is a breaking change. - if interface.get("public_ip") is not None: - return interface["public_ip"] + if interface.public_ip: + return interface.public_ip - if interface["ip_address"] == "dhcp": + if interface.ip_address == "dhcp": msg = ( - f"The IP address for WAN interface '{interface['name']}' on Route Server '{self.hostname}' is set to 'dhcp'. " + f"The IP address for WAN interface '{interface.name}' on Route Server '{self.hostname}' is set to 'dhcp'. " "Clients need to peer with a static IP which must be set under the 'wan_route_servers.path_groups.interfaces' key." ) raise AristaAvdError( msg, ) - return get_ip_from_ip_prefix(interface["ip_address"]) + return get_ip_from_ip_prefix(interface.ip_address) @cached_property def wan_site(self: SharedUtils) -> EosDesigns.CvPathfinderRegionsItem.SitesItem | EosDesigns.CvPathfinderGlobalSitesItem | None: @@ -213,13 +213,12 @@ def wan_site(self: SharedUtils) -> EosDesigns.CvPathfinderRegionsItem.SitesItem The site is required for edges, but optional for pathfinders """ - node_defined_site = get( - self.switch_data_combined, - "cv_pathfinder_site", - required=self.is_cv_pathfinder_client, - custom_error_msg="A node variable 'cv_pathfinder_site' must be defined when 'wan_role' is 'client' and 'wan_mode' is 'cv-pathfinder'.", - ) - if node_defined_site is None: + node_defined_site = self.node_config.cv_pathfinder_site + if not node_defined_site and self.is_cv_pathfinder_client: + msg = "A node variable 'cv_pathfinder_site' must be defined when 'wan_role' is 'client' and 'wan_mode' is 'cv-pathfinder'." + raise AristaAvdInvalidInputsError(msg) + + if not node_defined_site: return None # Special case for cv_pathfinders without a region, we find the site details under `cv_pathfinder_global_sites` instead. @@ -245,12 +244,11 @@ def wan_region(self: SharedUtils) -> EosDesigns.CvPathfinderRegionsItem | None: The region is required for edges, but optional for pathfinders """ - node_defined_region = get( - self.switch_data_combined, - "cv_pathfinder_region", - required=self.is_cv_pathfinder_client, - custom_error_msg="A node variable 'cv_pathfinder_region' must be defined when 'wan_role' is 'client' and 'wan_mode' is 'cv-pathfinder'.", - ) + node_defined_region = self.node_config.cv_pathfinder_region + if not node_defined_region and self.is_cv_pathfinder_client: + msg = "A node variable 'cv_pathfinder_region' must be defined when 'wan_role' is 'client' and 'wan_mode' is 'cv-pathfinder'." + raise AristaAvdInvalidInputsError(msg) + if node_defined_region is None: return None @@ -414,24 +412,22 @@ def cv_pathfinder_role(self: SharedUtils) -> str | None: @cached_property def wan_ha(self: SharedUtils) -> bool: """Only trigger HA if 2 cv_pathfinder clients are in the same group and wan_ha.enabled is true.""" - if not (self.is_cv_pathfinder_client and len(self.switch_data_node_group_nodes) == 2): + if not (self.is_cv_pathfinder_client and len(self.node_group_config.nodes) == 2): return False - if (ha_enabled := get(self.switch_data_combined, "wan_ha.enabled")) is None: + if self.node_config.wan_ha.enabled is None: msg = ( "Placing two WAN routers in a common node group will trigger WAN HA in a future AVD release. " "Currently WAN HA is in preview, so it will not be automatically enabled. " "To avoid unplanned configuration changes once the feature is released, " "it is currently required to set 'wan_ha.enabled' to 'true' or 'false'." ) - raise AristaAvdError( - msg, - ) - return ha_enabled + raise AristaAvdError(msg) + return self.node_config.wan_ha.enabled @cached_property def wan_ha_ipsec(self: SharedUtils) -> bool: - return self.wan_ha and get(self.switch_data_combined, "wan_ha.ipsec", default=True) + return self.wan_ha and self.node_config.wan_ha.ipsec @cached_property def is_first_ha_peer(self: SharedUtils) -> bool: @@ -440,29 +436,31 @@ def is_first_ha_peer(self: SharedUtils) -> bool: This should be called only from functions which have checked that HA is enabled. """ - return self.switch_data_node_group_nodes[0]["name"] == self.hostname + return next(iter(self.node_group_config.nodes.keys())) == self.hostname @cached_property def wan_ha_peer(self: SharedUtils) -> str | None: """Return the name of the WAN HA peer.""" if not self.wan_ha: return None + + keys = list(self.node_group_config.nodes.keys()) if self.is_first_ha_peer: - return self.switch_data_node_group_nodes[1]["name"] - if self.switch_data_node_group_nodes[1]["name"] == self.hostname: - return self.switch_data_node_group_nodes[0]["name"] + return keys[1] + if keys.index(self.hostname) == 1: + return keys[0] msg = "Unable to find WAN HA peer within same node group" raise AristaAvdError(msg) @cached_property def configured_wan_ha_mtu(self: SharedUtils) -> int: """Read the device wan_ha.mtu node settings.""" - return get(self.switch_data_combined, "wan_ha.mtu", default=9194) + return self.node_config.wan_ha.mtu @cached_property def configured_wan_ha_interfaces(self: SharedUtils) -> set: """Read the device wan_ha.ha_interfaces node settings.""" - return get(self.switch_data_combined, "wan_ha.ha_interfaces", default=[]) + return self.node_config.wan_ha.ha_interfaces @cached_property def vrf_default_uplinks(self: SharedUtils) -> list: @@ -515,7 +513,7 @@ def wan_ha_port_channel_id(self: SharedUtils) -> int: If not provided, computed from the list of configured members. """ - return get(self.switch_data_combined, "wan_ha.port_channel_id", default=int("".join(findall(r"\d", self.wan_ha_interfaces[0])))) + return default(self.node_config.wan_ha.port_channel_id, int("".join(findall(r"\d", self.wan_ha_interfaces[0])))) @cached_property def use_port_channel_for_direct_ha(self: SharedUtils) -> bool: @@ -532,7 +530,7 @@ def use_port_channel_for_direct_ha(self: SharedUtils) -> bool: interfaces = set(self.configured_wan_ha_interfaces) - return len(interfaces) > 1 or get(self.switch_data_combined, "wan_ha.use_port_channel_for_direct_ha", True) + return len(interfaces) > 1 or self.node_config.wan_ha.use_port_channel_for_direct_ha @cached_property def wan_ha_peer_ip_addresses(self: SharedUtils) -> list: @@ -593,12 +591,10 @@ def wan_ha_ip_addresses(self: SharedUtils) -> list: @cached_property def wan_ha_ipv4_pool(self: SharedUtils) -> str: """Return the configured wan_ha.ha_ipv4_pool.""" - return get( - self.switch_data_combined, - "wan_ha.ha_ipv4_pool", - required=True, - custom_error_msg="Missing `wan_ha.ha_ipv4_pool` node settings to allocate an IP address to defined HA interface.", - ) + if not self.node_config.wan_ha.ha_ipv4_pool: + msg = "Missing `wan_ha.ha_ipv4_pool` node settings to allocate an IP address to defined HA interface." + raise AristaAvdInvalidInputsError(msg) + return self.node_config.wan_ha.ha_ipv4_pool def generate_lb_policy_name(self: SharedUtils, name: str) -> str: """Returns LB-{name}.""" 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 918fcef16d8..7f6f5638d6a 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py @@ -8,7 +8,7 @@ from pyavd._eos_designs.avdfacts import AvdFacts from pyavd._errors import AristaAvdInvalidInputsError, AristaAvdMissingVariableError -from pyavd._utils import get, strip_empties_from_dict, strip_null_from_data +from pyavd._utils import default, get, strip_empties_from_dict, strip_null_from_data from pyavd.j2filters import natural_sort from .ntp import NtpMixin @@ -70,7 +70,7 @@ def router_bgp(self) -> dict | None: "as": self.shared_utils.bgp_as, "router_id": self.shared_utils.router_id, "distance": self.inputs.bgp_distance._as_dict() or None, - "bgp_defaults": get(self.shared_utils.switch_data_combined, "bgp_defaults"), + "bgp_defaults": self.shared_utils.node_config.bgp_defaults or None, "bgp": { "default": { "ipv4_unicast": self.inputs.bgp_default_ipv4_unicast, @@ -400,8 +400,8 @@ def spanning_tree(self) -> dict | None: if not self.shared_utils.network_services_l2: return {"mode": "none"} - spanning_tree_root_super = get(self.shared_utils.switch_data_combined, "spanning_tree_root_super") - spanning_tree_mode = get(self.shared_utils.switch_data_combined, "spanning_tree_mode") + spanning_tree_root_super = self.shared_utils.node_config.spanning_tree_root_super + spanning_tree_mode = self.shared_utils.node_config.spanning_tree_mode if spanning_tree_root_super is not True and spanning_tree_mode is None: return None @@ -411,7 +411,7 @@ def spanning_tree(self) -> dict | None: if spanning_tree_mode is not None: spanning_tree["mode"] = spanning_tree_mode - priority = get(self.shared_utils.switch_data_combined, "spanning_tree_priority", default=32768) + priority = self.shared_utils.node_config.spanning_tree_priority # "rapid-pvst" is not included below. Per vlan spanning-tree priorities are set under network-services. if spanning_tree_mode == "mstp": spanning_tree["mst_instances"] = [{"id": "0", "priority": priority}] @@ -520,7 +520,7 @@ def platform(self) -> dict | None: if trident_forwarding_table_partition and self.shared_utils.evpn_multicast: platform["trident"] = {"forwarding_table_partition": trident_forwarding_table_partition} - if (cpu_max_allocation := get(self.shared_utils.switch_data_combined, "data_plane_cpu_allocation_max")) is not None: + if (cpu_max_allocation := self.shared_utils.node_config.data_plane_cpu_allocation_max) is not None: platform["sfe"] = {"data_plane_cpu_allocation_max": cpu_max_allocation} elif self.shared_utils.is_wan_server: # For AutoVPN Route Reflectors and Pathfinders, running on CloudEOS, setting @@ -577,20 +577,18 @@ def link_tracking_groups(self) -> list | None: @cached_property def lacp(self) -> dict | None: """Lacp set based on lacp_port_id_range.""" - lacp_port_id_range = get(self.shared_utils.switch_data_combined, "lacp_port_id_range", default={}) - if lacp_port_id_range.get("enabled") is not True: + lacp_port_id_range = self.shared_utils.node_config.lacp_port_id_range + if not lacp_port_id_range.enabled: return None if (switch_id := self.shared_utils.id) is None: msg = f"'id' is not set on '{self.shared_utils.hostname}' to set LACP port ID ranges" raise AristaAvdInvalidInputsError(msg) - node_group_length = max(len(self.shared_utils.switch_data_node_group_nodes), 1) - port_range = int(get(lacp_port_id_range, "size", default=128)) - port_offset = int(get(lacp_port_id_range, "offset", default=0)) + node_group_length = max(len(self.shared_utils.node_group_config.nodes), 1) - begin = 1 + (((switch_id - 1) % node_group_length) * port_range) + port_offset - end = (((switch_id - 1) % node_group_length + 1) * port_range) + port_offset + begin = 1 + (((switch_id - 1) % node_group_length) * lacp_port_id_range.size) + lacp_port_id_range.offset + end = (((switch_id - 1) % node_group_length + 1) * lacp_port_id_range.size) + lacp_port_id_range.offset return { "port_id": { @@ -620,71 +618,44 @@ def ptp(self) -> dict | None: default_ptp_priority1 = self.shared_utils.node_type_key_data.default_ptp_priority1 default_clock_identity = None - priority1 = get(self.shared_utils.switch_data_combined, "ptp.priority1", default=default_ptp_priority1) - priority2 = get(self.shared_utils.switch_data_combined, "ptp.priority2") + priority1 = default(self.shared_utils.node_config.ptp.priority1, default_ptp_priority1) + priority2 = self.shared_utils.node_config.ptp.priority2 if priority2 is None: if self.shared_utils.id is None: msg = f"'id' must be set on '{self.shared_utils.hostname}' to set ptp priority2" raise AristaAvdInvalidInputsError(msg) priority2 = self.shared_utils.id % 256 - default_auto_clock_identity = self.inputs.ptp_settings.auto_clock_identity - if get(self.shared_utils.switch_data_combined, "ptp.auto_clock_identity", default=default_auto_clock_identity) is True: - clock_identity_prefix = get(self.shared_utils.switch_data_combined, "ptp.clock_identity_prefix", default="00:1C:73") + if default(self.shared_utils.node_config.ptp.auto_clock_identity, self.inputs.ptp_settings.auto_clock_identity): + clock_identity_prefix = self.shared_utils.node_config.ptp.clock_identity_prefix default_clock_identity = f"{clock_identity_prefix}:{priority1:02x}:00:{priority2:02x}" ptp = { - "mode": get(self.shared_utils.switch_data_combined, "ptp.mode", default="boundary"), - "mode_one_step": get(self.shared_utils.switch_data_combined, "ptp.mode_one_step"), - "forward_unicast": get(self.shared_utils.switch_data_combined, "ptp.forward_unicast"), - "clock_identity": get(self.shared_utils.switch_data_combined, "ptp.clock_identity", default=default_clock_identity), - "source": {"ip": get(self.shared_utils.switch_data_combined, "ptp.source_ip")}, + "mode": self.shared_utils.node_config.ptp.mode, + "mode_one_step": self.shared_utils.node_config.ptp.mode_one_step or None, # Historic output is without false + "forward_unicast": self.shared_utils.node_config.ptp.forward_unicast or None, # Historic output is without false + "clock_identity": default(self.shared_utils.node_config.ptp.clock_identity, default_clock_identity), + "source": {"ip": self.shared_utils.node_config.ptp.source_ip}, "priority1": priority1, "priority2": priority2, - "ttl": get(self.shared_utils.switch_data_combined, "ptp.ttl"), - "domain": get(self.shared_utils.switch_data_combined, "ptp.domain", default=default_ptp_domain), + "ttl": self.shared_utils.node_config.ptp.ttl, + "domain": default(self.shared_utils.node_config.ptp.domain, default_ptp_domain), "message_type": { "general": { - "dscp": get(self.shared_utils.switch_data_combined, "ptp.dscp.general_messages"), + "dscp": self.shared_utils.node_config.ptp.dscp.general_messages, }, "event": { - "dscp": get(self.shared_utils.switch_data_combined, "ptp.dscp.event_messages"), - }, - }, - "monitor": { - "enabled": get(self.shared_utils.switch_data_combined, "ptp.monitor.enabled", default=True), - "threshold": { - "offset_from_master": get(self.shared_utils.switch_data_combined, "ptp.monitor.threshold.offset_from_master", default=250), - "mean_path_delay": get(self.shared_utils.switch_data_combined, "ptp.monitor.threshold.mean_path_delay", default=1500), - "drop": { - "offset_from_master": get(self.shared_utils.switch_data_combined, "ptp.monitor.threshold.drop.offset_from_master"), - "mean_path_delay": get(self.shared_utils.switch_data_combined, "ptp.monitor.threshold.drop.mean_path_delay"), - }, - }, - "missing_message": { - "intervals": { - "announce": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.intervals.announce"), - "follow_up": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.intervals.follow_up"), - "sync": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.intervals.sync"), - }, - "sequence_ids": { - "enabled": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.sequence_ids.enabled", default=True), - "announce": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.sequence_ids.announce", default=3), - "delay_resp": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.sequence_ids.delay_resp", default=3), - "follow_up": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.sequence_ids.follow_up", default=3), - "sync": get(self.shared_utils.switch_data_combined, "ptp.monitor.missing_message.sequence_ids.sync", default=3), - }, + "dscp": self.shared_utils.node_config.ptp.dscp.event_messages, }, }, + "monitor": self.shared_utils.node_config.ptp.monitor._as_dict(include_default_values=True), } return strip_null_from_data(ptp, (None, {})) @cached_property def eos_cli(self) -> str | None: """Aggregate the values of raw_eos_cli and platform_settings.platform_raw_eos_cli facts.""" - raw_eos_cli = get(self.shared_utils.switch_data_combined, "raw_eos_cli") - platform_raw_eos_cli = self.shared_utils.platform_settings.raw_eos_cli - return "\n".join(filter(None, [raw_eos_cli, platform_raw_eos_cli])) or None + return "\n".join(filter(None, [self.shared_utils.node_config.raw_eos_cli, self.shared_utils.platform_settings.raw_eos_cli])) or None @cached_property def ip_radius_source_interfaces(self) -> list | None: diff --git a/python-avd/pyavd/_eos_designs/structured_config/custom_structured_configuration/__init__.py b/python-avd/pyavd/_eos_designs/structured_config/custom_structured_configuration/__init__.py index d327f0ece64..7965dd82000 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/custom_structured_configuration/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/custom_structured_configuration/__init__.py @@ -39,7 +39,7 @@ def _extract_and_apply_struct_cfg_from_list_of_dicts(self, list_of_dicts: list, return struct_cfgs def _struct_cfg(self) -> list: - if (struct_cfg := get(self.shared_utils.switch_data_combined, "structured_config")) is not None: + if struct_cfg := self.shared_utils.node_config.structured_config._as_dict(): return [struct_cfg] return [] diff --git a/python-avd/pyavd/_eos_designs/structured_config/metadata/cv_tags.py b/python-avd/pyavd/_eos_designs/structured_config/metadata/cv_tags.py index 867b03293db..c3d3d67f84b 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/metadata/cv_tags.py +++ b/python-avd/pyavd/_eos_designs/structured_config/metadata/cv_tags.py @@ -3,11 +3,10 @@ # that can be found in the LICENSE file. from __future__ import annotations -from functools import cached_property from typing import TYPE_CHECKING, Any from pyavd._errors import AristaAvdError -from pyavd._utils import default, get, get_item, strip_empties_from_dict, strip_empties_from_list +from pyavd._utils import default, get, strip_empties_from_dict, strip_empties_from_list if TYPE_CHECKING: from . import AvdStructuredConfigMetadata @@ -203,18 +202,14 @@ def _get_cv_pathfinder_interface_tags(self: AvdStructuredConfigMetadata, etherne {"name": "Circuit", } ]. """ - if ethernet_interface["name"] in self._wan_interface_names: - wan_interface = get_item(self.shared_utils.wan_interfaces, "name", ethernet_interface["name"], required=True) + if ethernet_interface["name"] in self.shared_utils.wan_interfaces: + wan_interface = self.shared_utils.wan_interfaces[ethernet_interface["name"]] return strip_empties_from_list( [ self._tag_dict("Type", "wan"), - self._tag_dict("Carrier", get(wan_interface, "wan_carrier")), - self._tag_dict("Circuit", get(wan_interface, "wan_circuit_id")), + self._tag_dict("Carrier", wan_interface.wan_carrier), + self._tag_dict("Circuit", wan_interface.wan_circuit_id), ], ) return [self._tag_dict("Type", "lan")] - - @cached_property - def _wan_interface_names(self: AvdStructuredConfigMetadata) -> list: - return [wan_interface["name"] for wan_interface in self.shared_utils.wan_interfaces] 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 f0684ca1968..d79e2c124d6 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py @@ -6,7 +6,7 @@ from functools import cached_property from pyavd._eos_designs.avdfacts import AvdFacts -from pyavd._utils import AvdStringFormatter, default, get, strip_empties_from_dict +from pyavd._utils import AvdStringFormatter, default, strip_empties_from_dict from pyavd.api.interface_descriptions import InterfaceDescriptionData from pyavd.j2filters import list_compress @@ -82,9 +82,7 @@ def vlan_interfaces(self) -> list | None: return [strip_empties_from_dict(main_vlan_interface)] # Create L3 data which will go on either a dedicated l3 vlan or the main mlag vlan - l3_cfg = { - "struct_cfg": get(self.shared_utils.switch_data_combined, "mlag_peer_l3_vlan_structured_config"), - } + l3_cfg = {"struct_cfg": self.shared_utils.node_config.mlag_peer_l3_vlan_structured_config._as_dict() or None} if self.shared_utils.underlay_routing_protocol == "ospf": l3_cfg.update( { @@ -157,12 +155,12 @@ def port_channel_interfaces(self) -> list: "mode": "trunk", "trunk": { "groups": [self.inputs.trunk_groups.mlag.name], - "allowed_vlan": get(self.shared_utils.switch_data_combined, "mlag_peer_link_allowed_vlans"), + "allowed_vlan": self.shared_utils.node_config.mlag_peer_link_allowed_vlans, }, }, "shutdown": False, "service_profile": self.inputs.p2p_uplinks_qos_profile, - "struct_cfg": get(self.shared_utils.switch_data_combined, "mlag_port_channel_structured_config"), + "struct_cfg": self.shared_utils.node_config.mlag_port_channel_structured_config._as_dict() or None, "flow_tracker": self.shared_utils.get_flow_tracker(self.inputs.fabric_flow_tracking.mlag_interfaces), } @@ -191,12 +189,9 @@ def port_channel_interfaces(self) -> list: @cached_property def ethernet_interfaces(self) -> list: - """Return dict with Ethernet Interfaces used for MLAG Peer Link.""" - if not (mlag_interfaces := self.shared_utils.mlag_interfaces): - return None - + """Return list of Ethernet Interfaces used for MLAG Peer Link.""" ethernet_interfaces = [] - for index, mlag_interface in enumerate(mlag_interfaces): + for index, mlag_interface in enumerate(self.shared_utils.mlag_interfaces): ethernet_interface = { "name": mlag_interface, "peer": self.shared_utils.mlag_peer, @@ -229,18 +224,14 @@ def ethernet_interfaces(self) -> list: def mlag_configuration(self) -> dict: """Return Structured Config for MLAG Configuration.""" mlag_configuration = { - "domain_id": get(self.shared_utils.switch_data_combined, "mlag_domain_id", default=self.shared_utils.group), + "domain_id": default(self.shared_utils.node_config.mlag_domain_id, self.shared_utils.group), "local_interface": f"Vlan{self.shared_utils.mlag_peer_vlan}", "peer_address": self.shared_utils.mlag_peer_ip, "peer_link": f"Port-Channel{self.shared_utils.mlag_port_channel_id}", "reload_delay_mlag": str(default(self.shared_utils.platform_settings.reload_delay.mlag, "")) or None, "reload_delay_non_mlag": str(default(self.shared_utils.platform_settings.reload_delay.non_mlag, "")) or None, } - if ( - get(self.shared_utils.switch_data_combined, "mlag_dual_primary_detection", default=False) is True - and self.shared_utils.mlag_peer_mgmt_ip is not None - and (self.shared_utils.mgmt_interface_vrf) is not None - ): + if self.shared_utils.node_config.mlag_dual_primary_detection and self.shared_utils.mlag_peer_mgmt_ip and self.shared_utils.mgmt_interface_vrf: mlag_configuration.update( { "peer_address_heartbeat": { diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/ethernet_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/ethernet_interfaces.py index 449ab00c017..7140fe926fc 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/ethernet_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/ethernet_interfaces.py @@ -31,7 +31,7 @@ def ethernet_interfaces(self: AvdStructuredConfigNetworkServices) -> list | None Only used with L3 or L1 network services """ - if not (self.shared_utils.network_services_l3 or self.shared_utils.network_services_l1 or self.shared_utils.l3_interfaces): + if not (self.shared_utils.network_services_l3 or self.shared_utils.network_services_l1): return None ethernet_interfaces = [] @@ -274,11 +274,11 @@ def ethernet_interfaces(self: AvdStructuredConfigNetworkServices) -> list | None { "name": connection["source_interface"], "ip_nat": { - "service_profile": self.get_internet_exit_nat_profile_name(internet_exit_policy["type"]), + "service_profile": self.get_internet_exit_nat_profile_name(internet_exit_policy.type), }, } - for internet_exit_policy in self._filtered_internet_exit_policies - for connection in internet_exit_policy.get("connections", []) + for internet_exit_policy, connections in self._filtered_internet_exit_policies_and_connections + for connection in connections if connection["type"] == "ethernet" ) 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 4d80433dad4..80871139b40 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 @@ -41,9 +41,9 @@ def _acl_internet_exit_zscaler(self: AvdStructuredConfigNetworkServices) -> dict @cached_property def _acl_internet_exit_direct(self: AvdStructuredConfigNetworkServices) -> dict | None: interface_ips = set() - for ie_policy in self._filtered_internet_exit_policies: - if ie_policy["type"] == "direct": - for connection in ie_policy["connections"]: + for ie_policy, connections in self._filtered_internet_exit_policies_and_connections: + if ie_policy.type == "direct": + for connection in connections: interface_ips.add(connection["source_interface_ip_address"]) if interface_ips: diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_security.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_security.py index 0a7644f52d5..843e72b3029 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_security.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/ip_security.py @@ -6,7 +6,7 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import get, strip_null_from_data +from pyavd._utils import strip_null_from_data from .utils import UtilsMixin @@ -24,17 +24,17 @@ class IpSecurityMixin(UtilsMixin): @cached_property def ip_security(self: AvdStructuredConfigNetworkServices) -> dict | None: """ip_security set based on cv_pathfinder_internet_exit_policies.""" - if not self._filtered_internet_exit_policies: + if not self._filtered_internet_exit_policies_and_connections: return None ip_security = {"ike_policies": [], "sa_policies": [], "profiles": []} - for internet_exit_policy in self._filtered_internet_exit_policies: + for internet_exit_policy, _connections in self._filtered_internet_exit_policies_and_connections: # Currently we only need ipsec for zscaler. - if internet_exit_policy["type"] != "zscaler": + if internet_exit_policy.type != "zscaler": continue - policy_name = internet_exit_policy["name"] - encrypt_traffic = get(internet_exit_policy, "zscaler.encrypt_traffic", default=True) + policy_name = internet_exit_policy.name + encrypt_traffic = internet_exit_policy.zscaler.encrypt_traffic ike_policy_name = f"IE-{policy_name}-IKE-POLICY" sa_policy_name = f"IE-{policy_name}-SA-POLICY" profile_name = f"IE-{policy_name}-PROFILE" diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/metadata.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/metadata.py index 0be9579a4ff..47bf196f85c 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/metadata.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/metadata.py @@ -43,29 +43,29 @@ def metadata(self: AvdStructuredConfigNetworkServices) -> dict | None: return {"cv_pathfinder": cv_pathfinder_metadata} - def get_cv_pathfinder_metadata_internet_exit_policies(self: AvdStructuredConfigNetworkServices) -> dict | None: + def get_cv_pathfinder_metadata_internet_exit_policies(self: AvdStructuredConfigNetworkServices) -> list[dict] | None: """Generate metadata.cv_pathfinder.internet_exit_policies if available.""" - if not self._filtered_internet_exit_policies: + if not self._filtered_internet_exit_policies_and_connections: return None internet_exit_polices = [] - for internet_exit_policy in self._filtered_internet_exit_policies: + for internet_exit_policy, connections in self._filtered_internet_exit_policies_and_connections: # Currently only supporting zscaler - if internet_exit_policy["type"] != "zscaler": + if internet_exit_policy.type != "zscaler": continue ufqdn, ipsec_key = self._get_ipsec_credentials(internet_exit_policy) internet_exit_polices.append( { - "name": internet_exit_policy["name"], - "type": internet_exit_policy["type"], + "name": internet_exit_policy.name, + "type": internet_exit_policy.type, "city": self._zscaler_endpoints.device_location.city, "country": self._zscaler_endpoints.device_location.country, - "upload_bandwidth": get(internet_exit_policy, "zscaler.upload_bandwidth"), - "download_bandwidth": get(internet_exit_policy, "zscaler.download_bandwidth"), - "firewall": get(internet_exit_policy, "zscaler.firewall.enabled", default=False), - "ips_control": get(internet_exit_policy, "zscaler.firewall.ips", default=False), - "acceptable_use_policy": get(internet_exit_policy, "zscaler.acceptable_use_policy", default=False), + "upload_bandwidth": internet_exit_policy.zscaler.upload_bandwidth, + "download_bandwidth": internet_exit_policy.zscaler.download_bandwidth, + "firewall": internet_exit_policy.zscaler.firewall.enabled, + "ips_control": internet_exit_policy.zscaler.firewall.ips, + "acceptable_use_policy": internet_exit_policy.zscaler.acceptable_use_policy, "vpn_credentials": [ { "fqdn": ufqdn, @@ -79,7 +79,7 @@ def get_cv_pathfinder_metadata_internet_exit_policies(self: AvdStructuredConfigN "preference": "Preferred" if connection["preference"] == "primary" else "Alternate", "endpoint": connection["endpoint"], } - for connection in internet_exit_policy["connections"] + for connection in connections ], }, ) diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/monitor_connectivity.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/monitor_connectivity.py index dbd446c9d98..0f3727364fe 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/monitor_connectivity.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/monitor_connectivity.py @@ -28,15 +28,15 @@ def monitor_connectivity(self: AvdStructuredConfigNetworkServices) -> dict | Non Only used for CV Pathfinder edge routers today """ - if not self._filtered_internet_exit_policies: + if not self._filtered_internet_exit_policies_and_connections: return None monitor_connectivity = {} interface_sets = [] hosts = [] - for policy in self._filtered_internet_exit_policies: - for connection in policy["connections"]: + for _policy, connections in self._filtered_internet_exit_policies_and_connections: + for connection in connections: interface_name = f"Tunnel{connection['tunnel_id']}" if connection["type"] == "tunnel" else connection["source_interface"] interface_set_name = f"SET-{self.shared_utils.sanitize_interface_name(interface_name)}" diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_adaptive_virtual_topology.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_adaptive_virtual_topology.py index 4c885f19416..0a6007fe19c 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_adaptive_virtual_topology.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_adaptive_virtual_topology.py @@ -110,11 +110,9 @@ def _cv_pathfinder_profiles(self: AvdStructuredConfigNetworkServices) -> list: "name": match["avt_profile"], "load_balance_policy": match["load_balance_policy"]["name"], } - if (internet_exit_policy_name := match["internet_exit_policy_name"]) is not None and get_item( - self._filtered_internet_exit_policies, - "name", - internet_exit_policy_name, - ) is not None: + if (internet_exit_policy_name := match["internet_exit_policy_name"]) is not None and internet_exit_policy_name in [ + policy.name for policy, _connections in self._filtered_internet_exit_policies_and_connections + ]: profile["internet_exit_policy"] = internet_exit_policy_name append_if_not_duplicate( @@ -129,11 +127,9 @@ def _cv_pathfinder_profiles(self: AvdStructuredConfigNetworkServices) -> list: "name": default_match["avt_profile"], "load_balance_policy": default_match["load_balance_policy"]["name"], } - if (internet_exit_policy_name := default_match["internet_exit_policy_name"]) is not None and get_item( - self._filtered_internet_exit_policies, - "name", - internet_exit_policy_name, - ) is not None: + if (internet_exit_policy_name := default_match["internet_exit_policy_name"]) is not None and internet_exit_policy_name in [ + policy.name for policy, _connections in self._filtered_internet_exit_policies_and_connections + ]: profile["internet_exit_policy"] = internet_exit_policy_name append_if_not_duplicate( diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_internet_exit.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_internet_exit.py index edf1959178c..a5b235f429b 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_internet_exit.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_internet_exit.py @@ -7,8 +7,6 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import get - from .utils import UtilsMixin if TYPE_CHECKING: @@ -29,28 +27,28 @@ def router_internet_exit(self: AvdStructuredConfigNetworkServices) -> dict | Non Only used for CV Pathfinder edge routers today """ - if not self._filtered_internet_exit_policies: + if not self._filtered_internet_exit_policies_and_connections: return None router_internet_exit = {} exit_groups_dict = defaultdict(lambda: {"local_connections": []}) policies = [] - for policy in self._filtered_internet_exit_policies: + for policy, connections in self._filtered_internet_exit_policies_and_connections: policy_exit_groups = [] # TODO: Today we use the order of the connection list to order the exit-groups inside the policy. # This works for zscaler but later we may need to use some sorting intelligence as order matters. - for connection in policy.get("connections", []): + for connection in connections: exit_group_name = connection["exit_group"] exit_groups_dict[exit_group_name]["local_connections"].append({"name": connection["name"]}) # Recording the exit_group in the policy if exit_group_name not in policy_exit_groups: policy_exit_groups.append(exit_group_name) - if get(policy, "fallback_to_system_default", default=True): + if policy.fallback_to_system_default: policy_exit_groups.append("system-default-exit-group") - policies.append({"name": policy["name"], "exit_groups": [{"name": exit_group_name} for exit_group_name in policy_exit_groups]}) + policies.append({"name": policy.name, "exit_groups": [{"name": exit_group_name} for exit_group_name in policy_exit_groups]}) if exit_groups_dict: router_internet_exit["exit_groups"] = [ diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_service_insertion.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_service_insertion.py index 004eecd07d2..1f3cef2f2c4 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_service_insertion.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_service_insertion.py @@ -26,14 +26,13 @@ def router_service_insertion(self: AvdStructuredConfigNetworkServices) -> dict | Only used for CV Pathfinder edge routers today """ - if not self._filtered_internet_exit_policies: + if not self._filtered_internet_exit_policies_and_connections: return None - router_service_insertion = {} - connections = [] + service_connections = [] - for policy in self._filtered_internet_exit_policies: - for connection in policy.get("connections", []): + for _policy, connections in self._filtered_internet_exit_policies_and_connections: + for connection in connections: service_connection = { "name": connection["name"], "monitor_connectivity_host": connection["monitor_name"], @@ -49,12 +48,9 @@ def router_service_insertion(self: AvdStructuredConfigNetworkServices) -> dict | "next_hop": connection["next_hop"], } - connections.append(service_connection) - if connections: - router_service_insertion["enabled"] = True - router_service_insertion["connections"] = connections + service_connections.append(service_connection) - if router_service_insertion: - return router_service_insertion + if service_connections: + return {"enabled": True, "connections": service_connections} return None diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/spanning_tree.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/spanning_tree.py index bf5ad397091..f199f3fb93e 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/spanning_tree.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/spanning_tree.py @@ -6,7 +6,6 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import get from pyavd.j2filters import list_compress from .utils import UtilsMixin @@ -28,11 +27,11 @@ def spanning_tree(self: AvdStructuredConfigNetworkServices) -> dict | None: if not self.shared_utils.network_services_l2: return None - spanning_tree_mode = get(self.shared_utils.switch_data_combined, "spanning_tree_mode") + spanning_tree_mode = self.shared_utils.node_config.spanning_tree_mode if spanning_tree_mode != "rapid-pvst": return None - default_priority = int(get(self.shared_utils.switch_data_combined, "spanning_tree_priority", default=32768)) + default_priority = self.shared_utils.node_config.spanning_tree_priority vlan_stp_priorities = {} non_default_vlans = set() diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/static_routes.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/static_routes.py index ea0171516c3..ff8d6eda18a 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/static_routes.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/static_routes.py @@ -66,8 +66,8 @@ def static_routes(self: AvdStructuredConfigNetworkServices) -> list[dict] | None if static_route not in static_routes: static_routes.append(static_route) - for internet_exit_policy in self._filtered_internet_exit_policies: - for connection in internet_exit_policy.get("connections", []): + for _internet_exit_policy, connections in self._filtered_internet_exit_policies_and_connections: + for connection in connections: if connection["type"] == "tunnel": static_route = { "destination_address_prefix": f"{connection['tunnel_destination_ip']}/32", diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/tunnel_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/tunnel_interfaces.py index e1121b91773..0a8cab363d7 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/tunnel_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/tunnel_interfaces.py @@ -28,13 +28,13 @@ def tunnel_interfaces(self: AvdStructuredConfigNetworkServices) -> list | None: Only used for CV Pathfinder edge routers today """ - if not self._filtered_internet_exit_policies: + if not self._filtered_internet_exit_policies_and_connections: return None tunnel_interfaces = [] - for internet_exit_policy in self._filtered_internet_exit_policies: - for connection in internet_exit_policy.get("connections", []): + for internet_exit_policy, connections in self._filtered_internet_exit_policies_and_connections: + for connection in connections: if connection["type"] == "tunnel": tunnel_interface = { "name": f"Tunnel{connection['tunnel_id']}", @@ -47,8 +47,8 @@ def tunnel_interfaces(self: AvdStructuredConfigNetworkServices) -> list | None: "ipsec_profile": connection["ipsec_profile"], } - if internet_exit_policy["type"] == "zscaler": - tunnel_interface["nat_profile"] = self.get_internet_exit_nat_profile_name(internet_exit_policy["type"]) + if internet_exit_policy.type == "zscaler": + tunnel_interface["nat_profile"] = self.get_internet_exit_nat_profile_name(internet_exit_policy.type) append_if_not_duplicate( list_of_dicts=tunnel_interfaces, 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 7d2163501a3..acdf0c23bbc 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 @@ -7,8 +7,8 @@ from typing import TYPE_CHECKING, Literal from pyavd._eos_designs.schema import EosDesigns -from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError -from pyavd._utils import get, get_all, get_ip_from_ip_prefix, get_item +from pyavd._errors import AristaAvdError, AristaAvdInvalidInputsError, AristaAvdMissingVariableError +from pyavd._utils import get, get_ip_from_ip_prefix from pyavd._utils.password_utils.password import simple_7_encrypt from pyavd.j2filters import natural_sort, range_expand @@ -510,7 +510,7 @@ def get_internet_exit_nat_pool_and_profile( @cached_property def _filtered_internet_exit_policy_types(self: AvdStructuredConfigNetworkServices) -> list: - return sorted({internet_exit_policy["type"] for internet_exit_policy in self._filtered_internet_exit_policies}) + return sorted({internet_exit_policy.type for internet_exit_policy, _connections in self._filtered_internet_exit_policies_and_connections}) @cached_property def _l3_interface_acls(self: AvdStructuredConfigNetworkServices) -> dict | None: @@ -560,14 +560,17 @@ def _l3_interface_acls(self: AvdStructuredConfigNetworkServices) -> dict | None: return l3_interface_acls @cached_property - def _filtered_internet_exit_policies(self: AvdStructuredConfigNetworkServices) -> list[dict]: + def _filtered_internet_exit_policies_and_connections( + self: AvdStructuredConfigNetworkServices, + ) -> list[tuple[EosDesigns.CvPathfinderInternetExitPoliciesItem, list[dict]]]: """ Only supported for CV Pathfinder Edge routers. Returns an empty list for pathfinders. - Parse self._filtered_wan_policies looking to internet_exit_policies. - Verify each internet_exit_policy is present in inputs `cv_pathfinder_internet_exit_policies`. - get_internet_exit_connections and insert into the policy dict. - - Return the list of relevant internet_exit_policies. + + Return the list of relevant internet_exit_policies together with the connections for that policy. """ if not self.shared_utils.is_cv_pathfinder_client: return [] @@ -613,123 +616,130 @@ def _filtered_internet_exit_policies(self: AvdStructuredConfigNetworkServices) - internet_exit_policies = [] for internet_exit_policy in candidate_internet_exit_policies: - local_interfaces = [ - wan_interface - for wan_interface in self.shared_utils.wan_interfaces - if internet_exit_policy.name in get_all(wan_interface, "cv_pathfinder_internet_exit.policies.name") - ] + local_interfaces = EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces( + [ + wan_interface + for wan_interface in self.shared_utils.wan_interfaces + if internet_exit_policy.name in wan_interface.cv_pathfinder_internet_exit.policies + ] + ) if not local_interfaces: # No local interface for this policy # TODO: Decide if we should raise here instead continue - internet_exit_policy_dict = internet_exit_policy._as_dict() - internet_exit_policy_dict["connections"] = self.get_internet_exit_connections(internet_exit_policy_dict, local_interfaces) - internet_exit_policies.append(internet_exit_policy_dict) + connections = self.get_internet_exit_connections(internet_exit_policy, local_interfaces) + internet_exit_policies.append((internet_exit_policy, connections)) return internet_exit_policies - def get_internet_exit_connections(self: AvdStructuredConfigNetworkServices, internet_exit_policy: dict, local_interfaces: list[dict]) -> list: + def get_internet_exit_connections( + self: AvdStructuredConfigNetworkServices, + internet_exit_policy: EosDesigns.CvPathfinderInternetExitPoliciesItem, + local_interfaces: EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces, + ) -> list: """ Return a list of connections (dicts) for the given internet_exit_policy. These are useful for easy creation of connectivity-monitor, service-insertion connections, exit-groups, tunnels etc. """ - policy_name = internet_exit_policy["name"] - policy_type = internet_exit_policy["type"] - - if policy_type == "direct": + if internet_exit_policy.type == "direct": return self.get_direct_internet_exit_connections(internet_exit_policy, local_interfaces) - if policy_type == "zscaler": + if internet_exit_policy.type == "zscaler": return self.get_zscaler_internet_exit_connections(internet_exit_policy, local_interfaces) - msg = f"Unsupported type '{policy_type}' found in cv_pathfinder_internet_exit[name={policy_name}]." + msg = f"Unsupported type '{internet_exit_policy.type}' found in cv_pathfinder_internet_exit[name={internet_exit_policy.name}]." raise AristaAvdError(msg) - def get_direct_internet_exit_connections(self: AvdStructuredConfigNetworkServices, internet_exit_policy: dict, local_interfaces: list[dict]) -> list: + def get_direct_internet_exit_connections( + self: AvdStructuredConfigNetworkServices, + internet_exit_policy: EosDesigns.CvPathfinderInternetExitPoliciesItem, + local_interfaces: EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces, + ) -> list[dict]: """Return a list of connections (dicts) for the given internet_exit_policy of type direct.""" - if get(internet_exit_policy, "type") != "direct": + if internet_exit_policy.type != "direct": return [] connections = [] # Build internet exit connection for each local interface (wan_interface) for wan_interface in local_interfaces: - wan_interface_internet_exit_policies = get(wan_interface, "cv_pathfinder_internet_exit.policies", default=[]) - if get_item(wan_interface_internet_exit_policies, "name", internet_exit_policy["name"]) is None: + if internet_exit_policy.name not in wan_interface.cv_pathfinder_internet_exit.policies: continue - if not wan_interface.get("peer_ip"): + if not wan_interface.peer_ip: msg = ( - f"{wan_interface['name']} peer_ip needs to be set. When using wan interface " + f"{wan_interface.name} peer_ip needs to be set. When using wan interface " "for direct type internet exit, peer_ip is used for nexthop, and connectivity monitoring." ) raise AristaAvdInvalidInputsError(msg) # wan interface ip will be used for acl, hence raise error if ip is not available - if (ip_address := wan_interface.get("ip_address")) == "dhcp" and not (ip_address := wan_interface.get("dhcp_ip")): + if (ip_address := wan_interface.ip_address) == "dhcp" and not (ip_address := wan_interface.dhcp_ip): msg = ( - f"{wan_interface['name']} 'dhcp_ip' needs to be set. When using WAN interface for 'direct' type Internet exit, " + f"{wan_interface.name} 'dhcp_ip' needs to be set. When using WAN interface for 'direct' type Internet exit, " "'dhcp_ip' is used in the NAT ACL." ) raise AristaAvdInvalidInputsError(msg) - sanitized_interface_name = self.shared_utils.sanitize_interface_name(wan_interface["name"]) + sanitized_interface_name = self.shared_utils.sanitize_interface_name(wan_interface.name) connections.append( { "type": "ethernet", "name": f"IE-{sanitized_interface_name}", "source_interface_ip_address": ip_address, "monitor_name": f"IE-{sanitized_interface_name}", - "monitor_host": wan_interface["peer_ip"], - "next_hop": wan_interface["peer_ip"], - "source_interface": wan_interface["name"], - "description": f"Internet Exit {internet_exit_policy['name']}", - "exit_group": f"{internet_exit_policy['name']}", + "monitor_host": wan_interface.peer_ip, + "next_hop": wan_interface.peer_ip, + "source_interface": wan_interface.name, + "description": f"Internet Exit {internet_exit_policy.name}", + "exit_group": f"{internet_exit_policy.name}", }, ) - return connections - def get_zscaler_internet_exit_connections(self: AvdStructuredConfigNetworkServices, internet_exit_policy: dict, local_interfaces: list[dict]) -> list: + def get_zscaler_internet_exit_connections( + self: AvdStructuredConfigNetworkServices, + internet_exit_policy: EosDesigns.CvPathfinderInternetExitPoliciesItem, + local_interfaces: EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3Interfaces, + ) -> list: """Return a list of connections (dicts) for the given internet_exit_policy of type zscaler.""" - if get(internet_exit_policy, "type") != "zscaler": + if internet_exit_policy.type != "zscaler": return [] - policy_name = internet_exit_policy["name"] + policy_name = internet_exit_policy.name cloud_name = self._zscaler_endpoints.cloud_name connections = [] # Build internet exit connection for each local interface (wan_interface) for wan_interface in local_interfaces: - wan_interface_internet_exit_policies = get(wan_interface, "cv_pathfinder_internet_exit.policies", default=[]) - if (interface_policy_config := get_item(wan_interface_internet_exit_policies, "name", internet_exit_policy["name"])) is None: + if policy_name not in wan_interface.cv_pathfinder_internet_exit.policies: continue + interface_policy_config = wan_interface.cv_pathfinder_internet_exit.policies[policy_name] + + if not wan_interface.peer_ip: + msg = f"The configured internet-exit policy requires `peer_ip` configured under the WAN Interface {wan_interface.name}." + raise AristaAvdInvalidInputsError(msg) + connection_base = { "type": "tunnel", - "source_interface": wan_interface["name"], - "next_hop": get( - wan_interface, - "peer_ip", - required=True, - custom_error_msg=f"The configured internet-exit policy requires `peer_ip` configured under the WAN Interface {wan_interface['name']}.", - ), + "source_interface": wan_interface.name, + "next_hop": wan_interface.peer_ip, # Accepting SonarLint issue: The URL is just for verifying connectivity. No data is passed. "monitor_url": f"http://gateway.{cloud_name}.net/vpntest", # NOSONAR } - tunnel_interface_numbers = get(interface_policy_config, "tunnel_interface_numbers") - if tunnel_interface_numbers is None: + if interface_policy_config.tunnel_interface_numbers is None: msg = ( - f"{wan_interface['name']}.cv_pathfinder_internet_exit.policies[{internet_exit_policy['name']}]." + f"{wan_interface.name}.cv_pathfinder_internet_exit.policies[{internet_exit_policy.name}]." "tunnel_interface_numbers needs to be set, when using wan interface for zscaler type internet exit." ) raise AristaAvdInvalidInputsError(msg) - tunnel_id_range = range_expand(tunnel_interface_numbers) + tunnel_id_range = range_expand(interface_policy_config.tunnel_interface_numbers) zscaler_endpoints = (self._zscaler_endpoints.primary, self._zscaler_endpoints.secondary, self._zscaler_endpoints.tertiary) for index, zscaler_endpoint in enumerate(zscaler_endpoints): @@ -762,16 +772,22 @@ def get_zscaler_internet_exit_connections(self: AvdStructuredConfigNetworkServic "endpoint": zscaler_endpoint, }, ) - return connections - def _get_ipsec_credentials(self: AvdStructuredConfigNetworkServices, internet_exit_policy: dict) -> tuple[str, str]: + def _get_ipsec_credentials( + self: AvdStructuredConfigNetworkServices, internet_exit_policy: EosDesigns.CvPathfinderInternetExitPoliciesItem + ) -> tuple[str, str]: """Returns ufqdn, shared_key based on various details from the given internet_exit_policy.""" - policy_name = internet_exit_policy["name"] - domain_name = get(internet_exit_policy, "zscaler.domain_name", required=True) - ipsec_key_salt = get(internet_exit_policy, "zscaler.ipsec_key_salt", required=True) - ipsec_key = self._generate_ipsec_key(name=policy_name, salt=ipsec_key_salt) - ufqdn = f"{self.shared_utils.hostname}_{policy_name}@{domain_name}" + if not internet_exit_policy.zscaler.domain_name: + msg = "zscaler.domain_name" + raise AristaAvdMissingVariableError(msg) + + if not internet_exit_policy.zscaler.ipsec_key_salt: + msg = "zscaler.ipsec_key_salt" + raise AristaAvdMissingVariableError(msg) + + ipsec_key = self._generate_ipsec_key(name=internet_exit_policy.name, salt=internet_exit_policy.zscaler.ipsec_key_salt) + ufqdn = f"{self.shared_utils.hostname}_{internet_exit_policy.name}@{internet_exit_policy.zscaler.domain_name}" return ufqdn, ipsec_key def _generate_ipsec_key(self: AvdStructuredConfigNetworkServices, name: str, salt: str) -> str: diff --git a/python-avd/pyavd/_eos_designs/structured_config/overlay/router_bgp.py b/python-avd/pyavd/_eos_designs/structured_config/overlay/router_bgp.py index 70916f1e4ed..23ef26d52e5 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/overlay/router_bgp.py +++ b/python-avd/pyavd/_eos_designs/structured_config/overlay/router_bgp.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdError -from pyavd._utils import AvdStringFormatter, default, get, strip_empties_from_dict +from pyavd._utils import AvdStringFormatter, default, strip_empties_from_dict from pyavd.j2filters import natural_sort from .utils import UtilsMixin @@ -53,7 +53,7 @@ def _bgp_cluster_id(self: AvdStructuredConfigOverlay) -> str | None: if self.shared_utils.overlay_routing_protocol == "ibgp" and ( self.shared_utils.evpn_role == "server" or self.shared_utils.mpls_overlay_role == "server" ): - return get(self.shared_utils.switch_data_combined, "bgp_cluster_id", default=self.shared_utils.router_id) + return default(self.shared_utils.node_config.bgp_cluster_id, self.shared_utils.router_id) return None def _bgp_listen_ranges(self: AvdStructuredConfigOverlay) -> list | None: @@ -171,8 +171,8 @@ def _peer_groups(self: AvdStructuredConfigOverlay) -> list | None: peer_groups.append( { **self._generate_base_peer_group("mpls", "ipvpn_gateway_peers"), - "local_as": self._ipvpn_gateway_local_as, - "maximum_routes": get(self.shared_utils.switch_data_combined, "ipvpn_gateway.maximum_routes", default=0), + "local_as": self.shared_utils.node_config.ipvpn_gateway.local_as, + "maximum_routes": self.shared_utils.node_config.ipvpn_gateway.maximum_routes, }, ) @@ -292,7 +292,7 @@ def _address_family_evpn(self: AvdStructuredConfigOverlay) -> dict | None: } if self.shared_utils.overlay_dpath is True: - address_family_evpn["domain_identifier"] = get(self.shared_utils.switch_data_combined, "ipvpn_gateway.evpn_domain_id", default="65535:1") + address_family_evpn["domain_identifier"] = self.shared_utils.node_config.ipvpn_gateway.evpn_domain_id if self.shared_utils.is_wan_server: address_family_evpn["next_hop"] = {"resolution_disabled": True} @@ -456,7 +456,7 @@ def _address_family_vpn_ipvx(self: AvdStructuredConfigOverlay, version: int) -> address_family_vpn_ipvx["peer_groups"] = peer_groups if self.shared_utils.overlay_dpath is True: - address_family_vpn_ipvx["domain_identifier"] = get(self.shared_utils.switch_data_combined, "ipvpn_gateway.ipvpn_domain_id", default="65535:2") + address_family_vpn_ipvx["domain_identifier"] = self.shared_utils.node_config.ipvpn_gateway.ipvpn_domain_id return address_family_vpn_ipvx @@ -632,7 +632,7 @@ def _neighbors(self: AvdStructuredConfigOverlay) -> list | None: overlay_peering_interface=data.get("overlay_peering_interface"), ) # Add ebgp_multihop if the gw peer is an ebgp peer. - if data["bgp_as"] != default(self._ipvpn_gateway_local_as, self.shared_utils.bgp_as): + if data["bgp_as"] != default(self.shared_utils.node_config.ipvpn_gateway.local_as, self.shared_utils.bgp_as): neighbor["ebgp_multihop"] = self.inputs.evpn_ebgp_gateway_multihop neighbors.append(neighbor) diff --git a/python-avd/pyavd/_eos_designs/structured_config/overlay/router_path_selection.py b/python-avd/pyavd/_eos_designs/structured_config/overlay/router_path_selection.py index 16daf87df4a..60dac18fc54 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/overlay/router_path_selection.py +++ b/python-avd/pyavd/_eos_designs/structured_config/overlay/router_path_selection.py @@ -29,7 +29,7 @@ def router_path_selection(self: AvdStructuredConfigOverlay) -> dict | None: return None router_path_selection = { - "tcp_mss_ceiling": {"ipv4_segment_size": get(self.shared_utils.switch_data_combined, "dps_mss_ipv4", default="auto")}, + "tcp_mss_ceiling": {"ipv4_segment_size": self.shared_utils.node_config.dps_mss_ipv4}, "path_groups": self._get_path_groups(), } diff --git a/python-avd/pyavd/_eos_designs/structured_config/overlay/stun.py b/python-avd/pyavd/_eos_designs/structured_config/overlay/stun.py index b05c950db23..c393563ba2f 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/overlay/stun.py +++ b/python-avd/pyavd/_eos_designs/structured_config/overlay/stun.py @@ -30,7 +30,7 @@ def stun(self: AvdStructuredConfigOverlay) -> dict | None: stun = {} if self.shared_utils.is_wan_server: - local_interfaces = [wan_interface["name"] for wan_interface in self.shared_utils.wan_interfaces] + local_interfaces = [wan_interface.name for wan_interface in self.shared_utils.wan_interfaces] stun["server"] = { "local_interfaces": local_interfaces, "ssl_profile": self.shared_utils.wan_stun_dtls_profile_name, diff --git a/python-avd/pyavd/_eos_designs/structured_config/overlay/utils.py b/python-avd/pyavd/_eos_designs/structured_config/overlay/utils.py index e45eac6bc43..c0c0b80ebd6 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/overlay/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/overlay/utils.py @@ -38,32 +38,30 @@ def _evpn_gateway_remote_peers(self: AvdStructuredConfigOverlay) -> dict: evpn_gateway_remote_peers = {} - evpn_gateway_remote_peers_list = get(self.shared_utils.switch_data_combined, "evpn_gateway.remote_peers", default=[]) + for remote_peer in self.shared_utils.node_config.evpn_gateway.remote_peers._natural_sorted(): + remote_peer_name = remote_peer.hostname - for gw_remote_peer_dict in natural_sort(evpn_gateway_remote_peers_list, sort_key="hostname"): # These remote gateways can be outside of the inventory or in the inventory - gw_remote_peer = gw_remote_peer_dict["hostname"] - gw_info = strip_empties_from_dict( { - "bgp_as": str(_as) if (_as := gw_remote_peer_dict.get("bgp_as")) else None, - "ip_address": gw_remote_peer_dict.get("ip_address"), + "bgp_as": remote_peer.bgp_as, + "ip_address": remote_peer.ip_address, # Not adding the "overlay_peering_interface" since we do not know it for this device. Only used for description. } ) - peer_facts = self.shared_utils.get_peer_facts(gw_remote_peer, required=False) + peer_facts = self.shared_utils.get_peer_facts(remote_peer_name, required=False) if peer_facts is None: # No matching host found in the inventory for this remote gateway - evpn_gateway_remote_peers[gw_remote_peer] = gw_info + evpn_gateway_remote_peers[remote_peer_name] = gw_info else: # Found a matching name for this remote gateway in the inventory - self._append_peer(evpn_gateway_remote_peers, gw_remote_peer, peer_facts) + self._append_peer(evpn_gateway_remote_peers, remote_peer_name, peer_facts) # Apply potential override if present in the input variables - evpn_gateway_remote_peers[gw_remote_peer].update(strip_empties_from_dict(gw_info)) + evpn_gateway_remote_peers[remote_peer_name].update(strip_empties_from_dict(gw_info)) - if any(key not in evpn_gateway_remote_peers[gw_remote_peer] for key in ["bgp_as", "ip_address"]): - msg = f"The EVPN Gateway remote peer '{gw_remote_peer}' is missing either a `bpg_as` or an `ip_address`." + if any(key not in evpn_gateway_remote_peers[remote_peer_name] for key in ["bgp_as", "ip_address"]): + msg = f"The EVPN Gateway remote peer '{remote_peer_name}' is missing either a `bpg_as` or an `ip_address`." raise AristaAvdError(msg) return evpn_gateway_remote_peers @@ -120,10 +118,6 @@ def _is_peer_mpls_client(self: AvdStructuredConfigOverlay, peer_facts: dict) -> def _is_peer_mpls_server(self: AvdStructuredConfigOverlay, peer_facts: dict) -> bool: return peer_facts.get("mpls_overlay_role") == "server" or (peer_facts.get("evpn_role") == "server" and get(peer_facts, "overlay.evpn_mpls") is True) - @cached_property - def _ipvpn_gateway_local_as(self: AvdStructuredConfigOverlay) -> str | None: - return str(_as) if (_as := get(self.shared_utils.switch_data_combined, "ipvpn_gateway.local_as")) is not None else None - @cached_property def _ipvpn_gateway_remote_peers(self: AvdStructuredConfigOverlay) -> dict: if self.shared_utils.overlay_ipvpn_gateway is not True: @@ -131,21 +125,14 @@ def _ipvpn_gateway_remote_peers(self: AvdStructuredConfigOverlay) -> dict: ipvpn_gateway_remote_peers = {} - for ipvpn_gw_peer_dict in natural_sort( - get( - self.shared_utils.switch_data_combined, - "ipvpn_gateway.remote_peers", - default=[], - ), - "hostname", - ): + for remote_peer in self.shared_utils.node_config.ipvpn_gateway.remote_peers._natural_sorted(): # These remote gw are outside of the inventory - bgp_as = ipvpn_gw_peer_dict["bgp_as"] + bgp_as = remote_peer.bgp_as - ipvpn_gateway_remote_peers[ipvpn_gw_peer_dict["hostname"]] = { + ipvpn_gateway_remote_peers[remote_peer.hostname] = { "bgp_as": str(bgp_as) if bgp_as is not None else None, - "ip_address": ipvpn_gw_peer_dict["ip_address"], + "ip_address": remote_peer.ip_address, # Not adding the "overlay_peering_interface" since we do not know it for this device. Only used for description. } diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/ethernet_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/ethernet_interfaces.py index 73d430c69b0..df5e987770d 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/ethernet_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/ethernet_interfaces.py @@ -6,8 +6,7 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._eos_designs.schema import EosDesigns -from pyavd._errors import AristaAvdError +from pyavd._errors import AristaAvdError, AristaAvdMissingVariableError from pyavd._utils import append_if_not_duplicate, get, strip_null_from_data from pyavd.api.interface_descriptions import InterfaceDescriptionData from pyavd.j2filters import encrypt, natural_sort @@ -77,7 +76,6 @@ def ethernet_interfaces(self: AvdStructuredConfigUnderlay) -> list | None: ptp_config["enable"] = True ptp_config.pop("profile", None) - ethernet_interface["ptp"] = ptp_config # MPLS @@ -248,10 +246,9 @@ def ethernet_interfaces(self: AvdStructuredConfigUnderlay) -> list | None: # Support l3_interface as sub interfaces subif_parent_interface_names = set() for l3_interface in self.shared_utils.l3_interfaces: - interface_name = l3_interface["name"] - if "." in interface_name: + if "." in l3_interface.name: # This is a subinterface so we need to ensure that the parent is created - parent_interface_name, _ = interface_name.split(".", maxsplit=1) + parent_interface_name, _ = l3_interface.name.split(".", maxsplit=1) subif_parent_interface_names.add(parent_interface_name) ethernet_interface = self._get_l3_interface_cfg(l3_interface) @@ -308,13 +305,13 @@ def _get_direct_ha_ethernet_interfaces(self: AvdStructuredConfigUnderlay) -> lis direct_wan_ha_interfaces = [] - direct_wan_ha_links_flow_tracker = self.shared_utils.get_flow_tracker( - EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.WanHa.FlowTracking._from_dict( - get(self.shared_utils.switch_data_combined, "wan_ha.flow_tracking", default={}) - ) - ) + direct_wan_ha_links_flow_tracker = self.shared_utils.get_flow_tracker(self.shared_utils.node_config.wan_ha.flow_tracking) + + if not self.shared_utils.node_config.wan_ha.ha_interfaces: + msg = "wan_ha.ha_interfaces" + raise AristaAvdMissingVariableError(msg) - for index, interface in enumerate(get(self.shared_utils.switch_data_combined, "wan_ha.ha_interfaces", required=True)): + for index, interface in enumerate(self.shared_utils.node_config.wan_ha.ha_interfaces): description = self.shared_utils.interface_descriptions.wan_ha_ethernet_interface( InterfaceDescriptionData( shared_utils=self.shared_utils, 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 6a3492a77b7..0887b21c8e3 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 @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import default, get +from pyavd._utils import default from pyavd.api.interface_descriptions import InterfaceDescriptionData from .utils import UtilsMixin @@ -103,9 +103,9 @@ def loopback_interfaces(self: AvdStructuredConfigUnderlay) -> list | None: return loopback_interfaces @cached_property - def _node_sid(self: AvdStructuredConfigUnderlay) -> str: + def _node_sid(self: AvdStructuredConfigUnderlay) -> int: if self.shared_utils.id is None: msg = f"'id' is not set on '{self.shared_utils.hostname}' and is required to set node SID" raise AristaAvdInvalidInputsError(msg) - node_sid_base = int(get(self.shared_utils.switch_data_combined, "node_sid_base", 0)) - return self.shared_utils.id + node_sid_base + + return self.shared_utils.id + self.shared_utils.node_config.node_sid_base diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/port_channel_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/port_channel_interfaces.py index 87688c29d10..e751803299f 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/port_channel_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/port_channel_interfaces.py @@ -6,7 +6,6 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._eos_designs.schema import EosDesigns from pyavd._utils import append_if_not_duplicate, get, short_esi_to_route_target, strip_null_from_data from pyavd.api.interface_descriptions import InterfaceDescriptionData @@ -130,11 +129,7 @@ def _get_direct_ha_port_channel_interface(self: AvdStructuredConfigUnderlay) -> if not self.shared_utils.use_port_channel_for_direct_ha: return None - direct_wan_ha_links_flow_tracker = self.shared_utils.get_flow_tracker( - EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.WanHa.FlowTracking._from_dict( - get(self.shared_utils.switch_data_combined, "wan_ha.flow_tracking", default={}) - ) - ) + direct_wan_ha_links_flow_tracker = self.shared_utils.get_flow_tracker(self.shared_utils.node_config.wan_ha.flow_tracking) port_channel_name = f"Port-Channel{self.shared_utils.wan_ha_port_channel_id}" description = self.shared_utils.interface_descriptions.wan_ha_port_channel_interface( InterfaceDescriptionData( diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/router_isis.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/router_isis.py index 3b208865c41..0996c6b8ce4 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/router_isis.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/router_isis.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from pyavd._errors import AristaAvdInvalidInputsError -from pyavd._utils import get +from pyavd._utils import default from .utils import UtilsMixin @@ -75,7 +75,7 @@ def router_isis(self: AvdStructuredConfigUnderlay) -> dict | None: @cached_property def _isis_net(self: AvdStructuredConfigUnderlay) -> str | None: if self.inputs.isis_system_id_format == "node_id": - isis_system_id_prefix = get(self.shared_utils.switch_data_combined, "isis_system_id_prefix") + isis_system_id_prefix = self.shared_utils.node_config.isis_system_id_prefix if self.shared_utils.underlay_isis is True and isis_system_id_prefix is None: msg = ( f"'isis_system_id_prefix' is required when 'isis_system_id_format' is set to 'node_id'." @@ -96,7 +96,7 @@ def _isis_net(self: AvdStructuredConfigUnderlay) -> str | None: @cached_property def _is_type(self: AvdStructuredConfigUnderlay) -> str: default_is_type = self.inputs.isis_default_is_type - is_type = str(get(self.shared_utils.switch_data_combined, "is_type", default=default_is_type)).lower() + is_type = default(self.shared_utils.node_config.is_type, default_is_type) if is_type not in ["level-1", "level-2", "level-1-2"]: is_type = default_is_type return is_type diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/static_routes.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/static_routes.py index e4b6c563eeb..e54f53638de 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/static_routes.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/static_routes.py @@ -6,7 +6,7 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._utils import get +from pyavd._errors import AristaAvdInvalidInputsError from .utils import UtilsMixin @@ -32,21 +32,16 @@ def static_routes(self: AvdStructuredConfigUnderlay) -> list[dict] | None: static_routes = [] for l3_interface in self.shared_utils.l3_interfaces: - if not (l3_interface_static_routes := get(l3_interface, "static_routes")): + if not l3_interface.static_routes: continue - interface_name = get(l3_interface, "name", required=True, org_key=f"...[node={self.shared_utils.hostname}].l3_interfaces[].name]") - - gateway = get( - l3_interface, - "peer_ip", - required=True, - custom_error_msg=f"Cannot set a static_route route for interface {interface_name} because 'peer_ip' is missing.", - ) + if not l3_interface.peer_ip: + msg = f"Cannot set a static_route route for interface {l3_interface.name} because 'peer_ip' is missing." + raise AristaAvdInvalidInputsError(msg) static_routes.extend( - {"destination_address_prefix": l3_interface_static_route["prefix"], "gateway": gateway} - for l3_interface_static_route in l3_interface_static_routes + {"destination_address_prefix": l3_interface_static_route.prefix, "gateway": l3_interface.peer_ip} + for l3_interface_static_route in l3_interface.static_routes ) if static_routes: 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 3d065a39d66..2d3cecfbcc8 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py @@ -6,13 +6,14 @@ from functools import cached_property from typing import TYPE_CHECKING -from pyavd._eos_designs.schema import EosDesigns from pyavd._errors import AristaAvdError from pyavd._utils import default, get, get_ip_from_ip_prefix, get_item, strip_empties_from_dict from pyavd.api.interface_descriptions import InterfaceDescriptionData from pyavd.j2filters import natural_sort, range_expand if TYPE_CHECKING: + from pyavd._eos_designs.schema import EosDesigns + from . import AvdStructuredConfigUnderlay @@ -142,69 +143,60 @@ def _underlay_vlan_trunk_groups(self: AvdStructuredConfigUnderlay) -> list: def _uplinks(self: AvdStructuredConfigUnderlay) -> list: return get(self._hostvars, "switch.uplinks") - def _get_l3_interface_cfg(self: AvdStructuredConfigUnderlay, l3_interface: dict) -> dict | None: + def _get_l3_interface_cfg( + self: AvdStructuredConfigUnderlay, l3_interface: EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3InterfacesItem + ) -> dict | None: """Returns structured_configuration for one L3 interface.""" - interface_name = get(l3_interface, "name", required=True, org_key=f"...[node={self.shared_utils.hostname}].l3_interfaces[].name]") - - interface_description = l3_interface.get("description") + interface_description = l3_interface.description if not interface_description: interface_description = self.shared_utils.interface_descriptions.underlay_ethernet_interface( InterfaceDescriptionData( shared_utils=self.shared_utils, - interface=interface_name, - peer=l3_interface.get("peer"), - peer_interface=l3_interface.get("peer_interface"), - wan_carrier=l3_interface.get("wan_carrier"), - wan_circuit_id=l3_interface.get("wan_circuit_id"), + interface=l3_interface.name, + peer=l3_interface.peer, + peer_interface=l3_interface.peer_interface, + wan_carrier=l3_interface.wan_carrier, + wan_circuit_id=l3_interface.wan_circuit_id, ), ) # TODO: catch if ip_address is not valid or not dhcp - ip_address = get( - l3_interface, - "ip_address", - required=True, - org_key=f"{self.shared_utils.node_type_key_data.key}.nodes[name={self.shared_utils.hostname}].l3_interfaces[name={interface_name}].ip_address", - ) - interface = { - "name": interface_name, + "name": l3_interface.name, "peer_type": "l3_interface", - "peer": l3_interface.get("peer"), - "peer_interface": l3_interface.get("peer_interface"), - "ip_address": ip_address, - "shutdown": not l3_interface.get("enabled", True), - "switchport": {"enabled": False if "." not in interface_name else None}, + "peer": l3_interface.peer, + "peer_interface": l3_interface.peer_interface, + "ip_address": l3_interface.ip_address, + "shutdown": not l3_interface.enabled, + "switchport": {"enabled": False if "." not in l3_interface.name else None}, "description": interface_description, - "speed": l3_interface.get("speed"), - "service_profile": l3_interface.get("qos_profile"), - "access_group_in": get(self._l3_interface_acls, f"{interface_name}..ipv4_acl_in..name", separator=".."), - "access_group_out": get(self._l3_interface_acls, f"{interface_name}..ipv4_acl_out..name", separator=".."), - "eos_cli": l3_interface.get("raw_eos_cli"), - "struct_cfg": l3_interface.get("structured_config"), - "flow_tracker": self.shared_utils.get_flow_tracker( - EosDesigns._DynamicKeys.DynamicNodeTypesItem.NodeTypes.NodesItem.L3InterfacesItem.FlowTracking._from_dict(l3_interface.get("flow_tracking", {})) - ), + "speed": l3_interface.speed, + "service_profile": l3_interface.qos_profile, + "access_group_in": get(self._l3_interface_acls, f"{l3_interface.name}..ipv4_acl_in..name", separator=".."), + "access_group_out": get(self._l3_interface_acls, f"{l3_interface.name}..ipv4_acl_out..name", separator=".."), + "eos_cli": l3_interface.raw_eos_cli, + "struct_cfg": l3_interface.structured_config._as_dict(), + "flow_tracker": self.shared_utils.get_flow_tracker(l3_interface.flow_tracking), } if self.inputs.fabric_sflow.l3_interfaces is not None: interface["sflow"] = {"enable": self.inputs.fabric_sflow.l3_interfaces} - if "." in interface_name: - interface["encapsulation_dot1q"] = {"vlan": int(get(l3_interface, "encapsulation_dot1q_vlan", default=interface_name.split(".")[-1]))} + if "." in l3_interface.name: + interface["encapsulation_dot1q"] = {"vlan": default(l3_interface.encapsulation_dot1q_vlan, int(l3_interface.name.split(".", maxsplit=1)[-1]))} - if ip_address == "dhcp" and l3_interface.get("dhcp_accept_default_route", True): + if l3_interface.ip_address == "dhcp" and l3_interface.dhcp_accept_default_route: interface["dhcp_client_accept_default_route"] = True if ( self.shared_utils.is_wan_router - and (wan_carrier_name := l3_interface.get("wan_carrier")) is not None + and (wan_carrier_name := l3_interface.wan_carrier) is not None and interface["access_group_in"] is None and (wan_carrier_name not in self.inputs.wan_carriers or not self.inputs.wan_carriers[wan_carrier_name].trusted) ): msg = ( "'ipv4_acl_in' must be set on WAN interfaces where 'wan_carrier' is set, unless the carrier is configured as 'trusted' " - f"under 'wan_carriers'. 'ipv4_acl_in' is missing on interface '{interface_name}'." + f"under 'wan_carriers'. 'ipv4_acl_in' is missing on interface '{l3_interface.name}'." ) raise AristaAvdError(msg) @@ -310,30 +302,28 @@ def _l3_interface_acls(self: AvdStructuredConfigUnderlay) -> dict[str, dict[str, """ l3_interface_acls = {} for l3_interface in self.shared_utils.l3_interfaces: - ipv4_acl_in = get(l3_interface, "ipv4_acl_in") - ipv4_acl_out = get(l3_interface, "ipv4_acl_out") + ipv4_acl_in = l3_interface.ipv4_acl_in + ipv4_acl_out = l3_interface.ipv4_acl_out if ipv4_acl_in is None and ipv4_acl_out is None: continue - interface_name = l3_interface["name"] - interface_ip: str | None = l3_interface.get("dhcp_ip") if (ip_address := l3_interface.get("ip_address")) == "dhcp" else ip_address + interface_ip = l3_interface.dhcp_ip if (ip_address := l3_interface.ip_address) == "dhcp" else ip_address if interface_ip is not None and "/" in interface_ip: interface_ip = get_ip_from_ip_prefix(interface_ip) - peer_ip: str | None = get(l3_interface, "peer_ip") if ipv4_acl_in is not None: - l3_interface_acls.setdefault(interface_name, {})["ipv4_acl_in"] = self.shared_utils.get_ipv4_acl( + l3_interface_acls.setdefault(l3_interface.name, {})["ipv4_acl_in"] = self.shared_utils.get_ipv4_acl( name=ipv4_acl_in, - interface_name=interface_name, + interface_name=l3_interface.name, interface_ip=interface_ip, - peer_ip=peer_ip, + peer_ip=l3_interface.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( + l3_interface_acls.setdefault(l3_interface.name, {})["ipv4_acl_out"] = self.shared_utils.get_ipv4_acl( name=ipv4_acl_out, - interface_name=interface_name, + interface_name=l3_interface.name, interface_ip=interface_ip, - peer_ip=peer_ip, + peer_ip=l3_interface.peer_ip, )._as_dict() return l3_interface_acls diff --git a/python-avd/pyavd/_schema/coerce_type.py b/python-avd/pyavd/_schema/coerce_type.py index eb4ae0fe763..29e59606e88 100644 --- a/python-avd/pyavd/_schema/coerce_type.py +++ b/python-avd/pyavd/_schema/coerce_type.py @@ -26,22 +26,22 @@ def nullifiy_class(cls: type[T_AvdBase]) -> type: """ class NullifiedCls(cls): - def _as_dict(self) -> None: + def _as_dict(self, *_args: Any, **_kwargs: Any) -> None: return None - def _as_list(self) -> None: + def _as_list(self, *_args: Any, **_kwargs: Any) -> None: return None def __repr__(self) -> str: return f"" - def _deepinherit(self, _other: T_AvdBase) -> None: + def _deepinherit(self, _other: T_AvdBase, *_args: Any, **_kwargs: Any) -> None: pass - def _inherit(self, _other: T_AvdBase) -> None: + def _inherit(self, _other: T_AvdBase, *_args: Any, **_kwargs: Any) -> None: pass - def _deepmerge(self, other: T_AvdBase) -> T_AvdBase: + def _deepmerge(self, other: T_AvdBase, *_args: Any, **_kwargs: Any) -> T_AvdBase: return other._deepcopy() return NullifiedCls diff --git a/python-avd/pyavd/_schema/models/avd_indexed_list.py b/python-avd/pyavd/_schema/models/avd_indexed_list.py index b841b498ade..576c024b1d9 100644 --- a/python-avd/pyavd/_schema/models/avd_indexed_list.py +++ b/python-avd/pyavd/_schema/models/avd_indexed_list.py @@ -99,9 +99,9 @@ def append(self, item: T_AvdModel) -> None: def extend(self, items: Iterable[T_AvdModel]) -> None: self._items.update({getattr(item, self._primary_key): item for item in items}) - def _as_list(self) -> list[dict]: + def _as_list(self, include_default_values: bool = False) -> list[dict]: """Returns a list with all the data from this model and any nested models.""" - return [item._as_dict() for item in self._items.values()] + return [item._as_dict(include_default_values=include_default_values) for item in self._items.values()] def _natural_sorted(self, ignore_case: bool = True) -> Self[T_AvdModel]: """Returns an iterator yielding the items natural sorted by primary key.""" diff --git a/python-avd/pyavd/_schema/models/avd_model.py b/python-avd/pyavd/_schema/models/avd_model.py index 3d5a43c24d2..4901dc0b753 100644 --- a/python-avd/pyavd/_schema/models/avd_model.py +++ b/python-avd/pyavd/_schema/models/avd_model.py @@ -145,19 +145,20 @@ def __bool__(self) -> bool: return any(getattr(self, key, None) for key in (self._fields or ()) if self._get_defined_attr(key) is not Undefined) def _as_dict(self, include_default_values: bool = False) -> dict: - """Returns a dict with all the data from this model and any nested models.""" + """ + Returns a dict with all the data from this model and any nested models. + + Filtered for nested None, {} and [] values. + """ as_dict = {} for field, field_info in self._fields.items() or (): if (value := self._get_defined_attr(field)) is Undefined: if not include_default_values: continue - if "default" not in field_info: - # The value is unset and there is no default value (only a default default like [] or None) - # These default defaults are not useful in the dict output so we skip. - continue - value = self._get_field_default_value(field) + if value in (None, [], {}): + continue if field == "_custom_data" and isinstance(value, dict) and value: as_dict.update(value) @@ -167,19 +168,20 @@ def _as_dict(self, include_default_values: bool = False) -> dict: key = self._field_to_key_map.get(field, field) if issubclass(field_info["type"], AvdModel) and isinstance(value, AvdModel): - as_dict[key] = value._as_dict() - continue - if issubclass(field_info["type"], AvdIndexedList) and isinstance(value, AvdIndexedList): - as_dict[key] = value._as_list() - continue - if field_info["type"] is list and isinstance(value, list): - if issubclass(field_info["items"], AvdModel): - as_dict[key] = [item._as_dict() for item in value if isinstance(item, AvdModel)] + value = value._as_dict(include_default_values=include_default_values) + if value == {}: # Keeping None values for nullified dicts. continue - if issubclass(field_info["items"], AvdIndexedList): - as_dict[key] = [item._as_list() for item in value if isinstance(item, AvdIndexedList)] + elif issubclass(field_info["type"], AvdIndexedList) and isinstance(value, AvdIndexedList): + value = value._as_list(include_default_values=include_default_values) + if value == []: # Keeping None values for nullified list. continue + elif field_info["type"] is list and isinstance(value, list): + if issubclass(field_info["items"], AvdModel): + value = [item._as_dict(include_default_values=include_default_values) for item in value if isinstance(item, AvdModel)] + elif issubclass(field_info["items"], AvdIndexedList): + value = [item._as_list(include_default_values=include_default_values) for item in value if isinstance(item, AvdIndexedList)] + as_dict[key] = value return as_dict