diff --git a/ansible_collections/arista/avd/docs/release-notes/5.x.x.md b/ansible_collections/arista/avd/docs/release-notes/5.x.x.md index ec10e36215d..7d42a4ba427 100644 --- a/ansible_collections/arista/avd/docs/release-notes/5.x.x.md +++ b/ansible_collections/arista/avd/docs/release-notes/5.x.x.md @@ -237,9 +237,9 @@ With AVD version 5.0.0 the default encapsulation used for EVPN iBGP peering betw See the [porting guide](../porting-guides/5.x.x.md#default-encapsulation-for-wan-evpn-ibgp-peerings-changed-to-path-selection) for details. -#### PREVIEW WAN HA Direct Link now uses Port-Channel by default +#### Preview WAN HA Direct Link now uses Port-Channel by default -TODO: Keep PREVIEW changes at the end as we warn users that PREVIEW mean changes +TODO: Keep Preview changes at the end as we warn users that Preview mean changes For WAN direct HA, AVD will now configure a port-channel by default. This feature was introduced recently in EOS and may not be supported on your version. diff --git a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-adapter-ptp-profile-does-not-exist.yml b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-adapter-ptp-profile-does-not-exist.yml new file mode 100644 index 00000000000..cc5df98a2b0 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-adapter-ptp-profile-does-not-exist.yml @@ -0,0 +1,33 @@ +--- +type: l3leaf + +ptp_profiles: + - profile: PROFILE-1 + +l3leaf: + defaults: + loopback_ipv4_pool: 10.254.1.0/27 + vtep_loopback_ipv4_pool: 10.254.11.0/27 + mlag_interfaces: ['Ethernet9', 'Ethernet10'] + mlag_peer_ipv4_pool: 10.254.1.64/27 + mlag_peer_l3_ipv4_pool: 10.254.1.96/27 + ptp: + enabled: true + nodes: + - name: failure-adapter-ptp-profile-does-not-exist + id: 1 + bgp_as: 65101 + mgmt_ip: 192.168.0.101/24 + +servers: + - name: INDIVIDUAL_1 + adapters: + - switches: [failure-adapter-ptp-profile-does-not-exist] + switch_ports: [Ethernet14] + ptp: + enabled: true + profile: THIS-PROFILE-DOES-NOT-EXIST + +expected_error_message: >- + PTP Profile 'THIS-PROFILE-DOES-NOT-EXIST' referenced under server[INDIVIDUAL_1].adapters[0] + does not exist in `ptp_profiles`. diff --git a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-network-port-ptp-profile-does-not-exist.yml b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-network-port-ptp-profile-does-not-exist.yml new file mode 100644 index 00000000000..a6173919272 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-network-port-ptp-profile-does-not-exist.yml @@ -0,0 +1,29 @@ +--- +type: l3leaf + +# Using default value of ptp_profiles + +l3leaf: + defaults: + loopback_ipv4_pool: 10.254.1.0/27 + vtep_loopback_ipv4_pool: 10.254.11.0/27 + mlag_interfaces: ['Ethernet9', 'Ethernet10'] + mlag_peer_ipv4_pool: 10.254.1.64/27 + mlag_peer_l3_ipv4_pool: 10.254.1.96/27 + ptp: + enabled: true + nodes: + - name: failure-network-port-ptp-profile-does-not-exist + id: 1 + bgp_as: 65101 + mgmt_ip: 192.168.0.101/24 + +network_ports: + - switches: [failure-network-port-ptp-profile-does-not-exist] + switch_ports: [Ethernet14, Ethernet15] + ptp: + enabled: true + profile: THIS-PROFILE-DOES-NOT-EXIST + +expected_error_message: >- + PTP Profile 'THIS-PROFILE-DOES-NOT-EXIST' referenced under network_ports[0] does not exist in `ptp_profiles`. diff --git a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-ptp-profile-does-not-exist.yml b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-ptp-profile-does-not-exist.yml new file mode 100644 index 00000000000..115e4c47a39 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/host_vars/failure-ptp-profile-does-not-exist.yml @@ -0,0 +1,31 @@ +--- +type: l3leaf + +ptp_profiles: + - profile: PROFILE-1 + +l3leaf: + defaults: + loopback_ipv4_pool: 10.254.1.0/27 + vtep_loopback_ipv4_pool: 10.254.11.0/27 + nodes: + - name: failure-ptp-profile-does-not-exist + id: 1 + bgp_as: 65101 + mgmt_ip: 192.168.0.101/24 + ptp: + enabled: true + # The profile is applied on uplink or l3edge + profile: THIS-PROFILE-DOES-NOT-EXIST + +l3_edge: + p2p_links: + - nodes: [failure-ptp-profile-does-not-exist, not-in-fabric] + interfaces: [Ethernet10, Ethernet11] + ip: [192.168.0.2/31, 192.168.0.3/31] + ptp: + enabled: true + +expected_error_message: >- + PTP Profile 'THIS-PROFILE-DOES-NOT-EXIST' referenced under `ptp.profile` node variables + does not exist in `ptp_profiles`. diff --git a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/hosts.yml b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/hosts.yml index 7d5bc888a78..33ff7214929 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/hosts.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_negative_unit_tests/inventory/hosts.yml @@ -89,18 +89,20 @@ all: duplicate-interfaces-underlay: duplicate-ip-address-uplink-switch-router-bgp: duplicate-tunnel-interface-internet-exit: + failure-adapter-ptp-profile-does-not-exist: + failure-connected-endpoint-port-profile-does-not-exist: + failure-connected-endpoint-parent-port-profile-does-not-exist: + failure-duplicate-evpn-vlan-bundle-name: failure-missing-evpn-vlan-bundle: failure-missing-evpn-vlan-bundle_svi: failure-missing-evpn-multicast-l3-with-pim: failure-missing-evpn-multicast-peg-rps: failure-missing-evpn-multicast-with-pim: - failure-duplicate-evpn-vlan-bundle-name: + failure-network-port-ptp-profile-does-not-exist: failure-no-local-path-group-in-default-policy: - failure-l3-interface-profile-does-not-exist: + failure-ptp-profile-does-not-exist: failure-svi-parent-profile-does-not-exist: failure-svi-grandparent-profile-does-not-exist: - failure-connected-endpoint-port-profile-does-not-exist: - failure-connected-endpoint-parent-port-profile-does-not-exist: ipv4-acl-in-missing-on-wan-interface: ipv4-acls: isis-system-id-format-missing-node-id: diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/custom-ptp-profile.cfg b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/custom-ptp-profile.cfg new file mode 100644 index 00000000000..43ffb1a9596 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/custom-ptp-profile.cfg @@ -0,0 +1,124 @@ +! +no enable password +no aaa root +! +vlan internal order ascending range 1006 1199 +! +transceiver qsfp default-mode 4x10G +! +service routing protocols model multi-agent +! +hostname custom-ptp-profile +! +ptp clock-identity 00:1C:73:1e:00:0c +ptp domain 127 +ptp mode boundary +ptp priority1 30 +ptp priority2 12 +ptp monitor threshold offset-from-master 250 +ptp monitor threshold mean-path-delay 1500 +ptp monitor sequence-id +ptp monitor threshold missing-message sync 3 sequence-ids +ptp monitor threshold missing-message follow-up 3 sequence-ids +ptp monitor threshold missing-message delay-resp 3 sequence-ids +ptp monitor threshold missing-message announce 3 sequence-ids +! +vlan 11 + name VLAN11 +! +vrf instance MGMT +! +vrf instance VRF1 +! +management api http-commands + protocol https + no shutdown + ! + vrf MGMT + no shutdown +! +interface Ethernet8 + description P2P_not-in-this-fabric_Ethernet8 + no shutdown + mtu 9214 + no switchport + ptp enable + ptp sync-message interval -7 + ptp announce interval 4 + ptp transport ipv4 + ptp announce timeout 3 + ptp delay-req interval -6 +! +interface Loopback0 + description ROUTER_ID + no shutdown + ip address 10.42.42.42/32/32 +! +interface Loopback1 + description VXLAN_TUNNEL_SOURCE + no shutdown + ip address 10.42.42.42/32/32 +! +interface Vlan11 + description VLAN11 + no shutdown + vrf VRF1 + ip address 172.16.11.1/24 +! +interface Vxlan1 + description custom-ptp-profile_VTEP + vxlan source-interface Loopback1 + vxlan udp-port 4789 + vxlan vlan 11 vni 10011 + vxlan vrf VRF1 vni 1 +! +ip routing +no ip routing vrf MGMT +ip routing vrf VRF1 +! +ip prefix-list PL-LOOPBACKS-EVPN-OVERLAY + seq 10 permit 10.10.10.0/24 eq 32 + seq 20 permit 10.10.10.0/24 eq 32 +! +route-map RM-CONN-2-BGP permit 10 + match ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +! +router bfd + multihop interval 300 min-rx 300 multiplier 3 +! +router bgp 65042 + router-id 10.42.42.42/32 + maximum-paths 4 ecmp 4 + update wait-install + no bgp default ipv4-unicast + neighbor EVPN-OVERLAY-PEERS peer group + neighbor EVPN-OVERLAY-PEERS update-source Loopback0 + neighbor EVPN-OVERLAY-PEERS bfd + neighbor EVPN-OVERLAY-PEERS ebgp-multihop 3 + neighbor EVPN-OVERLAY-PEERS send-community + neighbor EVPN-OVERLAY-PEERS maximum-routes 0 + neighbor IPv4-UNDERLAY-PEERS peer group + neighbor IPv4-UNDERLAY-PEERS send-community + neighbor IPv4-UNDERLAY-PEERS maximum-routes 12000 + redistribute connected route-map RM-CONN-2-BGP + ! + vlan 11 + rd 10.42.42.42/32:10011 + route-target both 10011:10011 + redistribute learned + ! + address-family evpn + neighbor EVPN-OVERLAY-PEERS activate + ! + address-family ipv4 + no neighbor EVPN-OVERLAY-PEERS activate + neighbor IPv4-UNDERLAY-PEERS activate + ! + vrf VRF1 + rd 10.42.42.42/32:1 + route-target import evpn 1:1 + route-target export evpn 1:1 + router-id 10.42.42.42/32 + redistribute connected +! +end diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/custom-ptp-profile.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/custom-ptp-profile.yml new file mode 100644 index 00000000000..a9166b70fea --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/custom-ptp-profile.yml @@ -0,0 +1,175 @@ +hostname: custom-ptp-profile +is_deployed: true +router_bgp: + as: '65042' + router_id: 10.42.42.42/32 + bgp: + default: + ipv4_unicast: false + maximum_paths: + paths: 4 + ecmp: 4 + updates: + wait_install: true + peer_groups: + - name: IPv4-UNDERLAY-PEERS + type: ipv4 + maximum_routes: 12000 + send_community: all + - name: EVPN-OVERLAY-PEERS + type: evpn + update_source: Loopback0 + bfd: true + send_community: all + maximum_routes: 0 + ebgp_multihop: 3 + address_family_ipv4: + peer_groups: + - name: IPv4-UNDERLAY-PEERS + activate: true + - name: EVPN-OVERLAY-PEERS + activate: false + redistribute_routes: + - source_protocol: connected + route_map: RM-CONN-2-BGP + address_family_evpn: + peer_groups: + - name: EVPN-OVERLAY-PEERS + activate: true + vrfs: + - name: VRF1 + rd: 10.42.42.42/32:1 + route_targets: + import: + - address_family: evpn + route_targets: + - '1:1' + export: + - address_family: evpn + route_targets: + - '1:1' + router_id: 10.42.42.42/32 + redistribute_routes: + - source_protocol: connected + vlans: + - id: 11 + tenant: PTP + rd: 10.42.42.42/32:10011 + route_targets: + both: + - 10011:10011 + redistribute_routes: + - learned +service_routing_protocols_model: multi-agent +ip_routing: true +vlan_internal_order: + allocation: ascending + range: + beginning: 1006 + ending: 1199 +aaa_root: + disabled: true +config_end: true +enable_password: + disabled: true +transceiver_qsfp_default_mode_4x10: true +vrfs: +- name: MGMT + ip_routing: false +- name: VRF1 + tenant: PTP + ip_routing: true +management_api_http: + enable_vrfs: + - name: MGMT + enable_https: true +ptp: + mode: boundary + clock_identity: 00:1C:73:1e:00:0c + priority1: 30 + priority2: 12 + domain: 127 + monitor: + enabled: true + threshold: + offset_from_master: 250 + mean_path_delay: 1500 + missing_message: + sequence_ids: + enabled: true + announce: 3 + delay_resp: 3 + follow_up: 3 + sync: 3 +loopback_interfaces: +- name: Loopback0 + description: ROUTER_ID + shutdown: false + ip_address: 10.42.42.42/32/32 +- name: Loopback1 + description: VXLAN_TUNNEL_SOURCE + shutdown: false + ip_address: 10.42.42.42/32/32 +prefix_lists: +- name: PL-LOOPBACKS-EVPN-OVERLAY + sequence_numbers: + - sequence: 10 + action: permit 10.10.10.0/24 eq 32 + - sequence: 20 + action: permit 10.10.10.0/24 eq 32 +route_maps: +- name: RM-CONN-2-BGP + sequence_numbers: + - sequence: 10 + type: permit + match: + - ip address prefix-list PL-LOOPBACKS-EVPN-OVERLAY +router_bfd: + multihop: + interval: 300 + min_rx: 300 + multiplier: 3 +ethernet_interfaces: +- name: Ethernet8 + peer: not-in-this-fabric + peer_interface: Ethernet8 + peer_type: other + switchport: + enabled: false + shutdown: false + mtu: 9214 + description: P2P_not-in-this-fabric_Ethernet8 + ptp: + announce: + interval: 4 + timeout: 3 + delay_req: -6 + sync_message: + interval: -7 + transport: ipv4 + enable: true +vlans: +- id: 11 + name: VLAN11 + tenant: PTP +ip_igmp_snooping: + globally_enabled: true +vlan_interfaces: +- name: Vlan11 + tenant: PTP + description: VLAN11 + shutdown: false + ip_address: 172.16.11.1/24 + vrf: VRF1 +vxlan_interface: + vxlan1: + description: custom-ptp-profile_VTEP + vxlan: + udp_port: 4789 + source_interface: Loopback1 + vlans: + - id: 11 + vni: 10011 + vrfs: + - name: VRF1 + vni: 1 diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/custom-ptp-profile.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/custom-ptp-profile.yml new file mode 100644 index 00000000000..fbd45d54bd4 --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/host_vars/custom-ptp-profile.yml @@ -0,0 +1,51 @@ +--- +type: l3leaf + +# Overriding the default profiles +ptp_profiles: + - profile: my-custom-ptp-profile + announce: + interval: 4 + timeout: 3 + delay_req: -6 + sync_message: + interval: -7 + transport: ipv4 + +l3_edge: + p2p_links: + - nodes: [ custom-ptp-profile, not-in-this-fabric ] + interfaces: [ Ethernet8, Ethernet8 ] + ptp: + enabled: true + include_in_underlay_protocol: false + +ptp_settings: + enabled: true + +tenants: + - name: PTP + mac_vrf_vni_base: 10000 + vrfs: + - name: VRF1 + vrf_vni: 1 + svis: + - id: 11 + name: VLAN11 + enabled: true + nodes: + - node: custom-ptp-profile + ip_address: 172.16.11.1/24 + +l3leaf: + defaults: + loopback_ipv4_pool: 10.10.10.0/24 + vtep_loopback_ipv4_pool: 10.10.10.0/24 + bgp_as: 65042 + nodes: + - name: custom-ptp-profile + loopback_ipv4_address: 10.42.42.42/32 + vtep_loopback_ipv4_address: 10.42.42.42/32 + id: 12 + ptp: + profile: my-custom-ptp-profile diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml index f06a89ef7f6..ea2d2f47b92 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/hosts.yml @@ -10,6 +10,7 @@ all: connected_endpoints: cvp-instance-ips-cvaas: cvp-instance-ips-onprem-token: + custom-ptp-profile: device.with.dots.in.hostname: evpn-vtep-with-default-vrf-not-evpn: duplicate-vrfs: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/connected-endpoints.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/connected-endpoints.md index 5c8e02f188a..1b7d59c292b 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/connected-endpoints.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/connected-endpoints.md @@ -42,7 +42,7 @@ | [        ptp](## ".[].adapters.[].ptp") | Dictionary | | | | The global PTP profile parameters will be applied to all connected endpoints where `ptp` is manually enabled.
`ptp role master` is set to ensure control over the PTP topology.
| | [          enabled](## ".[].adapters.[].ptp.enabled") | Boolean | | `False` | | | | [          endpoint_role](## ".[].adapters.[].ptp.endpoint_role") | String | | `follower` | Valid Values:
- bmca
- default
- follower | | - | [          profile](## ".[].adapters.[].ptp.profile") | String | | `aes67-r16-2016` | Valid Values:
- aes67
- aes67-r16-2016
- smpte2059-2 | | + | [          profile](## ".[].adapters.[].ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [        sflow](## ".[].adapters.[].sflow") | Boolean | | | | Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` setting. | | [        flow_tracking](## ".[].adapters.[].flow_tracking") | Dictionary | | | | Configures flow-tracking on the interface. Overrides `fabric_flow_tracking.endpoints` setting. | | [          enabled](## ".[].adapters.[].flow_tracking.enabled") | Boolean | | | | | @@ -274,7 +274,12 @@ ptp: enabled: endpoint_role: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + profile: # Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` setting. sflow: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/network-ports.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/network-ports.md index f4a3090cbd2..cb0a09ce1a5 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/network-ports.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/network-ports.md @@ -37,7 +37,7 @@ | [    ptp](## "network_ports.[].ptp") | Dictionary | | | | The global PTP profile parameters will be applied to all connected endpoints where `ptp` is manually enabled.
`ptp role master` is set to ensure control over the PTP topology.
| | [      enabled](## "network_ports.[].ptp.enabled") | Boolean | | `False` | | | | [      endpoint_role](## "network_ports.[].ptp.endpoint_role") | String | | `follower` | Valid Values:
- bmca
- default
- follower | | - | [      profile](## "network_ports.[].ptp.profile") | String | | `aes67-r16-2016` | Valid Values:
- aes67
- aes67-r16-2016
- smpte2059-2 | | + | [      profile](## "network_ports.[].ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [    sflow](## "network_ports.[].sflow") | Boolean | | | | Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` setting. | | [    flow_tracking](## "network_ports.[].flow_tracking") | Dictionary | | | | Configures flow-tracking on the interface. Overrides `fabric_flow_tracking.endpoints` setting. | | [      enabled](## "network_ports.[].flow_tracking.enabled") | Boolean | | | | | @@ -248,7 +248,12 @@ ptp: enabled: endpoint_role: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + profile: # Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` setting. sflow: 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 8f532d1fbb3..a49156bf73b 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 @@ -11,7 +11,7 @@ | [  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` | Valid Values:
- aes67
- smpte2059-2
- aes67-r16-2016 | | + | [      profile](## ".defaults.ptp.profile") | String | | `aes67-r16-2016` | | 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.
| @@ -52,7 +52,7 @@ | [        - 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` | Valid Values:
- aes67
- smpte2059-2
- aes67-r16-2016 | | + | [            profile](## ".node_groups.[].nodes.[].ptp.profile") | String | | `aes67-r16-2016` | | 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.
| @@ -89,7 +89,7 @@ | [                  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` | Valid Values:
- aes67
- smpte2059-2
- aes67-r16-2016 | | + | [        profile](## ".node_groups.[].ptp.profile") | String | | `aes67-r16-2016` | | 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.
| @@ -128,7 +128,7 @@ | [    - 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` | Valid Values:
- aes67
- smpte2059-2
- aes67-r16-2016 | | + | [        profile](## ".nodes.[].ptp.profile") | String | | `aes67-r16-2016` | | 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.
| @@ -173,7 +173,12 @@ defaults: ptp: enabled: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + 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: @@ -243,7 +248,12 @@ - name: ptp: enabled: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + 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: @@ -300,7 +310,12 @@ sync: ptp: enabled: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + 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: @@ -363,7 +378,12 @@ - name: ptp: enabled: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + 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: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/port-profiles.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/port-profiles.md index 7322c74c9a5..a1d4496d19b 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/port-profiles.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/port-profiles.md @@ -33,7 +33,7 @@ | [    ptp](## "port_profiles.[].ptp") | Dictionary | | | | The global PTP profile parameters will be applied to all connected endpoints where `ptp` is manually enabled.
`ptp role master` is set to ensure control over the PTP topology.
| | [      enabled](## "port_profiles.[].ptp.enabled") | Boolean | | `False` | | | | [      endpoint_role](## "port_profiles.[].ptp.endpoint_role") | String | | `follower` | Valid Values:
- bmca
- default
- follower | | - | [      profile](## "port_profiles.[].ptp.profile") | String | | `aes67-r16-2016` | Valid Values:
- aes67
- aes67-r16-2016
- smpte2059-2 | | + | [      profile](## "port_profiles.[].ptp.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [    sflow](## "port_profiles.[].sflow") | Boolean | | | | Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` setting. | | [    flow_tracking](## "port_profiles.[].flow_tracking") | Dictionary | | | | Configures flow-tracking on the interface. Overrides `fabric_flow_tracking.endpoints` setting. | | [      enabled](## "port_profiles.[].flow_tracking.enabled") | Boolean | | | | | @@ -230,7 +230,12 @@ ptp: enabled: endpoint_role: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + profile: # Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` setting. sflow: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/ptp_settings.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/ptp_settings.md index e4b2e42c958..c7b416afa5d 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/ptp_settings.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/ptp_settings.md @@ -9,7 +9,7 @@ | -------- | ---- | -------- | ------- | ------------------ | ----------- | | [ptp](## "ptp") removed | Dictionary | | | | This key was removed. Support was removed in AVD version v5.0.0. Use ptp_settings instead. | | [ptp_profiles](## "ptp_profiles") | List, items: Dictionary | | See (+) on YAML tab | | | - | [  - profile](## "ptp_profiles.[].profile") | String | | | | PTP profile. | + | [  - profile](## "ptp_profiles.[].profile") | String | Required, Unique | | | PTP profile. | | [    announce](## "ptp_profiles.[].announce") | Dictionary | | | | PTP announce interval. | | [      interval](## "ptp_profiles.[].announce.interval") | Integer | | | Min: -7
Max: 4 | | | [      timeout](## "ptp_profiles.[].announce.timeout") | Integer | | | Min: 2
Max: 255 | | @@ -19,7 +19,7 @@ | [    transport](## "ptp_profiles.[].transport") | String | | | Valid Values:
- ipv4 | | | [ptp_settings](## "ptp_settings") | Dictionary | | | | Common PTP settings. | | [  enabled](## "ptp_settings.enabled") | Boolean | | | | | - | [  profile](## "ptp_settings.profile") | String | | `aes67-r16-2016` | Valid Values:
- aes67
- smpte2059-2
- aes67-r16-2016 | | + | [  profile](## "ptp_settings.profile") | String | | `aes67-r16-2016` | | Default available profiles are:
- "aes67"
- "aes67-r16-2016"
- "smpte2059-2" | | [  domain](## "ptp_settings.domain") | Integer | | | Min: 0
Max: 255 | | | [  auto_clock_identity](## "ptp_settings.auto_clock_identity") | Boolean | | `True` | | | @@ -29,7 +29,7 @@ ptp_profiles: # (1)! # PTP profile. - - profile: + - profile: # PTP announce interval. announce: @@ -45,7 +45,12 @@ # Common PTP settings. ptp_settings: enabled: - profile: + + # Default available profiles are: + # - "aes67" + # - "aes67-r16-2016" + # - "smpte2059-2" + profile: domain: auto_clock_identity: ``` diff --git a/development/compare.py b/development/compare.py index 3198df8aea1..df1c7edaccc 100755 --- a/development/compare.py +++ b/development/compare.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. 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 1055b13ada9..8aea7157cc8 100644 --- a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml @@ -3493,6 +3493,7 @@ keys: documentation_options: table: ptp_settings type: list + primary_key: profile items: type: dict keys: @@ -3536,27 +3537,27 @@ keys: valid_values: - ipv4 default: - - announce: + - profile: aes67-r16-2016 + announce: interval: 0 timeout: 3 delay_req: -3 - profile: aes67-r16-2016 sync_message: interval: -3 transport: ipv4 - - announce: + - profile: smpte2059-2 + announce: interval: -2 timeout: 3 delay_req: -4 - profile: smpte2059-2 sync_message: interval: -4 transport: ipv4 - - announce: + - profile: aes67 + announce: interval: 2 timeout: 3 delay_req: 0 - profile: aes67 sync_message: interval: 0 transport: ipv4 @@ -3570,11 +3571,9 @@ keys: type: bool profile: type: str - valid_values: - - aes67 - - smpte2059-2 - - aes67-r16-2016 default: aes67-r16-2016 + description: "Default available profiles are:\n - \"aes67\"\n - \"aes67-r16-2016\"\n + \ - \"smpte2059-2\"" domain: type: int $ref: eos_cli_config_gen#/keys/ptp/keys/domain @@ -5199,10 +5198,8 @@ $defs: profile: type: str default: aes67-r16-2016 - valid_values: - - aes67 - - aes67-r16-2016 - - smpte2059-2 + description: "Default available profiles are:\n - \"aes67\"\n - \"aes67-r16-2016\"\n + \ - \"smpte2059-2\"" sflow: type: bool description: Configures sFlow on the interface. Overrides `fabric_sflow.endpoints` @@ -8858,10 +8855,8 @@ $defs: default: false profile: type: str - valid_values: - - aes67 - - smpte2059-2 - - aes67-r16-2016 + 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 diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml index b799a65d35d..f540f8f504d 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/defs_adapter_config.schema.yml @@ -145,10 +145,11 @@ $defs: profile: type: str default: "aes67-r16-2016" - valid_values: - - "aes67" - - "aes67-r16-2016" - - "smpte2059-2" + description: |- + Default available profiles are: + - "aes67" + - "aes67-r16-2016" + - "smpte2059-2" sflow: type: bool description: |- 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 dfad31b8b6c..95bf96d54ff 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 @@ -1067,10 +1067,11 @@ $defs: default: false profile: type: str - valid_values: - - "aes67" - - "smpte2059-2" - - "aes67-r16-2016" + description: |- + Default available profiles are: + - "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. diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_profiles.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_profiles.schema.yml index 2a930f1aaa4..e3efafb8298 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_profiles.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_profiles.schema.yml @@ -10,6 +10,7 @@ keys: documentation_options: table: ptp_settings type: list + primary_key: profile items: type: dict keys: @@ -52,27 +53,27 @@ keys: type: str valid_values: ["ipv4"] default: - - announce: + - profile: aes67-r16-2016 + announce: interval: 0 timeout: 3 delay_req: -3 - profile: aes67-r16-2016 sync_message: interval: -3 transport: ipv4 - - announce: + - profile: smpte2059-2 + announce: interval: -2 timeout: 3 delay_req: -4 - profile: smpte2059-2 sync_message: interval: -4 transport: ipv4 - - announce: + - profile: aes67 + announce: interval: 2 timeout: 3 delay_req: 0 - profile: aes67 sync_message: interval: 0 transport: ipv4 diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_settings.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_settings.schema.yml index 741c2e303da..9e35c57aa4c 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_settings.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/ptp_settings.schema.yml @@ -16,11 +16,12 @@ keys: type: bool profile: type: str - valid_values: - - "aes67" - - "smpte2059-2" - - "aes67-r16-2016" default: "aes67-r16-2016" + description: |- + Default available profiles are: + - "aes67" + - "aes67-r16-2016" + - "smpte2059-2" domain: type: int $ref: "eos_cli_config_gen#/keys/ptp/keys/domain" diff --git a/python-avd/pyavd/_eos_designs/shared_utils/ptp.py b/python-avd/pyavd/_eos_designs/shared_utils/ptp.py index 4c5f1c15af1..77fde6da48b 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/ptp.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/ptp.py @@ -11,39 +11,6 @@ if TYPE_CHECKING: from . import SharedUtils -DEFAULT_PTP_PROFILES = [ - { - "profile": "aes67-r16-2016", - "announce": { - "interval": 0, - "timeout": 3, - }, - "delay_req": -3, - "sync_message": {"interval": -3}, - "transport": "ipv4", - }, - { - "profile": "smpte2059-2", - "announce": { - "interval": -2, - "timeout": 3, - }, - "delay_req": -4, - "sync_message": {"interval": -4}, - "transport": "ipv4", - }, - { - "profile": "aes67", - "announce": { - "interval": 2, - "timeout": 3, - }, - "delay_req": 0, - "sync_message": {"interval": 0}, - "transport": "ipv4", - }, -] - class PtpMixin: """ @@ -65,13 +32,15 @@ def ptp_mlag(self: SharedUtils) -> bool: @cached_property def ptp_profile_name(self: SharedUtils) -> str: default_ptp_profile = get(self.hostvars, "ptp_settings.profile", default="aes67-r16-2016") - return get(self.switch_data_combined, "ptp.profile", default_ptp_profile) + return get(self.switch_data_combined, "ptp.profile", default=default_ptp_profile) @cached_property def ptp_profile(self: SharedUtils) -> dict: - return get_item(self.ptp_profiles, "profile", self.ptp_profile_name, default={}) + msg = f"PTP Profile '{self.ptp_profile_name}' referenced under `ptp.profile` node variables does not exist in `ptp_profiles`." + return get_item(self.ptp_profiles, "profile", self.ptp_profile_name, required=True, custom_error_msg=msg) @cached_property def ptp_profiles(self: SharedUtils) -> list: """Return ptp_profiles.""" - return get(self.hostvars, "ptp_profiles", default=DEFAULT_PTP_PROFILES) + default_ptp_profiles = self.schema.get_default_value(["ptp_profiles"]) + return get(self.hostvars, "ptp_profiles", default=default_ptp_profiles) diff --git a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py index 7f4486c8f09..12ee6b9bc66 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/ethernet_interfaces.py @@ -56,7 +56,8 @@ def ethernet_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list | N }, network_port, ) - ethernet_interface = self._get_ethernet_interface_cfg(tmp_network_port, 0, connected_endpoint, index) + context = f"network_ports[{index}]" + ethernet_interface = self._get_ethernet_interface_cfg(tmp_network_port, 0, connected_endpoint, context) replace_or_append_item(ethernet_interfaces, "name", ethernet_interface) for index, connected_endpoint in enumerate(self._filtered_connected_endpoints): @@ -65,7 +66,8 @@ def ethernet_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list | N if node_name != self.shared_utils.hostname: continue - ethernet_interface = self._get_ethernet_interface_cfg(adapter, node_index, connected_endpoint, index) + context = f"{connected_endpoint['type']}[{connected_endpoint['name']}].adapters[{index}]" + ethernet_interface = self._get_ethernet_interface_cfg(adapter, node_index, connected_endpoint, context) append_if_not_duplicate( list_of_dicts=non_overwritable_ethernet_interfaces, primary_key="name", @@ -81,7 +83,9 @@ def ethernet_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list | N return None - def _update_ethernet_interface_cfg(self: AvdStructuredConfigConnectedEndpoints, adapter: dict, ethernet_interface: dict, connected_endpoint: dict) -> dict: + def _update_ethernet_interface_cfg( + self: AvdStructuredConfigConnectedEndpoints, adapter: dict | ChainMap, ethernet_interface: dict, connected_endpoint: dict, context: str + ) -> dict: ethernet_interface.update( { "mtu": adapter.get("mtu") if self.shared_utils.platform_settings_feature_support_per_interface_mtu else None, @@ -105,7 +109,7 @@ def _update_ethernet_interface_cfg(self: AvdStructuredConfigConnectedEndpoints, "storm_control": self._get_adapter_storm_control(adapter), "dot1x": adapter.get("dot1x"), "poe": self._get_adapter_poe(adapter), - "ptp": self._get_adapter_ptp(adapter), + "ptp": self._get_adapter_ptp(adapter, context), "service_profile": adapter.get("qos_profile"), "sflow": self._get_adapter_sflow(adapter), "flow_tracker": self._get_adapter_flow_tracking(adapter), @@ -114,7 +118,9 @@ def _update_ethernet_interface_cfg(self: AvdStructuredConfigConnectedEndpoints, ) return strip_null_from_data(ethernet_interface, strip_values_tuple=(None, "", {})) - def _get_ethernet_interface_cfg(self: AvdStructuredConfigConnectedEndpoints, adapter: dict, node_index: int, connected_endpoint: dict, index: int) -> dict: + def _get_ethernet_interface_cfg( + self: AvdStructuredConfigConnectedEndpoints, adapter: dict, node_index: int, connected_endpoint: dict, context: str + ) -> dict: """Return structured_config for one ethernet_interface.""" peer = connected_endpoint["name"] endpoint_ports: list = default( @@ -192,11 +198,9 @@ def _get_ethernet_interface_cfg(self: AvdStructuredConfigConnectedEndpoints, ada msg, ) - profile = self.shared_utils.get_merged_port_profile( - profile_name, context=f"{connected_endpoint['type']}[{connected_endpoint['name']}].adapters[{index}].port_channel.lacp_fallback.individual" - ) + profile = self.shared_utils.get_merged_port_profile(profile_name, context=f"{context}.port_channel.lacp_fallback.individual") - ethernet_interface = self._update_ethernet_interface_cfg(profile, ethernet_interface, connected_endpoint) + ethernet_interface = self._update_ethernet_interface_cfg(profile, ethernet_interface, connected_endpoint, context) if port_channel_mode != "on" and get(adapter, "port_channel.lacp_timer") is not None: ethernet_interface["lacp_timer"] = { @@ -206,7 +210,7 @@ def _get_ethernet_interface_cfg(self: AvdStructuredConfigConnectedEndpoints, ada # NOT a port-channel member else: - ethernet_interface = self._update_ethernet_interface_cfg(adapter, ethernet_interface, connected_endpoint) + ethernet_interface = self._update_ethernet_interface_cfg(adapter, ethernet_interface, connected_endpoint, context) ethernet_interface["evpn_ethernet_segment"] = self._get_adapter_evpn_ethernet_segment_cfg( adapter, short_esi, diff --git a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py index b0d2a575171..5c90afaf5b4 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/port_channel_interfaces.py @@ -35,7 +35,7 @@ def port_channel_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list - Raise a duplicate error for any other duplicate port-channel interface """ port_channel_interfaces = [] - for connected_endpoint in self._filtered_connected_endpoints: + for index, connected_endpoint in enumerate(self._filtered_connected_endpoints): for adapter in connected_endpoint["adapters"]: if get(adapter, "port_channel.mode") is None: continue @@ -44,7 +44,8 @@ def port_channel_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list channel_group_id = get(adapter, "port_channel.channel_id", default=default_channel_group_id) port_channel_interface_name = f"Port-Channel{channel_group_id}" - port_channel_config = self._get_port_channel_interface_cfg(adapter, port_channel_interface_name, channel_group_id, connected_endpoint) + context = f"{connected_endpoint['type']}[{connected_endpoint['name']}].adapters[{index}]" + port_channel_config = self._get_port_channel_interface_cfg(adapter, port_channel_interface_name, channel_group_id, connected_endpoint, context) append_if_not_duplicate( list_of_dicts=port_channel_interfaces, primary_key="name", @@ -75,7 +76,7 @@ def port_channel_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list context_keys=["name"], ) - for network_port in self._filtered_network_ports: + for index, network_port in enumerate(self._filtered_network_ports): if get(network_port, "port_channel.mode") is None: continue @@ -100,7 +101,10 @@ def port_channel_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list channel_group_id = get(tmp_network_port, "port_channel.channel_id", default=default_channel_group_id) port_channel_interface_name = f"Port-Channel{channel_group_id}" - port_channel_config = self._get_port_channel_interface_cfg(tmp_network_port, port_channel_interface_name, channel_group_id, connected_endpoint) + context = f"network_ports[{index}]" + port_channel_config = self._get_port_channel_interface_cfg( + tmp_network_port, port_channel_interface_name, channel_group_id, connected_endpoint, context + ) append_if_not_duplicate( list_of_dicts=port_channel_interfaces, primary_key="name", @@ -116,10 +120,11 @@ def port_channel_interfaces(self: AvdStructuredConfigConnectedEndpoints) -> list def _get_port_channel_interface_cfg( self: AvdStructuredConfigConnectedEndpoints, - adapter: dict, + adapter: dict | ChainMap, port_channel_interface_name: str, channel_group_id: int, connected_endpoint: dict, + context: str, ) -> dict: """Return structured_config for one port_channel_interface.""" peer = connected_endpoint["name"] @@ -154,7 +159,7 @@ def _get_port_channel_interface_cfg( "mtu": adapter.get("mtu") if self.shared_utils.platform_settings_feature_support_per_interface_mtu else None, "service_profile": adapter.get("qos_profile"), "link_tracking_groups": self._get_adapter_link_tracking_groups(adapter), - "ptp": self._get_adapter_ptp(adapter), + "ptp": self._get_adapter_ptp(adapter, context), "sflow": self._get_adapter_sflow(adapter), "flow_tracker": self._get_adapter_flow_tracking(adapter), "validate_state": None if adapter.get("validate_state", True) else False, diff --git a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/utils.py b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/utils.py index 22654b41fb8..994caa94af7 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/connected_endpoints/utils.py @@ -197,7 +197,7 @@ def _get_adapter_link_tracking_groups(self: AvdStructuredConfigConnectedEndpoint }, ] - def _get_adapter_ptp(self: AvdStructuredConfigConnectedEndpoints, adapter: dict) -> dict | None: + def _get_adapter_ptp(self: AvdStructuredConfigConnectedEndpoints, adapter: dict, context: str) -> dict | None: """Return ptp for one adapter.""" if get(adapter, "ptp.enabled") is not True: return None @@ -206,7 +206,8 @@ def _get_adapter_ptp(self: AvdStructuredConfigConnectedEndpoints, adapter: dict) # Apply PTP profile config if (ptp_profile_name := get(adapter, "ptp.profile", default=self.shared_utils.ptp_profile_name)) is not None: - ptp_config.update(get_item(self.shared_utils.ptp_profiles, "profile", ptp_profile_name, default={})) + msg = f"PTP Profile '{ptp_profile_name}' referenced under {context} does not exist in `ptp_profiles`." + ptp_config.update(get_item(self.shared_utils.ptp_profiles, "profile", ptp_profile_name, required=True, custom_error_msg=msg)) ptp_config["enable"] = True