From 6b27b2c5ae679751cd8e4197618fd766c2db2bf8 Mon Sep 17 00:00:00 2001 From: Claus Holbech Date: Tue, 17 Dec 2024 07:19:45 +0100 Subject: [PATCH 1/7] Refactor(plugins): Improve schema models (#4795) --- .../plugins/plugin_utils/pyavd_wrappers.py | 5 + .../roles/eos_designs/docs/input-variables.md | 2 +- .../shared_utils/link_tracking_groups.py | 4 +- .../pyavd/_eos_designs/shared_utils/misc.py | 34 ++-- .../structured_config/base/__init__.py | 2 +- .../ethernet_interfaces.py | 2 +- .../port_channel_interfaces.py | 2 +- .../__init__.py | 6 +- .../structured_config/mlag/__init__.py | 10 +- .../network_services/ethernet_interfaces.py | 2 +- .../network_services/router_bgp.py | 6 +- .../network_services/struct_cfgs.py | 2 +- .../network_services/vlan_interfaces.py | 2 +- .../structured_config/overlay/router_bgp.py | 2 +- .../structured_config/underlay/router_bgp.py | 2 +- .../structured_config/underlay/utils.py | 4 +- python-avd/pyavd/_schema/coerce_type.py | 58 +------ python-avd/pyavd/_schema/models/avd_base.py | 33 +++- .../pyavd/_schema/models/avd_indexed_list.py | 95 +++++++++--- python-avd/pyavd/_schema/models/avd_list.py | 65 ++++++-- python-avd/pyavd/_schema/models/avd_model.py | 145 +++++++++++++----- .../test_eos_designs_class.py | 4 +- 22 files changed, 305 insertions(+), 182 deletions(-) diff --git a/ansible_collections/arista/avd/plugins/plugin_utils/pyavd_wrappers.py b/ansible_collections/arista/avd/plugins/plugin_utils/pyavd_wrappers.py index 58f98a98aad..63ae2dcf886 100644 --- a/ansible_collections/arista/avd/plugins/plugin_utils/pyavd_wrappers.py +++ b/ansible_collections/arista/avd/plugins/plugin_utils/pyavd_wrappers.py @@ -32,6 +32,11 @@ def __init__(self, exception: Exception) -> None: def __call__(self, *_args: Any, **_kwargs: Any) -> NoReturn: raise self.exception + def __getattr__(self, name: str) -> Any: + if not name.startswith("__"): + raise self.exception + return self.__getattribute__(name) + def wrap_plugin(plugin_type: Literal["filter", "test"], name: str) -> Callable: plugin_map = { diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md b/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md index e2b0f2da452..f2b658a8152 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/input-variables.md @@ -1480,7 +1480,7 @@ This feature currently provides the following configurations based on the given `max_uplink_switches` and `max_parallel_uplinks` to ensure consistent IP addressing. ??? example "`cv_topology` example" - To use this feature set `default_interfaces` according to the intended design (see [default_intefaces](#default-interface-settings) for details) and set `use_cv_topology` to `true`. + To use this feature set `default_interfaces` according to the intended design (see [default_interfaces](#default-interface-settings) for details) and set `use_cv_topology` to `true`. Provide a full topology under `cv_topology` like this example: ```yaml 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 0782dd521af..8849a5e90fc 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 +from pyavd._utils import default, strip_empties_from_list if TYPE_CHECKING: from . import SharedUtils @@ -33,6 +33,6 @@ def link_tracking_groups(self: SharedUtils) -> list | None: else: link_tracking_groups.append({"name": "LT_GROUP1", "recovery_delay": default_recovery_delay}) - return link_tracking_groups + return strip_empties_from_list(link_tracking_groups) return None diff --git a/python-avd/pyavd/_eos_designs/shared_utils/misc.py b/python-avd/pyavd/_eos_designs/shared_utils/misc.py index bf7a7b88ac7..7cf27721407 100644 --- a/python-avd/pyavd/_eos_designs/shared_utils/misc.py +++ b/python-avd/pyavd/_eos_designs/shared_utils/misc.py @@ -188,6 +188,7 @@ def get_ipv4_acl( "interface_ip": interface_ip, "peer_ip": peer_ip, } + changed = False for index, entry in enumerate(ipv4_acl.entries): if entry._get("remark"): continue @@ -202,13 +203,15 @@ def get_ipv4_acl( entry.source = self._get_ipv4_acl_field_with_substitution(entry.source, ip_replacements, f"{err_context}.source", interface_name) entry.destination = self._get_ipv4_acl_field_with_substitution(entry.destination, ip_replacements, f"{err_context}.destination", interface_name) + if entry.source != org_ipv4_acl.entries[index].source or entry.destination != org_ipv4_acl.entries[index].destination: + changed = True - if ipv4_acl != org_ipv4_acl: + if changed: ipv4_acl.name += f"_{self.sanitize_interface_name(interface_name)}" return ipv4_acl @staticmethod - def _get_ipv4_acl_field_with_substitution(field_value: str, replacements: dict[str, str], field_context: str, interface_name: str) -> str: + def _get_ipv4_acl_field_with_substitution(field_value: str, replacements: dict[str, str | None], field_context: str, interface_name: str) -> str: """ Checks one field if the value can be substituted. @@ -218,18 +221,15 @@ def _get_ipv4_acl_field_with_substitution(field_value: str, replacements: dict[s If a replacement is done, but the value is None, an error will be raised. """ - for key, value in replacements.items(): - if field_value != key: - continue - - if value is None: - msg = ( - f"Unable to perform substitution of the value '{key}' defined under '{field_context}', " - f"since no substitution value was found for interface '{interface_name}'. " - "Make sure to set the appropriate fields on the interface." - ) - raise AristaAvdError(msg) - - return value - - return field_value + if field_value not in replacements: + return field_value + + if (replacement_value := replacements[field_value]) is None: + msg = ( + f"Unable to perform substitution of the value '{field_value}' defined under '{field_context}', " + f"since no substitution value was found for interface '{interface_name}'. " + "Make sure to set the appropriate fields on the interface." + ) + raise AristaAvdError(msg) + + return replacement_value 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 da090f15922..296cb9787d3 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/base/__init__.py @@ -780,6 +780,6 @@ def route_maps(self) -> list | None: @cached_property def struct_cfgs(self) -> list | None: if self.shared_utils.platform_settings.structured_config: - return [self.shared_utils.platform_settings.structured_config._as_dict(strip_values=())] + return [self.shared_utils.platform_settings.structured_config._as_dict()] return None 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 4b1bc4f2e2d..c8161251a87 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 @@ -182,7 +182,7 @@ def _get_ethernet_interface_cfg( "dot1x": adapter.dot1x._as_dict() or None, "poe": self._get_adapter_poe(adapter), "eos_cli": adapter.raw_eos_cli, - "struct_cfg": adapter.structured_config._as_dict(strip_values=()), + "struct_cfg": adapter.structured_config._as_dict(), } # Port-channel member 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 0ceeff3d2b1..3a8160e2a97 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 @@ -158,7 +158,7 @@ def _get_port_channel_interface_cfg( "validate_state": None if (adapter.validate_state if adapter.validate_state is not None else True) else False, "validate_lldp": None if (adapter.validate_lldp if adapter.validate_lldp is not None else True) else False, "eos_cli": adapter.port_channel.raw_eos_cli, - "struct_cfg": adapter.port_channel.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": adapter.port_channel.structured_config._as_dict() or None, } if adapter.port_channel.subinterfaces: 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 6dc0d62b595..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 := self.shared_utils.node_config.structured_config._as_dict(strip_values=()): + if struct_cfg := self.shared_utils.node_config.structured_config._as_dict(): return [struct_cfg] return [] @@ -114,9 +114,7 @@ def _router_bgp_vlans(self) -> list: ] def _custom_structured_configurations(self) -> list[dict]: - return [ - custom_structured_configuration.value._as_dict(strip_values=()) for custom_structured_configuration in self.inputs._custom_structured_configurations - ] + return [custom_structured_configuration.value._as_dict() for custom_structured_configuration in self.inputs._custom_structured_configurations] def render(self) -> list[dict]: """ 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 90abdddb325..ec50862acff 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/mlag/__init__.py @@ -70,7 +70,7 @@ def vlan_interfaces(self) -> list | None: ), "shutdown": False, "no_autostate": True, - "struct_cfg": self.shared_utils.node_config.mlag_peer_vlan_structured_config._as_dict(strip_values=()) or None, + "struct_cfg": self.shared_utils.node_config.mlag_peer_vlan_structured_config._as_dict() or None, "mtu": self.shared_utils.p2p_uplinks_mtu, } @@ -82,7 +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": self.shared_utils.node_config.mlag_peer_l3_vlan_structured_config._as_dict(strip_values=()) or None} + 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( { @@ -121,7 +121,7 @@ def vlan_interfaces(self) -> list | None: main_vlan_interface.update(l3_cfg) # Applying structured config again in the case it is set on both l3vlan and main vlan if self.shared_utils.node_config.mlag_peer_vlan_structured_config is not None: - main_vlan_interface["struct_cfg"] = self.shared_utils.node_config.mlag_peer_vlan_structured_config._as_dict(strip_values=()) + main_vlan_interface["struct_cfg"] = self.shared_utils.node_config.mlag_peer_vlan_structured_config._as_dict() return [strip_empties_from_dict(main_vlan_interface)] @@ -169,7 +169,7 @@ def port_channel_interfaces(self) -> list: }, "shutdown": False, "service_profile": self.inputs.p2p_uplinks_qos_profile, - "struct_cfg": self.shared_utils.node_config.mlag_port_channel_structured_config._as_dict(strip_values=()) or None, + "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), } @@ -350,7 +350,7 @@ def _router_bgp_mlag_peer_group(self) -> dict: "bfd": self.inputs.bgp_peer_groups.ipv4_underlay_peers.bfd or None, "maximum_routes": 12000, "send_community": "all", - "struct_cfg": self.inputs.bgp_peer_groups.mlag_ipv4_underlay_peer.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": self.inputs.bgp_peer_groups.mlag_ipv4_underlay_peer.structured_config._as_dict() or None, } if self.shared_utils.node_config.mlag_ibgp_origin_incomplete: peer_group["route_map_in"] = "RM-MLAG-PEER-IN" 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 49cab1e01c6..6fffe42a324 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 @@ -70,7 +70,7 @@ def ethernet_interfaces(self: AvdStructuredConfigNetworkServices) -> list | None "shutdown": not l3_interface.enabled, "description": interface_description, "eos_cli": l3_interface.raw_eos_cli, - "struct_cfg": l3_interface.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": l3_interface.structured_config._as_dict() or None, "flow_tracker": self.shared_utils.get_flow_tracker(l3_interface.flow_tracking), } diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py index 8d26e868441..907d2eac546 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/router_bgp.py @@ -153,7 +153,7 @@ def _router_bgp_vrfs(self: AvdStructuredConfigNetworkServices) -> dict: bgp_vrf = strip_empties_from_dict( { "eos_cli": vrf.bgp.raw_eos_cli, - "struct_cfg": vrf.bgp.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": vrf.bgp.structured_config._as_dict() or None, } ) @@ -493,7 +493,7 @@ def _router_bgp_vlans_vlan( "route_targets": {"both": [vlan_rt]}, "redistribute_routes": ["learned"], "eos_cli": vlan.bgp.raw_eos_cli, - "struct_cfg": vlan.bgp.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": vlan.bgp.structured_config._as_dict() or None, } if self.shared_utils.node_config.evpn_gateway.evpn_l2.enabled and default( vlan.evpn_l2_multi_domain, vrf.evpn_l2_multi_domain, tenant.evpn_l2_multi_domain @@ -816,7 +816,7 @@ def _router_bgp_mlag_peer_group(self: AvdStructuredConfigNetworkServices) -> dic "password": self.inputs.bgp_peer_groups.mlag_ipv4_underlay_peer.password, "maximum_routes": 12000, "send_community": "all", - "struct_cfg": self.inputs.bgp_peer_groups.mlag_ipv4_underlay_peer.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": self.inputs.bgp_peer_groups.mlag_ipv4_underlay_peer.structured_config._as_dict() or None, } if self.shared_utils.node_config.mlag_ibgp_origin_incomplete: diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/struct_cfgs.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/struct_cfgs.py index 4b34c21ceb0..48f2ccc8054 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/struct_cfgs.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/struct_cfgs.py @@ -32,7 +32,7 @@ def struct_cfgs(self: AvdStructuredConfigNetworkServices) -> list | None: for vrf in tenant.vrfs: if vrf.structured_config: # Inserting VRF into structured_config to perform duplication checks - vrf_struct_cfg = {"vrf": vrf.name, "struct_cfg": vrf.structured_config._as_dict(strip_values=())} + vrf_struct_cfg = {"vrf": vrf.name, "struct_cfg": vrf.structured_config._as_dict()} append_if_not_duplicate( list_of_dicts=vrf_struct_cfgs, primary_key="vrf", diff --git a/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py b/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py index d07d1fa2fee..92b6958923a 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py +++ b/python-avd/pyavd/_eos_designs/structured_config/network_services/vlan_interfaces.py @@ -103,7 +103,7 @@ def _check_virtual_router_mac_address(vlan_interface_config: dict, variables: li "access_group_out": get(self._svi_acls, f"{interface_name}.ipv4_acl_out.name"), "mtu": svi.mtu if self.shared_utils.platform_settings.feature_support.per_interface_mtu else None, "eos_cli": svi.raw_eos_cli, - "struct_cfg": svi.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": svi.structured_config._as_dict() or None, } # Only set VARP if ip_address is set if vlan_interface_config["ip_address"] is not None: 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 54196e460d3..89d90a33cad 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 @@ -88,7 +88,7 @@ def _generate_base_peer_group( "password": peer_group.password, "send_community": "all", "maximum_routes": maximum_routes, - "struct_cfg": peer_group.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": peer_group.structured_config._as_dict() or None, } def _peer_groups(self: AvdStructuredConfigOverlay) -> list | None: diff --git a/python-avd/pyavd/_eos_designs/structured_config/underlay/router_bgp.py b/python-avd/pyavd/_eos_designs/structured_config/underlay/router_bgp.py index 835147cb981..1c35a78678c 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/router_bgp.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/router_bgp.py @@ -36,7 +36,7 @@ def router_bgp(self: AvdStructuredConfigUnderlay) -> dict | None: "bfd": self.inputs.bgp_peer_groups.ipv4_underlay_peers.bfd or None, "maximum_routes": 12000, "send_community": "all", - "struct_cfg": self.inputs.bgp_peer_groups.ipv4_underlay_peers.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": self.inputs.bgp_peer_groups.ipv4_underlay_peers.structured_config._as_dict() or None, } if self.shared_utils.overlay_routing_protocol == "ibgp" and self.shared_utils.is_cv_pathfinder_router: 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 556bc1d8dc9..20da753bfba 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py +++ b/python-avd/pyavd/_eos_designs/structured_config/underlay/utils.py @@ -182,7 +182,7 @@ def _get_l3_interface_cfg( "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(strip_values=()), + "struct_cfg": l3_interface.structured_config._as_dict(), "flow_tracker": self.shared_utils.get_flow_tracker(l3_interface.flow_tracking), } @@ -268,7 +268,7 @@ def _get_l2_as_subint( "ipv6_enable": svi.ipv6_enable, "mtu": svi.mtu if self.shared_utils.platform_settings.feature_support.per_interface_mtu else None, "eos_cli": svi.raw_eos_cli, - "struct_cfg": svi.structured_config._as_dict(strip_values=()) or None, + "struct_cfg": svi.structured_config._as_dict() or None, "flow_tracker": link.get("flow_tracker"), } if (mtu := subinterface["mtu"]) is not None and subinterface["mtu"] > self.shared_utils.p2p_uplinks_mtu: diff --git a/python-avd/pyavd/_schema/coerce_type.py b/python-avd/pyavd/_schema/coerce_type.py index d071a0f7539..34d9a3e2905 100644 --- a/python-avd/pyavd/_schema/coerce_type.py +++ b/python-avd/pyavd/_schema/coerce_type.py @@ -12,64 +12,9 @@ if TYPE_CHECKING: from typing import NoReturn, TypeVar - from typing_extensions import Self - - from pyavd._schema.models.type_vars import T_AvdBase - T = TypeVar("T") -def nullifiy_class(cls: type[T_AvdBase]) -> type: - """ - Returns a subclass of the given class with overrides for "null" values. - - This class is used when the input for a dict or a list is None/null/none, - to be able to signal to the deepmerge/inherit methods that this is not the same as an unset variable. - """ - - class NullifiedCls(cls): - def _get_defined_attr(self, name: str) -> T_AvdBase | None: - """ - Return the default values or None. - - This is required for the various merge / inheritance logic to always take from this if undefined. - """ - return getattr(self, name) - - def _as_dict(self, *_args: Any, **_kwargs: Any) -> None: - """Always None.""" - - def _as_list(self, *_args: Any, **_kwargs: Any) -> None: - """Always None.""" - - def __repr__(self) -> str: - return f"" - - def _deepinherited(self, *_args: Any, **_kwargs: Any) -> Self: - """Nothing to do since a NullifiedCls will override anything with None.""" - return self._deepcopy() - - def _deepinherit(self, *_args: Any, **_kwargs: Any) -> None: - """Nothing to do since a NullifiedCls will override anything with None.""" - - def _inherit(self, *_args: Any, **_kwargs: Any) -> None: - """Nothing to do since a NullifiedCls will override anything with None.""" - - def _deepmerge(self, *_args: Any, **_kwargs: Any) -> NoReturn: - msg = "A NullifiedCls cannot be inplace deepmerged. Use _deepmerged() instead." - raise NotImplementedError(msg) - - def _deepmerged(self, other: T_AvdBase, *_args: Any, **_kwargs: Any) -> T_AvdBase: - """Returning the other directly since NullifiedCls is empty.""" - return other._deepcopy() - - def _cast_as(self, new_type: type[T_AvdBase], *_args: Any, **_kwargs: Any) -> T_AvdBase: - """Wrap the new type in it's own NullifiedCls.""" - return nullifiy_class(new_type)() - - return NullifiedCls - - def coerce_type(value: Any, target_type: type[T]) -> T: """ Return a coerced variant of the given value to the target_type. @@ -81,8 +26,7 @@ def coerce_type(value: Any, target_type: type[T]) -> T: if value is None: if issubclass(target_type, AvdBase): # None values are sometimes used to overwrite inherited profiles. - # This ensures we still follow the type hint of the class. - return nullifiy_class(target_type)() + return target_type._from_null() # Other None values are left untouched. elif target_type is Any or isinstance(value, target_type): diff --git a/python-avd/pyavd/_schema/models/avd_base.py b/python-avd/pyavd/_schema/models/avd_base.py index 22beb558607..f9dd8bf714f 100644 --- a/python-avd/pyavd/_schema/models/avd_base.py +++ b/python-avd/pyavd/_schema/models/avd_base.py @@ -18,11 +18,21 @@ class AvdBase(ABC): """Base class used for schema-based data classes holding data loaded from AVD inputs.""" - def __eq__(self, other: object) -> bool: - """Compare two instances of AvdBase by comparing their repr.""" - if isinstance(other, self.__class__): - return repr(self) == repr(other) - return False + _created_from_null: bool = False + """ + Flag to say if this data was loaded from a ': null' value in YAML. + + This is used to handle inheritance and merging correctly. + When _created_from_null we inherit nothing (we win!). + When _created_from_null we take anything in when merging and clear the flag. + TODO: Stop changing data in-place. + + The flag is not carried across between classes, so it should not affect anything outside the loaded inputs. + Only exception is on _cast_as, where the flag is carried over. + """ + + _block_inheritance: bool = False + """Flag to block inheriting further if we at some point inherited from a class with _created_from_null set.""" def _deepcopy(self) -> Self: """Return a copy including all nested models.""" @@ -33,8 +43,19 @@ def _deepcopy(self) -> Self: def _load(cls, data: Sequence | Mapping) -> Self: """Returns a new instance loaded with the given data.""" + @classmethod + def _from_null(cls) -> Self: + """Returns a new instance with all attributes set to None. This represents the YAML input ': null'.""" + new_instance = cls() + new_instance._created_from_null = True + return new_instance + + @abstractmethod + def _strip_empties(self) -> None: + """In-place update the instance to remove data matching the given strip_values.""" + @abstractmethod - def _dump(self, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> dict | list: + def _dump(self, include_default_values: bool = False) -> dict | list: """Dump data into native Python types with or without default values.""" @abstractmethod diff --git a/python-avd/pyavd/_schema/models/avd_indexed_list.py b/python-avd/pyavd/_schema/models/avd_indexed_list.py index b2e43f5e651..cfb6118519c 100644 --- a/python-avd/pyavd/_schema/models/avd_indexed_list.py +++ b/python-avd/pyavd/_schema/models/avd_indexed_list.py @@ -5,9 +5,9 @@ import re from collections.abc import Iterable, Iterator, Sequence -from copy import deepcopy -from typing import TYPE_CHECKING, ClassVar, Generic, Literal +from typing import TYPE_CHECKING, ClassVar, Generic, Literal, cast +from pyavd._errors import AristaAvdDuplicateDataError from pyavd._schema.coerce_type import coerce_type from pyavd._utils import Undefined, UndefinedType @@ -52,7 +52,7 @@ def _from_list(cls, data: Sequence) -> Self: msg = f"Expecting 'data' as a 'Sequence' when loading data into '{cls.__name__}'. Got '{type(data)}" raise TypeError(msg) - cls_items = [coerce_type(item, cls._item_type) for item in data] + cls_items = cast(Iterable[T_AvdModel], (coerce_type(item, cls._item_type) for item in data)) return cls(cls_items) def __init__(self, items: Iterable[T_AvdModel] | UndefinedType = Undefined) -> None: @@ -104,22 +104,52 @@ def keys(self) -> Iterable[T_PrimaryKey]: def values(self) -> Iterable[T_AvdModel]: return self._items.values() - def append(self, item: T_AvdModel) -> None: - self._items[getattr(item, self._primary_key)] = item + def obtain(self, key: T_PrimaryKey) -> T_AvdModel: + """Return item with given primary key, autocreating if missing.""" + if key not in self._items: + item_type = cast(T_AvdModel, self._item_type) + self._items[key] = item_type._from_dict({self._primary_key: key}) + return self._items[key] + + def append(self, item: T_AvdModel, ignore_fields: tuple[str, ...] = ()) -> None: + if (primary_key := getattr(item, self._primary_key)) in self._items: + # Found existing entry using the same primary key. Ignore if it is the exact same content. + if item._compare(existing_item := self._items[primary_key], ignore_fields): + # Ignore identical item. + return + raise AristaAvdDuplicateDataError(type(self).__name__, str(item), str(existing_item)) + + self._items[primary_key] = item + + if TYPE_CHECKING: + append_new: type[T_AvdModel] + + else: + + def append_new(self, *args: Any, **kwargs: Any) -> T_AvdModel: + """ + Create a new instance with the given arguments and append to the list. + + Returns the new item, or in case of an identical duplicate item it returns the existing item. + """ + new_item = self._item_type(*args, **kwargs) + self.append(new_item) + return self._items[kwargs[self._primary_key]] 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, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> list[dict]: + def _strip_empties(self) -> None: + """In-place update the instance to remove data matching the given strip_values.""" + [item._strip_empties() for item in self._items.values()] + self._items = {primary_key: item for primary_key, item in self._items.items() if item} + + 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 [ - value - for item in self._items.values() - if (value := item._as_dict(include_default_values=include_default_values, strip_values=strip_values)) not in strip_values - ] + return [item._as_dict(include_default_values=include_default_values) for item in self._items.values()] - def _dump(self, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> list[dict]: - return self._as_list(include_default_values=include_default_values, strip_values=strip_values) + def _dump(self, include_default_values: bool = False) -> list[dict]: + return self._as_list(include_default_values=include_default_values) def _natural_sorted(self, ignore_case: bool = True) -> Self: """Return new instance where the items are natural sorted by primary key.""" @@ -151,15 +181,24 @@ def _deepmerge(self, other: Self, list_merge: Literal["append", "replace"] = "ap msg = f"Unable to merge type '{type(other)}' into '{cls}'" raise TypeError(msg) + if self._created_from_null or other._created_from_null: + # Clear the flag and set list_merge to replace so we overwrite with data from other below. + self._created_from_null = False + list_merge = "replace" + if list_merge == "replace": - self._items = deepcopy(other._items) + self._items = other._items.copy() return for primary_key, new_item in other.items(): - old_value = self.get(primary_key) - if old_value is Undefined or not isinstance(old_value, type(new_item)): + if new_item._created_from_null: + # Remove the complete item when merging in a Null item. + self._items.pop(primary_key, None) + continue + + if (old_value := self.get(primary_key)) is Undefined or not isinstance(old_value, type(new_item)): # New item or different type so we can just replace - self[primary_key] = deepcopy(new_item) + self[primary_key] = new_item continue # Existing item of same type, so deepmerge. @@ -172,11 +211,19 @@ def _deepinherit(self, other: Self) -> None: msg = f"Unable to inherit from type '{type(other)}' into '{cls}'" raise TypeError(msg) + if self._created_from_null or self._block_inheritance: + # Null always wins, so no inheritance. + return + + if other._created_from_null: + # Nothing to inherit, and we set the special block flag to prevent inheriting from something else later. + self._block_inheritance = True + return + for primary_key, new_item in other.items(): - old_value = self.get(primary_key) - if old_value is Undefined: + if self.get(primary_key) is Undefined: # New item so we can just append - self[primary_key] = deepcopy(new_item) + self[primary_key] = new_item continue # Existing item, so deepinherit. @@ -195,4 +242,10 @@ def _cast_as(self, new_type: type[T_AvdIndexedList], ignore_extra_keys: bool = F msg = f"Unable to cast '{cls}' as type '{new_type}' since '{new_type}' is not an AvdIndexedList subclass." raise TypeError(msg) - return new_type([item._cast_as(new_type._item_type, ignore_extra_keys=ignore_extra_keys) for item in self]) + new_instance = new_type([item._cast_as(new_type._item_type, ignore_extra_keys=ignore_extra_keys) for item in self]) + + # Pass along the internal flags + new_instance._created_from_null = self._created_from_null + new_instance._block_inheritance = self._block_inheritance + + return new_instance diff --git a/python-avd/pyavd/_schema/models/avd_list.py b/python-avd/pyavd/_schema/models/avd_list.py index 51266627969..490bf725d11 100644 --- a/python-avd/pyavd/_schema/models/avd_list.py +++ b/python-avd/pyavd/_schema/models/avd_list.py @@ -5,8 +5,7 @@ import re from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence -from copy import deepcopy -from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal +from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, cast from pyavd._schema.coerce_type import coerce_type from pyavd._utils import Undefined, UndefinedType @@ -57,10 +56,10 @@ def _from_list(cls, data: Sequence) -> Self: def __init__(self, items: Iterable[T_ItemType] | UndefinedType = Undefined) -> None: """ - AvdIndexedList subclass. + AvdList subclass. Args: - items: Iterable holding items of the correct type to be loaded into the indexed list. + items: Iterable holding items of the correct type to be loaded into the list. """ if isinstance(items, UndefinedType): self._items = [] @@ -98,20 +97,44 @@ def get(self, index: int, default: T | UndefinedType = Undefined) -> T_ItemType def append(self, item: T_ItemType) -> None: self._items.append(item) + def append_unique(self, item: T_ItemType) -> None: + """Append the item if not there already. Otherwise ignore.""" + if item not in self._items: + self._items.append(item) + + if TYPE_CHECKING: + append_new: type[T_ItemType] + else: + + def append_new(self, *args: Any, **kwargs: Any) -> T_ItemType: + """Create a new instance with the given arguments and append to the list. Returns the new item.""" + new_item = self._item_type(*args, **kwargs) + self.append(new_item) + return new_item + def extend(self, items: Iterable[T_ItemType]) -> None: self._items.extend(items) - def _as_list(self, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> list: + def _strip_empties(self) -> None: + """In-place update the instance to remove data matching the given strip_values.""" + if issubclass(self._item_type, AvdBase): + items = cast(list[AvdBase], self._items) + [item._strip_empties() for item in items] + self._items = [item for item in self._items if item] + return + + self._items = [item for item in self._items if item is not None] + + def _as_list(self, include_default_values: bool = False) -> list: """Returns a list with all the data from this model and any nested models.""" if issubclass(self._item_type, AvdBase): - items: list[AvdBase] = self._items - return [ - value for item in items if (value := item._dump(include_default_values=include_default_values, strip_values=strip_values)) not in strip_values - ] - return [item for item in self._items if item not in strip_values] + items = cast(list[AvdBase], self._items) + return [item._dump(include_default_values=include_default_values) for item in items] + + return list(self._items) - def _dump(self, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> list: - return self._as_list(include_default_values=include_default_values, strip_values=strip_values) + def _dump(self, include_default_values: bool = False) -> list: + return self._as_list(include_default_values=include_default_values) def _natural_sorted(self, sort_key: str | None = None, ignore_case: bool = True) -> Self: """Return new instance where the items are natural sorted by the given sort key or by the item itself.""" @@ -153,12 +176,17 @@ def _deepmerge(self, other: Self, list_merge: Literal["append", "replace"] = "ap msg = f"Unable to merge type '{type(other)}' into '{cls}'" raise TypeError(msg) + if self._created_from_null: + # Overwrite all data from other and clear the flag. + self._created_from_null = False + list_merge = "replace" + if list_merge == "replace": - self._items = deepcopy(other._items) + self._items = other._items.copy() return # Append non-existing items. - self._items.extend(deepcopy([new_item for new_item in other._items if new_item not in self._items])) + self._items.extend(new_item for new_item in other._items if new_item not in self._items) def _cast_as(self, new_type: type[T_AvdList], ignore_extra_keys: bool = False) -> T_AvdList: """ @@ -174,11 +202,16 @@ def _cast_as(self, new_type: type[T_AvdList], ignore_extra_keys: bool = False) - raise TypeError(msg) if issubclass(self._item_type, AvdBase): - items: list[AvdBase] = self._items + items = cast(list[AvdBase], self._items) return new_type([item._cast_as(new_type._item_type, ignore_extra_keys=ignore_extra_keys) for item in items]) if self._item_type != new_type._item_type: msg = f"Unable to cast '{cls}' as type '{new_type}' since they have incompatible item types." raise TypeError(msg) - return new_type(self._items) + new_instance = new_type(self._items) + + # Pass along the _created_from_null flag + new_instance._created_from_null = self._created_from_null + + return new_instance diff --git a/python-avd/pyavd/_schema/models/avd_model.py b/python-avd/pyavd/_schema/models/avd_model.py index c425e51708d..cca18716d25 100644 --- a/python-avd/pyavd/_schema/models/avd_model.py +++ b/python-avd/pyavd/_schema/models/avd_model.py @@ -5,7 +5,8 @@ from collections.abc import Mapping from copy import deepcopy -from typing import TYPE_CHECKING, Any, ClassVar, Literal +from logging import getLogger +from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast from pyavd._schema.coerce_type import coerce_type from pyavd._utils import Undefined, UndefinedType, merge @@ -18,6 +19,8 @@ from .type_vars import T_AvdModel +LOGGER = getLogger(__name__) + class AvdModel(AvdBase): """Base class used for schema-based data classes holding dictionaries loaded from AVD inputs.""" @@ -131,6 +134,8 @@ def _get_defined_attr(self, name: str) -> Any | UndefinedType: Get attribute or Undefined. Avoids the overridden __getattr__ to avoid default values. + + Falls back to __getattr__ in case of _created_from_null to always insert None or default value. """ if name not in self._fields: msg = f"'{type(self).__name__}' object has no attribute '{name}'" @@ -164,39 +169,57 @@ def __bool__(self) -> bool: for field in self._fields ) - def _as_dict(self, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> dict: + def _strip_empties(self) -> None: + """In-place update the instance to remove data matching the given strip_values.""" + for field, field_info in self._fields.items(): + if (value := self._get_defined_attr(field)) is Undefined or field == "_custom_data": + continue + + if issubclass(field_info["type"], AvdBase): + value = cast(AvdBase, value) + value._strip_empties() + if not value: + delattr(self, field) + continue + + if value is None: + delattr(self, field) + + def _as_dict(self, include_default_values: bool = False) -> dict: """ 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: + for field, field_info in self._fields.items(): + value = self._get_defined_attr(field) + + if field == "_custom_data": + if value: + value = cast(dict[str, Any], value) + as_dict.update(value) + continue + + if value is Undefined: if not include_default_values: continue value = self._get_field_default_value(field) - if field == "_custom_data" and isinstance(value, dict) and value: - as_dict.update(value) - continue - # Removing field_ prefix if needed. key = self._field_to_key_map.get(field, field) - if issubclass(field_info["type"], AvdBase) and isinstance(value, AvdBase): - value = value._dump(include_default_values=include_default_values, strip_values=strip_values) - - if value in strip_values: - continue + if issubclass(field_info["type"], AvdBase): + value = cast(AvdBase, value) + value = None if value._created_from_null else value._dump(include_default_values=include_default_values) as_dict[key] = value return as_dict - def _dump(self, include_default_values: bool = False, strip_values: tuple = (None, [], {})) -> dict: - return self._as_dict(include_default_values=include_default_values, strip_values=strip_values) + def _dump(self, include_default_values: bool = False) -> dict: + return self._as_dict(include_default_values=include_default_values) def _get(self, name: str, default: Any = None) -> Any: """ @@ -208,20 +231,14 @@ def _get(self, name: str, default: Any = None) -> Any: return default return value - def _update(self, other: Self) -> None: - """Update instance by shallow merging the other instance in.""" - cls = type(self) - if not isinstance(other, cls): - msg = f"Unable to merge type '{type(other)}' into '{cls}'" - raise TypeError(msg) + if TYPE_CHECKING: + _update: type[Self] + else: - for field in cls._fields: - if new_value := other._get_defined_attr(field) is Undefined: - continue - old_value = self._get_defined_attr(field) - if old_value == new_value: - continue - setattr(self, field, new_value) + def _update(self, *args: Any, **kwargs: Any) -> Self: + """Update instance with the given kwargs. Reuses __init__.""" + self.__init__(*args, **kwargs) + return self def _deepmerge(self, other: Self, list_merge: Literal["append", "replace"] = "append") -> None: """ @@ -239,6 +256,10 @@ def _deepmerge(self, other: Self, list_merge: Literal["append", "replace"] = "ap raise TypeError(msg) for field, field_info in cls._fields.items(): + if other._created_from_null and self._get_defined_attr(field) is not Undefined: + # Force the field back to unset if other is a "null" class. + delattr(self, field) + if (new_value := other._get_defined_attr(field)) is Undefined: continue old_value = self._get_defined_attr(field) @@ -246,25 +267,34 @@ def _deepmerge(self, other: Self, list_merge: Literal["append", "replace"] = "ap continue if not isinstance(old_value, type(new_value)): - # Different type so we can just replace - setattr(self, field, deepcopy(new_value)) + # Different types so we can just replace with the new value. + setattr(self, field, new_value) continue # Merge new value field_type = field_info["type"] - if issubclass(field_type, AvdBase) and isinstance(old_value, field_type): + if issubclass(field_type, AvdBase): # Merge in to the existing object + old_value = cast(AvdBase, old_value) + new_value = cast(AvdBase, new_value) old_value._deepmerge(new_value, list_merge=list_merge) continue if field_type is dict: # In-place deepmerge in to the existing dict without schema. # Deepcopying since merge() does not copy. - merge(old_value, deepcopy(new_value), list_merge=list_merge) + merge(old_value, new_value, list_merge=list_merge) continue setattr(self, field, new_value) + if other._created_from_null: + # Inherit the _created_from_null attribute to make sure we output null values instead of empty dicts. + self._created_from_null = True + elif self._created_from_null: + # We merged into a "null" class, but since we now have proper data, we clear the flag. + self._created_from_null = False + def _inherit(self, other: Self) -> None: """Update unset fields on this instance with fields from other instance. No merging.""" cls = type(self) @@ -272,6 +302,15 @@ def _inherit(self, other: Self) -> None: msg = f"Unable to inherit from type '{type(other)}' into '{cls}'" raise TypeError(msg) + if self._created_from_null: + # Null always wins, so no inheritance. + return + + if other._created_from_null: + # Nothing to inherit, but we set the flag to prevent inheriting from something else later. + self._created_from_null = True + return + for field in cls._fields: if self._get_defined_attr(field) is not Undefined: continue @@ -287,6 +326,15 @@ def _deepinherit(self, other: Self) -> None: msg = f"Unable to inherit from type '{type(other)}' into '{cls}'" raise TypeError(msg) + if self._created_from_null or self._block_inheritance: + # Null always wins, so no inheritance. + return + + if other._created_from_null: + # Nothing to inherit, and we set the special block flag to prevent inheriting from something else later. + self._block_inheritance = True + return + for field, field_info in cls._fields.items(): if (new_value := other._get_defined_attr(field)) is Undefined: continue @@ -296,19 +344,26 @@ def _deepinherit(self, other: Self) -> None: # Inherit the field only if the old value is Undefined. if old_value is Undefined: - setattr(self, field, deepcopy(new_value)) + setattr(self, field, new_value) continue # Merge new value if it is a class with inheritance support. field_type = field_info["type"] - if issubclass(field_type, (AvdModel, AvdIndexedList)) and isinstance(old_value, field_type): + if issubclass(field_type, AvdModel): # Inherit into the existing object. + old_value = cast(AvdModel, old_value) + new_value = cast(AvdModel, new_value) + old_value._deepinherit(new_value) + continue + if issubclass(field_type, AvdIndexedList): + # Inherit into the existing object. + old_value = cast(AvdIndexedList, old_value) + new_value = cast(AvdIndexedList, new_value) old_value._deepinherit(new_value) continue if field_type is dict: # In-place deepmerge in to the existing dict without schema. - # Deepcopying since merge() does not copy. merge(old_value, deepcopy(new_value), list_merge="replace") def _deepinherited(self, other: Self) -> Self: @@ -341,9 +396,10 @@ def _cast_as(self, new_type: type[T_AvdModel], ignore_extra_keys: bool = False) msg = f"Unable to cast '{cls}' as type '{new_type}' since the field '{field}' is missing from the new class. " raise TypeError(msg) if field_info != new_type._fields[field]: - if issubclass(field_info["type"], (AvdBase)) and isinstance(value, (AvdBase)): + if issubclass(field_info["type"], AvdBase): # TODO: Consider using the TypeError we raise below to ensure we know the outer type. # TODO: with suppress(TypeError): + value = cast(AvdBase, value) new_args[field] = value._cast_as(new_type._fields[field]["type"], ignore_extra_keys=ignore_extra_keys) continue @@ -351,6 +407,19 @@ def _cast_as(self, new_type: type[T_AvdModel], ignore_extra_keys: bool = False) raise TypeError(msg) new_args[field] = value - continue - return new_type(**new_args) + new_instance = new_type(**new_args) + + # Pass along the internal flags + new_instance._created_from_null = self._created_from_null + new_instance._block_inheritance = self._block_inheritance + + return new_instance + + def _compare(self, other: Self, ignore_fields: tuple[str, ...] = ()) -> bool: + cls = type(self) + if not isinstance(other, cls): + msg = f"Unable to compare '{cls}' with a '{type(other)}' class." + raise TypeError(msg) + + return all(self._get_defined_attr(field) == other._get_defined_attr(field) for field in self._fields if field not in ignore_fields) diff --git a/python-avd/tests/pyavd/molecule_scenarios/test_eos_designs_class.py b/python-avd/tests/pyavd/molecule_scenarios/test_eos_designs_class.py index 40b7eb0b4b2..823c0756b21 100644 --- a/python-avd/tests/pyavd/molecule_scenarios/test_eos_designs_class.py +++ b/python-avd/tests/pyavd/molecule_scenarios/test_eos_designs_class.py @@ -46,7 +46,7 @@ @pytest.mark.parametrize(("prefix", "expected_data"), CSC_TESTS) -def test_eos_designs_custom_structured_configuration(prefix: str | None, expected_data: dict) -> None: +def test_eos_designs_custom_structured_configuration(prefix: str | None, expected_data: EosDesigns._CustomStructuredConfigurations) -> None: data = CSC_DATA.copy() if prefix: data.update({"custom_structured_configuration_prefix": prefix}) @@ -57,7 +57,7 @@ def test_eos_designs_custom_structured_configuration(prefix: str | None, expecte for entry in loaded_model._custom_structured_configurations: assert isinstance(entry, EosDesigns._CustomStructuredConfigurationsItem) - assert loaded_model._custom_structured_configurations == expected_data + assert repr(loaded_model._custom_structured_configurations) == repr(expected_data) # eos_cli_config_gen inputs are validated by `validate_structured_config` in another file. From 7b2196825ca92c3b55bb9467e3c464a7c829d92f Mon Sep 17 00:00:00 2001 From: Shivani-gslab <145646625+Shivani-gslab@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:27:11 +0530 Subject: [PATCH 2/7] Feat(eos_cli_config_gen): Add switchport 'tap' and 'tool' mode config to the ethernet and port-channel interfaces (#4174) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Shivani Chourasiya Co-authored-by: Carl Buchmann Co-authored-by: Guillaume Mulocher Co-authored-by: Claus Holbech --- .../documentation/devices/host1.md | 86 +- .../intended/configs/host1.cfg | 86 +- .../host_vars/host1/ethernet-interfaces.yml | 107 + .../host1/port-channel-interfaces.yml | 101 +- .../docs/tables/ethernet-interfaces.md | 185 + .../docs/tables/port-channel-interfaces.md | 185 + .../j2templates/eos/ethernet-interfaces.j2 | 113 + .../eos/port-channel-interfaces.j2 | 110 + .../_eos_cli_config_gen/schema/__init__.py | 5904 ++++++++++------- .../schema/eos_cli_config_gen.schema.yml | 257 + .../ethernet_interfaces.schema.yml | 225 + .../port_channel_interfaces.schema.yml | 6 + 12 files changed, 5146 insertions(+), 2219 deletions(-) diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md index 093907ebd10..728b9e0be8f 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md @@ -4758,6 +4758,49 @@ interface Ethernet81/2 interface Ethernet81/10 description isis_port_channel_member channel-group 110 mode active +! +interface Ethernet82 + description Switchport_tap_tool + switchport tap native vlan 10 + switchport tap identity 3 inner 5 + switchport tap mac-address dest 01:00:00:00:00:00 src 01:23:45:67:89:ab + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 protocol 0x0 strip + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x10 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x11 feature header length 2 strip re-encapsulation ethernet + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x12 strip re-encapsulation ethernet + switchport tap encapsulation gre destination 2.1.1.3 source 2.1.1.4 strip + switchport tap mpls pop all + switchport tool mpls pop all + switchport tool encapsulation vn-tag strip + switchport tool encapsulation dot1br strip + switchport tap allowed vlan 25 + switchport tool allowed vlan 23 + switchport tool identity qinq + switchport tool identity dot1q source dzgre port + switchport tap truncation 150 + switchport tap default group g1 group g2 group g3 + switchport tap default nexthop-group nexthop_g1 nexthop_g2 nexthop_g3 + switchport tap default interface ethernet4 + switchport tap default interface port-channel10 + switchport tool group set group1 group2 group3 + switchport tool dot1q remove outer 1 +! +interface Ethernet83 + description Test_tap_tool + switchport tap identity 5 + switchport tap mac-address dest 01:00:00:00:00:00 + switchport tap encapsulation vxlan strip + switchport tap encapsulation gre strip + switchport tool identity dot1q + switchport tool identity qinq source dzgre policy inner port + switchport tap truncation +! +interface Ethernet84 + switchport tap encapsulation gre protocol 0x1 strip + switchport tap encapsulation gre protocol 0x2 feature header length 3 strip + switchport tap encapsulation gre protocol 0x3 feature header length 2 strip re-encapsulation ethernet + switchport tap encapsulation gre protocol 0x4 strip re-encapsulation ethernet ``` ### Port-Channel Interfaces @@ -5111,7 +5154,7 @@ interface Port-Channel16 isis authentication mode md5 isis authentication key 0 spanning-tree guard none - switchport backup-link Port-Channel100.102 prefer vlan 20 + switchport backup-link Port-Channel100 prefer vlan 20 ! interface Port-Channel17 description PBR Description @@ -5502,6 +5545,47 @@ interface Port-Channel131.10 interface Port-Channel132 profile test-interface-profile description Test_port-channel_interface-profile +! +interface Port-Channel133 + description Test1_switchport_tap_tool + switchport tap native vlan 10 + switchport tap identity 3 + switchport tap mac-address dest 01:00:00:00:00:00 src 01:23:45:67:89:ab + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 protocol 0x0 strip + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 strip + switchport tap encapsulation gre destination 1.1.1.3 source 1.1.1.4 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x1 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x2 feature header length 2 strip re-encapsulation ethernet + switchport tap mpls pop all + switchport tool mpls pop all + switchport tool encapsulation vn-tag strip + switchport tool encapsulation dot1br strip + switchport tap allowed vlan 25 + switchport tool allowed vlan 23 + switchport tool identity qinq + switchport tool identity qinq source dzgre port inner policy + switchport tap truncation + switchport tap default group g1 group g2 group g3 + switchport tap default nexthop-group nexthop_g1 nexthop_g2 nexthop_g3 + switchport tap default interface ethernet4 + switchport tap default interface port-channel10 + switchport tool group set group1 group2 group3 + switchport tool dot1q remove outer 1-2 +! +interface Port-Channel134 + description Test2_switchport_tap_tool + switchport tap identity 3 inner 10 + switchport tap mac-address dest 01:00:00:00:00:00 + switchport tap encapsulation vxlan strip + switchport tap encapsulation gre strip + switchport tool identity dot1q + switchport tool identity dot1q source dzgre policy + switchport tap truncation 120 +! +interface Port-Channel135 + switchport tap encapsulation gre protocol 0x2 feature header length 3 strip + switchport tap encapsulation gre protocol 0x3 feature header length 2 strip re-encapsulation ethernet + switchport tap encapsulation gre protocol 0x10 strip ``` ### Loopback Interfaces diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg index 1bccdbcf856..60c6848eda4 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg @@ -1783,7 +1783,7 @@ interface Port-Channel16 isis authentication mode md5 isis authentication key 0 password spanning-tree guard none - switchport backup-link Port-Channel100.102 prefer vlan 20 + switchport backup-link Port-Channel100 prefer vlan 20 ! interface Port-Channel17 description PBR Description @@ -2175,6 +2175,47 @@ interface Port-Channel132 profile test-interface-profile description Test_port-channel_interface-profile ! +interface Port-Channel133 + description Test1_switchport_tap_tool + switchport tap native vlan 10 + switchport tap identity 3 + switchport tap mac-address dest 01:00:00:00:00:00 src 01:23:45:67:89:ab + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 protocol 0x0 strip + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 strip + switchport tap encapsulation gre destination 1.1.1.3 source 1.1.1.4 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x1 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x2 feature header length 2 strip re-encapsulation ethernet + switchport tap mpls pop all + switchport tool mpls pop all + switchport tool encapsulation vn-tag strip + switchport tool encapsulation dot1br strip + switchport tap allowed vlan 25 + switchport tool allowed vlan 23 + switchport tool identity qinq + switchport tool identity qinq source dzgre port inner policy + switchport tap truncation + switchport tap default group g1 group g2 group g3 + switchport tap default nexthop-group nexthop_g1 nexthop_g2 nexthop_g3 + switchport tap default interface ethernet4 + switchport tap default interface port-channel10 + switchport tool group set group1 group2 group3 + switchport tool dot1q remove outer 1-2 +! +interface Port-Channel134 + description Test2_switchport_tap_tool + switchport tap identity 3 inner 10 + switchport tap mac-address dest 01:00:00:00:00:00 + switchport tap encapsulation vxlan strip + switchport tap encapsulation gre strip + switchport tool identity dot1q + switchport tool identity dot1q source dzgre policy + switchport tap truncation 120 +! +interface Port-Channel135 + switchport tap encapsulation gre protocol 0x2 feature header length 3 strip + switchport tap encapsulation gre protocol 0x3 feature header length 2 strip re-encapsulation ethernet + switchport tap encapsulation gre protocol 0x10 strip +! interface Dps1 description Test DPS Interface shutdown @@ -3155,6 +3196,49 @@ interface Ethernet81/10 description isis_port_channel_member channel-group 110 mode active ! +interface Ethernet82 + description Switchport_tap_tool + switchport tap native vlan 10 + switchport tap identity 3 inner 5 + switchport tap mac-address dest 01:00:00:00:00:00 src 01:23:45:67:89:ab + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 protocol 0x0 strip + switchport tap encapsulation gre destination 1.1.1.1 source 1.1.1.2 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x10 strip + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x11 feature header length 2 strip re-encapsulation ethernet + switchport tap encapsulation gre destination 2.1.1.2 protocol 0x12 strip re-encapsulation ethernet + switchport tap encapsulation gre destination 2.1.1.3 source 2.1.1.4 strip + switchport tap mpls pop all + switchport tool mpls pop all + switchport tool encapsulation vn-tag strip + switchport tool encapsulation dot1br strip + switchport tap allowed vlan 25 + switchport tool allowed vlan 23 + switchport tool identity qinq + switchport tool identity dot1q source dzgre port + switchport tap truncation 150 + switchport tap default group g1 group g2 group g3 + switchport tap default nexthop-group nexthop_g1 nexthop_g2 nexthop_g3 + switchport tap default interface ethernet4 + switchport tap default interface port-channel10 + switchport tool group set group1 group2 group3 + switchport tool dot1q remove outer 1 +! +interface Ethernet83 + description Test_tap_tool + switchport tap identity 5 + switchport tap mac-address dest 01:00:00:00:00:00 + switchport tap encapsulation vxlan strip + switchport tap encapsulation gre strip + switchport tool identity dot1q + switchport tool identity qinq source dzgre policy inner port + switchport tap truncation +! +interface Ethernet84 + switchport tap encapsulation gre protocol 0x1 strip + switchport tap encapsulation gre protocol 0x2 feature header length 3 strip + switchport tap encapsulation gre protocol 0x3 feature header length 2 strip re-encapsulation ethernet + switchport tap encapsulation gre protocol 0x4 strip re-encapsulation ethernet +! interface Loopback0 description EVPN_Overlay_Peering ip address 192.168.255.3/32 diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml index ea1e1d17356..b6c5faea864 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml @@ -1656,6 +1656,7 @@ ethernet_interfaces: inner_vlan: 11 network: encapsulation: client inner + - name: Ethernet70 description: dot1x_aaa_unresponsive shutdown: false @@ -1674,6 +1675,7 @@ ethernet_interfaces: traffic_allow: true apply_alternate: true mac_based_access_list: true + - name: Ethernet71 description: dot1x_aaa_unresponsive1 shutdown: false @@ -1690,6 +1692,7 @@ ethernet_interfaces: time_duration: 10 time_duration_unit: hours mac_based_access_list: true + - name: Ethernet72 description: dot1x_aaa_unresponsive2 shutdown: false @@ -1845,3 +1848,107 @@ ethernet_interfaces: spanning_tree_portfast: edge lldp: ztp_vlan: 112 + + - name: Ethernet82 + description: Switchport_tap_tool + switchport: + tap: + allowed_vlan: 25 + default: + groups: + - g2 + - g1 + - g3 + nexthop_groups: + - nexthop_g1 + - nexthop_g3 + - nexthop_g2 + interfaces: + - port-channel10 + - ethernet4 + identity: + id: 3 + inner_vlan: 5 + mpls_pop_all: true + native_vlan: 10 + truncation: + enabled: true + size: 150 + mac_address: + destination: 01:00:00:00:00:00 + source: 01:23:45:67:89:ab + encapsulation: + gre: + destinations: + - destination: 1.1.1.1 + source: 1.1.1.2 + protocols: + - protocol: "0x0" + strip: true + strip: true + - destination: 2.1.1.2 + protocols: + - protocol: "0x10" + strip: true + - protocol: "0x11" + feature_header_length: 2 + re_encapsulation_ethernet_header: true + strip: true + - protocol: "0x12" + re_encapsulation_ethernet_header: true + strip: true + - destination: 2.1.1.3 + source: 2.1.1.4 + strip: true + tool: + mpls_pop_all: true + encapsulation: + dot1br_strip: true + vn_tag_strip: true + allowed_vlan: 23 + identity: + tag: qinq + dot1q_dzgre_source: port + groups: + - group1 + - group2 + - group3 + dot1q_remove_outer_vlan_tag: 1 + + - name: Ethernet83 + description: Test_tap_tool + switchport: + tap: + identity: + id: 5 + mac_address: + destination: 01:00:00:00:00:00 + encapsulation: + vxlan_strip: true + gre: + strip: true + truncation: + enabled: true + tool: + identity: + tag: dot1q + qinq_dzgre_source: policy inner port + + - name: Ethernet84 + switchport: + tap: + encapsulation: + gre: + protocols: + - protocol: "0x1" + strip: true + - protocol: "0x2" + feature_header_length: 3 + strip: true + - protocol: "0x3" + feature_header_length: 2 + re_encapsulation_ethernet_header: true + strip: true + - protocol: "0x4" + re_encapsulation_ethernet_header: true + strip: true diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml index f0e20267983..16709ce88ce 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml @@ -141,7 +141,7 @@ port_channel_interfaces: - from: 23 dot1q_tunnel_to: 22 backup_link: - interface: Port-Channel100.102 + interface: Port-Channel100 prefer_vlan: 20 - name: Port-Channel3 @@ -947,3 +947,102 @@ port_channel_interfaces: - name: Port-Channel132 description: Test_port-channel_interface-profile profile: test-interface-profile + + - name: Port-Channel133 + description: Test1_switchport_tap_tool + switchport: + tap: + allowed_vlan: 25 + default: + groups: + - g2 + - g1 + - g3 + nexthop_groups: + - nexthop_g1 + - nexthop_g3 + - nexthop_g2 + interfaces: + - port-channel10 + - ethernet4 + identity: + id: 3 + mpls_pop_all: true + native_vlan: 10 + truncation: + enabled: true + mac_address: + destination: 01:00:00:00:00:00 + source: 01:23:45:67:89:ab + encapsulation: + gre: + destinations: + - destination: 1.1.1.1 + source: 1.1.1.2 + protocols: + # the protocol value must be enclosed in quotes + - protocol: "0x0" + strip: true + strip: true + - destination: 2.1.1.2 + protocols: + - protocol: "0x1" + strip: true + - protocol: "0x2" + feature_header_length: 2 + re_encapsulation_ethernet_header: true + strip: true + - destination: 1.1.1.3 + source: 1.1.1.4 + strip: true + tool: + mpls_pop_all: true + encapsulation: + dot1br_strip: true + vn_tag_strip: true + allowed_vlan: 23 + identity: + tag: qinq + qinq_dzgre_source: port inner policy + groups: + - group1 + - group2 + - group3 + dot1q_remove_outer_vlan_tag: 1-2 + + - name: Port-Channel134 + description: Test2_switchport_tap_tool + switchport: + tap: + identity: + id: 3 + inner_vlan: 10 + mac_address: + destination: 01:00:00:00:00:00 + encapsulation: + vxlan_strip: true + gre: + strip: true + truncation: + enabled: true + size: 120 + tool: + identity: + tag: dot1q + dot1q_dzgre_source: policy + + - name: Port-Channel135 + switchport: + tap: + encapsulation: + gre: + protocols: + - protocol: "0x10" + strip: true + - protocol: "0x2" + feature_header_length: 3 + strip: true + - protocol: "0x3" + feature_header_length: 2 + re_encapsulation_ethernet_header: true + strip: true diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md index 1c560ba600c..2222322c542 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md @@ -548,6 +548,57 @@ | [        vlans](## "ethernet_interfaces.[].switchport.port_security.vlans") | List, items: Dictionary | | | | | | [          - range](## "ethernet_interfaces.[].switchport.port_security.vlans.[].range") | String | Required, Unique | | | VLAN ID or range(s) of VLAN IDs, <1-4094>.
Example:
- 3
- 1,3
- 1-10
| | [            mac_address_maximum](## "ethernet_interfaces.[].switchport.port_security.vlans.[].mac_address_maximum") | Integer | Required | | | | + | [      tap](## "ethernet_interfaces.[].switchport.tap") | Dictionary | | | | In tap mode, the interface operates as a tap port.
Tap ports receive traffic for replication on one or more tool ports.
This setting applies only to parent interfaces. | + | [        allowed_vlan](## "ethernet_interfaces.[].switchport.tap.allowed_vlan") | String | | | | VLAN ID or range(s) of VLAN IDs within range 1-4094. | + | [        default](## "ethernet_interfaces.[].switchport.tap.default") | Dictionary | | | | Default tap destination config. | + | [          groups](## "ethernet_interfaces.[].switchport.tap.default.groups") | List, items: String | | | | Tap group names for the interface. | + | [            - <str>](## "ethernet_interfaces.[].switchport.tap.default.groups.[]") | String | | | | | + | [          interfaces](## "ethernet_interfaces.[].switchport.tap.default.interfaces") | List, items: String | | | | Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. | + | [            - <str>](## "ethernet_interfaces.[].switchport.tap.default.interfaces.[]") | String | | | | | + | [          nexthop_groups](## "ethernet_interfaces.[].switchport.tap.default.nexthop_groups") | List, items: String | | | | Default nexthop-group names. | + | [            - <str>](## "ethernet_interfaces.[].switchport.tap.default.nexthop_groups.[]") | String | | | | | + | [        identity](## "ethernet_interfaces.[].switchport.tap.identity") | Dictionary | | | | | + | [          id](## "ethernet_interfaces.[].switchport.tap.identity.id") | Integer | | | Min: 1
Max: 65535 | Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). | + | [          inner_vlan](## "ethernet_interfaces.[].switchport.tap.identity.inner_vlan") | Integer | | | Min: 1
Max: 4094 | Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). | + | [        mpls_pop_all](## "ethernet_interfaces.[].switchport.tap.mpls_pop_all") | Boolean | | | | Pop all MPLS labels. | + | [        native_vlan](## "ethernet_interfaces.[].switchport.tap.native_vlan") | Integer | | | Min: 1
Max: 4094 | Native VLAN ID when interface is in tap mode. | + | [        truncation](## "ethernet_interfaces.[].switchport.tap.truncation") | Dictionary | | | | | + | [          enabled](## "ethernet_interfaces.[].switchport.tap.truncation.enabled") | Boolean | | | | | + | [          size](## "ethernet_interfaces.[].switchport.tap.truncation.size") | Integer | | | Min: 100
Max: 9236 | Ingress packet truncation size in bytes. | + | [        mac_address](## "ethernet_interfaces.[].switchport.tap.mac_address") | Dictionary | | | | | + | [          source](## "ethernet_interfaces.[].switchport.tap.mac_address.source") | String | | | Pattern: `^([0-9a-f]{2}:){5}[0-9a-f]{2}$` | MAC address for the source. | + | [          destination](## "ethernet_interfaces.[].switchport.tap.mac_address.destination") | String | | | Pattern: `^([0-9a-f]{2}:){5}[0-9a-f]{2}$` | MAC address for the destination. | + | [        encapsulation](## "ethernet_interfaces.[].switchport.tap.encapsulation") | Dictionary | | | | | + | [          vxlan_strip](## "ethernet_interfaces.[].switchport.tap.encapsulation.vxlan_strip") | Boolean | | | | Strip VXLAN encapsulation header.
`encapsulation.vxlan_strip` and `mpls_pop_all` are mutually exclusive.
`mpls_pop_all` takes precedence. | + | [          gre](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre") | Dictionary | | | | | + | [            strip](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.strip") | Boolean | | | | Strip GRE encapsulation header for all GRE tunnels. | + | [            protocols](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.protocols") | List, items: Dictionary | | | | Protocols for all destinations; destination-specific protocols should be set under the `destinations[].protocols` key. | + | [              - protocol](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].protocol") | String | Required, Unique | | | Protocol type in GRE header.
Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". | + | [                strip](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].strip") | Boolean | | | | This is a required key to strip GRE encapsulation header with protocols. | + | [                feature_header_length](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].feature_header_length") | Integer | | | Min: 1
Max: 16 | Feature header length in bytes.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [                re_encapsulation_ethernet_header](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].re_encapsulation_ethernet_header") | Boolean | | | | Extra ethernet header to prepend to the terminated packet.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [            destinations](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations") | List, items: Dictionary | | | | In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are mutually exclusive. | + | [              - destination](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].destination") | String | Required, Unique | | | Destination IP address of tunnel packets. | + | [                source](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].source") | String | | | | Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any GRE packet that matches the `destination` is terminated. | + | [                strip](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].strip") | Boolean | | | | Strip GRE encapsulation header for specific destination. | + | [                protocols](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols") | List, items: Dictionary | | | | | + | [                  - protocol](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].protocol") | String | Required, Unique | | | Protocol type in GRE header.
Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". | + | [                    strip](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].strip") | Boolean | | | | This is a required key to strip GRE encapsulation header for specific destination with protocols. | + | [                    feature_header_length](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].feature_header_length") | Integer | | | Min: 1
Max: 16 | Feature header length in bytes.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [                    re_encapsulation_ethernet_header](## "ethernet_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].re_encapsulation_ethernet_header") | Boolean | | | | Extra ethernet header to prepend to the terminated packet.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [      tool](## "ethernet_interfaces.[].switchport.tool") | Dictionary | | | | In tool mode, the interface operates as a tool port.
Tool ports replicate traffic received by tap ports.
This setting applies only to parent interfaces. | + | [        mpls_pop_all](## "ethernet_interfaces.[].switchport.tool.mpls_pop_all") | Boolean | | | | Pop all MPLS labels. | + | [        encapsulation](## "ethernet_interfaces.[].switchport.tool.encapsulation") | Dictionary | | | | | + | [          dot1br_strip](## "ethernet_interfaces.[].switchport.tool.encapsulation.dot1br_strip") | Boolean | | | | Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. | + | [          vn_tag_strip](## "ethernet_interfaces.[].switchport.tool.encapsulation.vn_tag_strip") | Boolean | | | | Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. | + | [        allowed_vlan](## "ethernet_interfaces.[].switchport.tool.allowed_vlan") | String | | | | VLAN ID or range of VLAN IDs within range 1-4094. | + | [        identity](## "ethernet_interfaces.[].switchport.tool.identity") | Dictionary | | | | | + | [          tag](## "ethernet_interfaces.[].switchport.tool.identity.tag") | String | | | Valid Values:
- dot1q
- qinq | | + | [          dot1q_dzgre_source](## "ethernet_interfaces.[].switchport.tool.identity.dot1q_dzgre_source") | String | | | Valid Values:
- policy
- port | | + | [          qinq_dzgre_source](## "ethernet_interfaces.[].switchport.tool.identity.qinq_dzgre_source") | String | | | Valid Values:
- policy inner port
- port inner policy | | + | [        groups](## "ethernet_interfaces.[].switchport.tool.groups") | List, items: String | | | | Tool groups for the interface. | + | [          - <str>](## "ethernet_interfaces.[].switchport.tool.groups.[]") | String | | | | | + | [        dot1q_remove_outer_vlan_tag](## "ethernet_interfaces.[].switchport.tool.dot1q_remove_outer_vlan_tag") | String | | | | Indices of vlan tags to be removed.
Range: 1-2 | | [    eos_cli](## "ethernet_interfaces.[].eos_cli") | String | | | | Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. | === "YAML" @@ -1729,6 +1780,140 @@ - range: mac_address_maximum: + # In tap mode, the interface operates as a tap port. + # Tap ports receive traffic for replication on one or more tool ports. + # This setting applies only to parent interfaces. + tap: + + # VLAN ID or range(s) of VLAN IDs within range 1-4094. + allowed_vlan: + + # Default tap destination config. + default: + + # Tap group names for the interface. + groups: + - + + # Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. + interfaces: + - + + # Default nexthop-group names. + nexthop_groups: + - + identity: + + # Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). + id: + + # Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). + inner_vlan: + + # Pop all MPLS labels. + mpls_pop_all: + + # Native VLAN ID when interface is in tap mode. + native_vlan: + truncation: + enabled: + + # Ingress packet truncation size in bytes. + size: + mac_address: + + # MAC address for the source. + source: + + # MAC address for the destination. + destination: + encapsulation: + + # Strip VXLAN encapsulation header. + # `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually exclusive. + # `mpls_pop_all` takes precedence. + vxlan_strip: + gre: + + # Strip GRE encapsulation header for all GRE tunnels. + strip: + + # Protocols for all destinations; destination-specific protocols should be set under the `destinations[].protocols` key. + protocols: + + # Protocol type in GRE header. + # Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". + - protocol: + + # This is a required key to strip GRE encapsulation header with protocols. + strip: + + # Feature header length in bytes. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + feature_header_length: + + # Extra ethernet header to prepend to the terminated packet. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + re_encapsulation_ethernet_header: + + # In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are mutually exclusive. + destinations: + + # Destination IP address of tunnel packets. + - destination: + + # Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any GRE packet that matches the `destination` is terminated. + source: + + # Strip GRE encapsulation header for specific destination. + strip: + protocols: + + # Protocol type in GRE header. + # Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". + - protocol: + + # This is a required key to strip GRE encapsulation header for specific destination with protocols. + strip: + + # Feature header length in bytes. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + feature_header_length: + + # Extra ethernet header to prepend to the terminated packet. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + re_encapsulation_ethernet_header: + + # In tool mode, the interface operates as a tool port. + # Tool ports replicate traffic received by tap ports. + # This setting applies only to parent interfaces. + tool: + + # Pop all MPLS labels. + mpls_pop_all: + encapsulation: + + # Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. + dot1br_strip: + + # Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. + vn_tag_strip: + + # VLAN ID or range of VLAN IDs within range 1-4094. + allowed_vlan: + identity: + tag: + dot1q_dzgre_source: + qinq_dzgre_source: + + # Tool groups for the interface. + groups: + - + + # Indices of vlan tags to be removed. + # Range: 1-2 + dot1q_remove_outer_vlan_tag: + # Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. eos_cli: ``` diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md index 2966bf18859..91739aff4a7 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md @@ -375,6 +375,57 @@ | [        vlans](## "port_channel_interfaces.[].switchport.port_security.vlans") | List, items: Dictionary | | | | | | [          - range](## "port_channel_interfaces.[].switchport.port_security.vlans.[].range") | String | Required, Unique | | | VLAN ID or range(s) of VLAN IDs, <1-4094>.
Example:
- 3
- 1,3
- 1-10
| | [            mac_address_maximum](## "port_channel_interfaces.[].switchport.port_security.vlans.[].mac_address_maximum") | Integer | | | | | + | [      tap](## "port_channel_interfaces.[].switchport.tap") | Dictionary | | | | In tap mode, the interface operates as a tap port.
Tap ports receive traffic for replication on one or more tool ports.
This setting applies only to parent interfaces. | + | [        allowed_vlan](## "port_channel_interfaces.[].switchport.tap.allowed_vlan") | String | | | | VLAN ID or range(s) of VLAN IDs within range 1-4094. | + | [        default](## "port_channel_interfaces.[].switchport.tap.default") | Dictionary | | | | Default tap destination config. | + | [          groups](## "port_channel_interfaces.[].switchport.tap.default.groups") | List, items: String | | | | Tap group names for the interface. | + | [            - <str>](## "port_channel_interfaces.[].switchport.tap.default.groups.[]") | String | | | | | + | [          interfaces](## "port_channel_interfaces.[].switchport.tap.default.interfaces") | List, items: String | | | | Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. | + | [            - <str>](## "port_channel_interfaces.[].switchport.tap.default.interfaces.[]") | String | | | | | + | [          nexthop_groups](## "port_channel_interfaces.[].switchport.tap.default.nexthop_groups") | List, items: String | | | | Default nexthop-group names. | + | [            - <str>](## "port_channel_interfaces.[].switchport.tap.default.nexthop_groups.[]") | String | | | | | + | [        identity](## "port_channel_interfaces.[].switchport.tap.identity") | Dictionary | | | | | + | [          id](## "port_channel_interfaces.[].switchport.tap.identity.id") | Integer | | | Min: 1
Max: 65535 | Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). | + | [          inner_vlan](## "port_channel_interfaces.[].switchport.tap.identity.inner_vlan") | Integer | | | Min: 1
Max: 4094 | Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). | + | [        mpls_pop_all](## "port_channel_interfaces.[].switchport.tap.mpls_pop_all") | Boolean | | | | Pop all MPLS labels. | + | [        native_vlan](## "port_channel_interfaces.[].switchport.tap.native_vlan") | Integer | | | Min: 1
Max: 4094 | Native VLAN ID when interface is in tap mode. | + | [        truncation](## "port_channel_interfaces.[].switchport.tap.truncation") | Dictionary | | | | | + | [          enabled](## "port_channel_interfaces.[].switchport.tap.truncation.enabled") | Boolean | | | | | + | [          size](## "port_channel_interfaces.[].switchport.tap.truncation.size") | Integer | | | Min: 100
Max: 9236 | Ingress packet truncation size in bytes. | + | [        mac_address](## "port_channel_interfaces.[].switchport.tap.mac_address") | Dictionary | | | | | + | [          source](## "port_channel_interfaces.[].switchport.tap.mac_address.source") | String | | | Pattern: `^([0-9a-f]{2}:){5}[0-9a-f]{2}$` | MAC address for the source. | + | [          destination](## "port_channel_interfaces.[].switchport.tap.mac_address.destination") | String | | | Pattern: `^([0-9a-f]{2}:){5}[0-9a-f]{2}$` | MAC address for the destination. | + | [        encapsulation](## "port_channel_interfaces.[].switchport.tap.encapsulation") | Dictionary | | | | | + | [          vxlan_strip](## "port_channel_interfaces.[].switchport.tap.encapsulation.vxlan_strip") | Boolean | | | | Strip VXLAN encapsulation header.
`encapsulation.vxlan_strip` and `mpls_pop_all` are mutually exclusive.
`mpls_pop_all` takes precedence. | + | [          gre](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre") | Dictionary | | | | | + | [            strip](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.strip") | Boolean | | | | Strip GRE encapsulation header for all GRE tunnels. | + | [            protocols](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.protocols") | List, items: Dictionary | | | | Protocols for all destinations; destination-specific protocols should be set under the `destinations[].protocols` key. | + | [              - protocol](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].protocol") | String | Required, Unique | | | Protocol type in GRE header.
Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". | + | [                strip](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].strip") | Boolean | | | | This is a required key to strip GRE encapsulation header with protocols. | + | [                feature_header_length](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].feature_header_length") | Integer | | | Min: 1
Max: 16 | Feature header length in bytes.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [                re_encapsulation_ethernet_header](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.protocols.[].re_encapsulation_ethernet_header") | Boolean | | | | Extra ethernet header to prepend to the terminated packet.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [            destinations](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations") | List, items: Dictionary | | | | In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are mutually exclusive. | + | [              - destination](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].destination") | String | Required, Unique | | | Destination IP address of tunnel packets. | + | [                source](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].source") | String | | | | Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any GRE packet that matches the `destination` is terminated. | + | [                strip](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].strip") | Boolean | | | | Strip GRE encapsulation header for specific destination. | + | [                protocols](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols") | List, items: Dictionary | | | | | + | [                  - protocol](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].protocol") | String | Required, Unique | | | Protocol type in GRE header.
Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". | + | [                    strip](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].strip") | Boolean | | | | This is a required key to strip GRE encapsulation header for specific destination with protocols. | + | [                    feature_header_length](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].feature_header_length") | Integer | | | Min: 1
Max: 16 | Feature header length in bytes.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [                    re_encapsulation_ethernet_header](## "port_channel_interfaces.[].switchport.tap.encapsulation.gre.destinations.[].protocols.[].re_encapsulation_ethernet_header") | Boolean | | | | Extra ethernet header to prepend to the terminated packet.
Note: This setting does not appear in the EOS running-config for protocol 0x0. | + | [      tool](## "port_channel_interfaces.[].switchport.tool") | Dictionary | | | | In tool mode, the interface operates as a tool port.
Tool ports replicate traffic received by tap ports.
This setting applies only to parent interfaces. | + | [        mpls_pop_all](## "port_channel_interfaces.[].switchport.tool.mpls_pop_all") | Boolean | | | | Pop all MPLS labels. | + | [        encapsulation](## "port_channel_interfaces.[].switchport.tool.encapsulation") | Dictionary | | | | | + | [          dot1br_strip](## "port_channel_interfaces.[].switchport.tool.encapsulation.dot1br_strip") | Boolean | | | | Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. | + | [          vn_tag_strip](## "port_channel_interfaces.[].switchport.tool.encapsulation.vn_tag_strip") | Boolean | | | | Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. | + | [        allowed_vlan](## "port_channel_interfaces.[].switchport.tool.allowed_vlan") | String | | | | VLAN ID or range of VLAN IDs within range 1-4094. | + | [        identity](## "port_channel_interfaces.[].switchport.tool.identity") | Dictionary | | | | | + | [          tag](## "port_channel_interfaces.[].switchport.tool.identity.tag") | String | | | Valid Values:
- dot1q
- qinq | | + | [          dot1q_dzgre_source](## "port_channel_interfaces.[].switchport.tool.identity.dot1q_dzgre_source") | String | | | Valid Values:
- policy
- port | | + | [          qinq_dzgre_source](## "port_channel_interfaces.[].switchport.tool.identity.qinq_dzgre_source") | String | | | Valid Values:
- policy inner port
- port inner policy | | + | [        groups](## "port_channel_interfaces.[].switchport.tool.groups") | List, items: String | | | | Tool groups for the interface. | + | [          - <str>](## "port_channel_interfaces.[].switchport.tool.groups.[]") | String | | | | | + | [        dot1q_remove_outer_vlan_tag](## "port_channel_interfaces.[].switchport.tool.dot1q_remove_outer_vlan_tag") | String | | | | Indices of vlan tags to be removed.
Range: 1-2 | | [    validate_state](## "port_channel_interfaces.[].validate_state") | Boolean | | | | Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. | | [    validate_lldp](## "port_channel_interfaces.[].validate_lldp") | Boolean | | | | Set to false to disable the LLDP topology validation performed by the `eos_validate_state` role. | | [    eos_cli](## "port_channel_interfaces.[].eos_cli") | String | | | | Multiline EOS CLI rendered directly on the port-channel interface in the final EOS configuration. | @@ -1223,6 +1274,140 @@ - range: mac_address_maximum: + # In tap mode, the interface operates as a tap port. + # Tap ports receive traffic for replication on one or more tool ports. + # This setting applies only to parent interfaces. + tap: + + # VLAN ID or range(s) of VLAN IDs within range 1-4094. + allowed_vlan: + + # Default tap destination config. + default: + + # Tap group names for the interface. + groups: + - + + # Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. + interfaces: + - + + # Default nexthop-group names. + nexthop_groups: + - + identity: + + # Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). + id: + + # Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). + inner_vlan: + + # Pop all MPLS labels. + mpls_pop_all: + + # Native VLAN ID when interface is in tap mode. + native_vlan: + truncation: + enabled: + + # Ingress packet truncation size in bytes. + size: + mac_address: + + # MAC address for the source. + source: + + # MAC address for the destination. + destination: + encapsulation: + + # Strip VXLAN encapsulation header. + # `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually exclusive. + # `mpls_pop_all` takes precedence. + vxlan_strip: + gre: + + # Strip GRE encapsulation header for all GRE tunnels. + strip: + + # Protocols for all destinations; destination-specific protocols should be set under the `destinations[].protocols` key. + protocols: + + # Protocol type in GRE header. + # Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". + - protocol: + + # This is a required key to strip GRE encapsulation header with protocols. + strip: + + # Feature header length in bytes. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + feature_header_length: + + # Extra ethernet header to prepend to the terminated packet. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + re_encapsulation_ethernet_header: + + # In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are mutually exclusive. + destinations: + + # Destination IP address of tunnel packets. + - destination: + + # Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any GRE packet that matches the `destination` is terminated. + source: + + # Strip GRE encapsulation header for specific destination. + strip: + protocols: + + # Protocol type in GRE header. + # Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". + - protocol: + + # This is a required key to strip GRE encapsulation header for specific destination with protocols. + strip: + + # Feature header length in bytes. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + feature_header_length: + + # Extra ethernet header to prepend to the terminated packet. + # Note: This setting does not appear in the EOS running-config for protocol 0x0. + re_encapsulation_ethernet_header: + + # In tool mode, the interface operates as a tool port. + # Tool ports replicate traffic received by tap ports. + # This setting applies only to parent interfaces. + tool: + + # Pop all MPLS labels. + mpls_pop_all: + encapsulation: + + # Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. + dot1br_strip: + + # Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. + vn_tag_strip: + + # VLAN ID or range of VLAN IDs within range 1-4094. + allowed_vlan: + identity: + tag: + dot1q_dzgre_source: + qinq_dzgre_source: + + # Tool groups for the interface. + groups: + - + + # Indices of vlan tags to be removed. + # Range: 1-2 + dot1q_remove_outer_vlan_tag: + # Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. validate_state: diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 index 93c177d3056..9467cd18e05 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 @@ -990,6 +990,119 @@ interface {{ ethernet_interface.name }} priority {{ ethernet_interface.sync_e.priority }} {% endif %} {% endif %} +{% if ethernet_interface.switchport.tap is arista.avd.defined or ethernet_interface.switchport.tool is arista.avd.defined %} +{% if ethernet_interface.switchport.tap.native_vlan is arista.avd.defined %} + switchport tap native vlan {{ ethernet_interface.switchport.tap.native_vlan }} +{% endif %} +{% if ethernet_interface.switchport.tap.identity.id is arista.avd.defined %} +{% set tap_identity_cli = "switchport tap identity " ~ ethernet_interface.switchport.tap.identity.id %} +{% if ethernet_interface.switchport.tap.identity.inner_vlan is arista.avd.defined %} +{% set tap_identity_cli = tap_identity_cli ~ " inner " ~ ethernet_interface.switchport.tap.identity.inner_vlan %} +{% endif %} + {{ tap_identity_cli }} +{% endif %} +{% if ethernet_interface.switchport.tap.mac_address.destination is arista.avd.defined %} +{% set tap_mac_address_cli = "switchport tap mac-address dest " ~ ethernet_interface.switchport.tap.mac_address.destination %} +{% if ethernet_interface.switchport.tap.mac_address.source is arista.avd.defined %} +{% set tap_mac_address_cli = tap_mac_address_cli ~ " src " ~ ethernet_interface.switchport.tap.mac_address.source %} +{% endif %} + {{ tap_mac_address_cli }} +{% endif %} +{% if ethernet_interface.switchport.tap.encapsulation.vxlan_strip is arista.avd.defined(true) and ethernet_interface.switchport.tap.mpls_pop_all is not arista.avd.defined(true) %} + switchport tap encapsulation vxlan strip +{% endif %} +{% for protocol in ethernet_interface.switchport.tap.encapsulation.gre.protocols | arista.avd.natural_sort('protocol') %} +{% if protocol.strip is arista.avd.defined(true) %} +{% set tap_encapsulation_cli = "switchport tap encapsulation gre protocol " ~ protocol.protocol %} +{% if protocol.feature_header_length is arista.avd.defined %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " feature header length " ~ protocol.feature_header_length %} +{% endif %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " strip" %} +{% if protocol.re_encapsulation_ethernet_header is arista.avd.defined(true) %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " re-encapsulation ethernet" %} +{% endif %} + {{ tap_encapsulation_cli }} +{% endif %} +{% endfor %} +{% if ethernet_interface.switchport.tap.encapsulation.gre.strip is arista.avd.defined(true) %} + switchport tap encapsulation gre strip +{% endif %} +{% for destination in ethernet_interface.switchport.tap.encapsulation.gre.destinations | arista.avd.natural_sort('destination') %} +{% set tap_encapsulation_cli = "switchport tap encapsulation gre destination " ~ destination.destination %} +{% if destination.source is arista.avd.defined %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " source " ~ destination.source %} +{% endif %} +{% for destination_protocol in destination.protocols | arista.avd.natural_sort('protocol') %} +{% if destination_protocol.strip is arista.avd.defined(true) %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_cli ~ " protocol " ~ destination_protocol.protocol %} +{% if destination_protocol.feature_header_length is arista.avd.defined %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_protocol_cli ~ " feature header length " ~ destination_protocol.feature_header_length %} +{% endif %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_protocol_cli ~ " strip" %} +{% if destination_protocol.re_encapsulation_ethernet_header is arista.avd.defined(true) %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_protocol_cli ~ " re-encapsulation ethernet" %} +{% endif %} + {{ tap_encapsulation_protocol_cli }} +{% endif %} +{% endfor %} +{% if destination.strip is arista.avd.defined(true) %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " strip" %} + {{ tap_encapsulation_cli }} +{% endif %} +{% endfor %} +{% if ethernet_interface.switchport.tap.mpls_pop_all is arista.avd.defined(true) %} + switchport tap mpls pop all +{% endif %} +{% if ethernet_interface.switchport.tool.mpls_pop_all is arista.avd.defined(true) %} + switchport tool mpls pop all +{% endif %} +{% if ethernet_interface.switchport.tool.encapsulation.vn_tag_strip is arista.avd.defined(true) %} + switchport tool encapsulation vn-tag strip +{% endif %} +{% if ethernet_interface.switchport.tool.encapsulation.dot1br_strip is arista.avd.defined(true) %} + switchport tool encapsulation dot1br strip +{% endif %} +{% if ethernet_interface.switchport.tap.allowed_vlan is arista.avd.defined %} + switchport tap allowed vlan {{ ethernet_interface.switchport.tap.allowed_vlan }} +{% endif %} +{% if ethernet_interface.switchport.tool.allowed_vlan is arista.avd.defined %} + switchport tool allowed vlan {{ ethernet_interface.switchport.tool.allowed_vlan }} +{% endif %} +{% if ethernet_interface.switchport.tool.identity.tag is arista.avd.defined %} + switchport tool identity {{ ethernet_interface.switchport.tool.identity.tag }} +{% endif %} +{% if ethernet_interface.switchport.tool.identity.dot1q_dzgre_source is arista.avd.defined %} + switchport tool identity dot1q source dzgre {{ ethernet_interface.switchport.tool.identity.dot1q_dzgre_source }} +{% elif ethernet_interface.switchport.tool.identity.qinq_dzgre_source is arista.avd.defined %} + switchport tool identity qinq source dzgre {{ ethernet_interface.switchport.tool.identity.qinq_dzgre_source }} +{% endif %} +{% if ethernet_interface.switchport.tap.truncation.enabled is arista.avd.defined(true) %} +{% set tap_truncation_cli = "switchport tap truncation" %} +{% if ethernet_interface.switchport.tap.truncation.size is arista.avd.defined %} +{% set tap_truncation_cli = tap_truncation_cli ~ " " ~ ethernet_interface.switchport.tap.truncation.size %} +{% endif %} + {{ tap_truncation_cli }} +{% endif %} +{% if ethernet_interface.switchport.tap.default.groups is arista.avd.defined %} + switchport tap default group {{ ethernet_interface.switchport.tap.default.groups | arista.avd.natural_sort | join(" group ") }} +{% endif %} +{% if ethernet_interface.switchport.tap.default.nexthop_groups is arista.avd.defined %} + switchport tap default nexthop-group {{ ethernet_interface.switchport.tap.default.nexthop_groups | arista.avd.natural_sort | join(" ") }} +{% endif %} +{% for interface in ethernet_interface.switchport.tap.default.interfaces | arista.avd.natural_sort %} + switchport tap default interface {{ interface }} +{% endfor %} +{% if ethernet_interface.switchport.tool.groups is arista.avd.defined %} +{% set tool_groups = ethernet_interface.switchport.tool.groups | arista.avd.natural_sort | join(" ") %} + switchport tool group set {{ tool_groups }} +{% endif %} +{% if ethernet_interface.switchport.tool.dot1q_remove_outer_vlan_tag is arista.avd.defined %} + switchport tool dot1q remove outer {{ ethernet_interface.switchport.tool.dot1q_remove_outer_vlan_tag }} +{% endif %} +{% if ethernet_interface.switchport.tool.dzgre_preserve is arista.avd.defined(true) %} + switchport tool dzgre preserve +{% endif %} +{% endif %} {% for link_tracking_group in ethernet_interface.link_tracking_groups | arista.avd.natural_sort %} {% if link_tracking_group.name is arista.avd.defined and link_tracking_group.direction is arista.avd.defined %} link tracking group {{ link_tracking_group.name }} {{ link_tracking_group.direction }} diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 index 2184b778ab4..25cdb756206 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 @@ -813,6 +813,116 @@ interface {{ port_channel_interface.name }} switchport backup dest-macaddr {{ port_channel_interface.switchport.backup.dest_macaddr }} {% endif %} {% endif %} +{% if port_channel_interface.switchport.tap is arista.avd.defined or port_channel_interface.switchport.tool is arista.avd.defined %} +{% if port_channel_interface.switchport.tap.native_vlan is arista.avd.defined %} + switchport tap native vlan {{ port_channel_interface.switchport.tap.native_vlan }} +{% endif %} +{% if port_channel_interface.switchport.tap.identity.id is arista.avd.defined %} +{% set tap_identity_cli = "switchport tap identity " ~ port_channel_interface.switchport.tap.identity.id %} +{% if port_channel_interface.switchport.tap.identity.inner_vlan is arista.avd.defined %} +{% set tap_identity_cli = tap_identity_cli ~ " inner " ~ port_channel_interface.switchport.tap.identity.inner_vlan %} +{% endif %} + {{ tap_identity_cli }} +{% endif %} +{% if port_channel_interface.switchport.tap.mac_address.destination is arista.avd.defined %} +{% set tap_mac_address_cli = "switchport tap mac-address dest " ~ port_channel_interface.switchport.tap.mac_address.destination %} +{% if port_channel_interface.switchport.tap.mac_address.source is arista.avd.defined %} +{% set tap_mac_address_cli = tap_mac_address_cli ~ " src " ~ port_channel_interface.switchport.tap.mac_address.source %} +{% endif %} + {{ tap_mac_address_cli }} +{% endif %} +{% if port_channel_interface.switchport.tap.encapsulation.vxlan_strip is arista.avd.defined(true) and port_channel_interface.switchport.tap.mpls_pop_all is not arista.avd.defined(true) %} + switchport tap encapsulation vxlan strip +{% endif %} +{% for protocol in port_channel_interface.switchport.tap.encapsulation.gre.protocols | arista.avd.natural_sort('protocol') %} +{% if protocol.strip is arista.avd.defined(true) %} +{% set tap_encapsulation_cli = "switchport tap encapsulation gre protocol " ~ protocol.protocol %} +{% if protocol.feature_header_length is arista.avd.defined %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " feature header length " ~ protocol.feature_header_length %} +{% endif %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " strip" %} +{% if protocol.re_encapsulation_ethernet_header is arista.avd.defined(true) %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " re-encapsulation ethernet" %} +{% endif %} + {{ tap_encapsulation_cli }} +{% endif %} +{% endfor %} +{% if port_channel_interface.switchport.tap.encapsulation.gre.strip is arista.avd.defined(true) %} + switchport tap encapsulation gre strip +{% endif %} +{% for destination in port_channel_interface.switchport.tap.encapsulation.gre.destinations | arista.avd.natural_sort('destination') %} +{% set tap_encapsulation_cli = "switchport tap encapsulation gre destination " ~ destination.destination %} +{% if destination.source is arista.avd.defined %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " source " ~ destination.source %} +{% endif %} +{% for destination_protocol in destination.protocols | arista.avd.natural_sort('protocol') %} +{% if destination_protocol.strip is arista.avd.defined(true) %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_cli ~ " protocol " ~ destination_protocol.protocol %} +{% if destination_protocol.feature_header_length is arista.avd.defined %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_protocol_cli ~ " feature header length " ~ destination_protocol.feature_header_length %} +{% endif %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_protocol_cli ~ " strip" %} +{% if destination_protocol.re_encapsulation_ethernet_header is arista.avd.defined(true) %} +{% set tap_encapsulation_protocol_cli = tap_encapsulation_protocol_cli ~ " re-encapsulation ethernet" %} +{% endif %} + {{ tap_encapsulation_protocol_cli }} +{% endif %} +{% endfor %} +{% if destination.strip is arista.avd.defined(true) %} +{% set tap_encapsulation_cli = tap_encapsulation_cli ~ " strip" %} + {{ tap_encapsulation_cli }} +{% endif %} +{% endfor %} +{% if port_channel_interface.switchport.tap.mpls_pop_all is arista.avd.defined(true) %} + switchport tap mpls pop all +{% endif %} +{% if port_channel_interface.switchport.tool.mpls_pop_all is arista.avd.defined(true) %} + switchport tool mpls pop all +{% endif %} +{% if port_channel_interface.switchport.tool.encapsulation.vn_tag_strip is arista.avd.defined(true) %} + switchport tool encapsulation vn-tag strip +{% endif %} +{% if port_channel_interface.switchport.tool.encapsulation.dot1br_strip is arista.avd.defined(true) %} + switchport tool encapsulation dot1br strip +{% endif %} +{% if port_channel_interface.switchport.tap.allowed_vlan is arista.avd.defined %} + switchport tap allowed vlan {{ port_channel_interface.switchport.tap.allowed_vlan }} +{% endif %} +{% if port_channel_interface.switchport.tool.allowed_vlan is arista.avd.defined %} + switchport tool allowed vlan {{ port_channel_interface.switchport.tool.allowed_vlan }} +{% endif %} +{% if port_channel_interface.switchport.tool.identity.tag is arista.avd.defined %} + switchport tool identity {{ port_channel_interface.switchport.tool.identity.tag }} +{% endif %} +{% if port_channel_interface.switchport.tool.identity.dot1q_dzgre_source is arista.avd.defined %} + switchport tool identity dot1q source dzgre {{ port_channel_interface.switchport.tool.identity.dot1q_dzgre_source }} +{% elif port_channel_interface.switchport.tool.identity.qinq_dzgre_source is arista.avd.defined %} + switchport tool identity qinq source dzgre {{ port_channel_interface.switchport.tool.identity.qinq_dzgre_source }} +{% endif %} +{% if port_channel_interface.switchport.tap.truncation.enabled is arista.avd.defined(true) %} +{% set tap_truncation_cli = "switchport tap truncation" %} +{% if port_channel_interface.switchport.tap.truncation.size is arista.avd.defined %} +{% set tap_truncation_cli = tap_truncation_cli ~ " " ~ port_channel_interface.switchport.tap.truncation.size %} +{% endif %} + {{ tap_truncation_cli }} +{% endif %} +{% if port_channel_interface.switchport.tap.default.groups is arista.avd.defined %} + switchport tap default group {{ port_channel_interface.switchport.tap.default.groups | arista.avd.natural_sort | join(" group ") }} +{% endif %} +{% if port_channel_interface.switchport.tap.default.nexthop_groups is arista.avd.defined %} + switchport tap default nexthop-group {{ port_channel_interface.switchport.tap.default.nexthop_groups | arista.avd.natural_sort | join(" ") }} +{% endif %} +{% for interface in port_channel_interface.switchport.tap.default.interfaces | arista.avd.natural_sort %} + switchport tap default interface {{ interface }} +{% endfor %} +{% if port_channel_interface.switchport.tool.groups is arista.avd.defined %} +{% set tool_groups = port_channel_interface.switchport.tool.groups | arista.avd.natural_sort | join(" ") %} + switchport tool group set {{ tool_groups }} +{% endif %} +{% if port_channel_interface.switchport.tool.dot1q_remove_outer_vlan_tag is arista.avd.defined %} + switchport tool dot1q remove outer {{ port_channel_interface.switchport.tool.dot1q_remove_outer_vlan_tag }} +{% endif %} +{% endif %} {% for link_tracking_group in port_channel_interface.link_tracking_groups | arista.avd.natural_sort('name') %} {% if link_tracking_group.name is arista.avd.defined and link_tracking_group.direction is arista.avd.defined %} link tracking group {{ link_tracking_group.name }} {{ link_tracking_group.direction }} diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py index d2f8786e347..a40a32fa721 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py @@ -11317,6 +11317,706 @@ def __init__( """ + class Tap(AvdModel): + """Subclass of AvdModel.""" + + class Default(AvdModel): + """Subclass of AvdModel.""" + + class Groups(AvdList[str]): + """Subclass of AvdList with `str` items.""" + + Groups._item_type = str + + class Interfaces(AvdList[str]): + """Subclass of AvdList with `str` items.""" + + Interfaces._item_type = str + + class NexthopGroups(AvdList[str]): + """Subclass of AvdList with `str` items.""" + + NexthopGroups._item_type = str + + _fields: ClassVar[dict] = { + "groups": {"type": Groups}, + "interfaces": {"type": Interfaces}, + "nexthop_groups": {"type": NexthopGroups}, + "_custom_data": {"type": dict}, + } + groups: Groups + """ + Tap group names for the interface. + + Subclass of AvdList with `str` items. + """ + interfaces: Interfaces + """ + Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. + + Subclass of AvdList + with `str` items. + """ + nexthop_groups: NexthopGroups + """ + Default nexthop-group names. + + Subclass of AvdList with `str` items. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + groups: Groups | UndefinedType = Undefined, + interfaces: Interfaces | UndefinedType = Undefined, + nexthop_groups: NexthopGroups | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Default. + + + Subclass of AvdModel. + + Args: + groups: + Tap group names for the interface. + + Subclass of AvdList with `str` items. + interfaces: + Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. + + Subclass of AvdList + with `str` items. + nexthop_groups: + Default nexthop-group names. + + Subclass of AvdList with `str` items. + _custom_data: _custom_data + + """ + + class Identity(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"id": {"type": int}, "inner_vlan": {"type": int}, "_custom_data": {"type": dict}} + id: int | None + """Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535).""" + inner_vlan: int | None + """Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094).""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + id: int | None | UndefinedType = Undefined, + inner_vlan: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Identity. + + + Subclass of AvdModel. + + Args: + id: Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). + inner_vlan: Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). + _custom_data: _custom_data + + """ + + class Truncation(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"enabled": {"type": bool}, "size": {"type": int}, "_custom_data": {"type": dict}} + enabled: bool | None + size: int | None + """Ingress packet truncation size in bytes.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + enabled: bool | None | UndefinedType = Undefined, + size: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Truncation. + + + Subclass of AvdModel. + + Args: + enabled: enabled + size: Ingress packet truncation size in bytes. + _custom_data: _custom_data + + """ + + class MacAddress(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"source": {"type": str}, "destination": {"type": str}, "_custom_data": {"type": dict}} + source: str | None + """MAC address for the source.""" + destination: str | None + """MAC address for the destination.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + source: str | None | UndefinedType = Undefined, + destination: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + MacAddress. + + + Subclass of AvdModel. + + Args: + source: MAC address for the source. + destination: MAC address for the destination. + _custom_data: _custom_data + + """ + + class Encapsulation(AvdModel): + """Subclass of AvdModel.""" + + class Gre(AvdModel): + """Subclass of AvdModel.""" + + class ProtocolsItem(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = { + "protocol": {"type": str}, + "strip": {"type": bool}, + "feature_header_length": {"type": int}, + "re_encapsulation_ethernet_header": {"type": bool}, + "_custom_data": {"type": dict}, + } + protocol: str + """ + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + """ + strip: bool | None + """This is a required key to strip GRE encapsulation header with protocols.""" + feature_header_length: int | None + """ + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + """ + re_encapsulation_ethernet_header: bool | None + """ + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + protocol: str | UndefinedType = Undefined, + strip: bool | None | UndefinedType = Undefined, + feature_header_length: int | None | UndefinedType = Undefined, + re_encapsulation_ethernet_header: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + ProtocolsItem. + + + Subclass of AvdModel. + + Args: + protocol: + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + strip: This is a required key to strip GRE encapsulation header with protocols. + feature_header_length: + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + re_encapsulation_ethernet_header: + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + _custom_data: _custom_data + + """ + + class Protocols(AvdIndexedList[str, ProtocolsItem]): + """Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`).""" + + _primary_key: ClassVar[str] = "protocol" + + Protocols._item_type = ProtocolsItem + + class DestinationsItem(AvdModel): + """Subclass of AvdModel.""" + + class ProtocolsItem(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = { + "protocol": {"type": str}, + "strip": {"type": bool}, + "feature_header_length": {"type": int}, + "re_encapsulation_ethernet_header": {"type": bool}, + "_custom_data": {"type": dict}, + } + protocol: str + """ + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + """ + strip: bool | None + """This is a required key to strip GRE encapsulation header for specific destination with protocols.""" + feature_header_length: int | None + """ + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + """ + re_encapsulation_ethernet_header: bool | None + """ + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + protocol: str | UndefinedType = Undefined, + strip: bool | None | UndefinedType = Undefined, + feature_header_length: int | None | UndefinedType = Undefined, + re_encapsulation_ethernet_header: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + ProtocolsItem. + + + Subclass of AvdModel. + + Args: + protocol: + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + strip: This is a required key to strip GRE encapsulation header for specific destination with protocols. + feature_header_length: + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + re_encapsulation_ethernet_header: + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + _custom_data: _custom_data + + """ + + class Protocols(AvdIndexedList[str, ProtocolsItem]): + """Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`).""" + + _primary_key: ClassVar[str] = "protocol" + + Protocols._item_type = ProtocolsItem + + _fields: ClassVar[dict] = { + "destination": {"type": str}, + "source": {"type": str}, + "strip": {"type": bool}, + "protocols": {"type": Protocols}, + "_custom_data": {"type": dict}, + } + destination: str + """Destination IP address of tunnel packets.""" + source: str | None + """ + Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any + GRE packet that matches the `destination` is terminated. + """ + strip: bool | None + """Strip GRE encapsulation header for specific destination.""" + protocols: Protocols + """Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`).""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + destination: str | UndefinedType = Undefined, + source: str | None | UndefinedType = Undefined, + strip: bool | None | UndefinedType = Undefined, + protocols: Protocols | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + DestinationsItem. + + + Subclass of AvdModel. + + Args: + destination: Destination IP address of tunnel packets. + source: + Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any + GRE packet that matches the `destination` is terminated. + strip: Strip GRE encapsulation header for specific destination. + protocols: Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`). + _custom_data: _custom_data + + """ + + class Destinations(AvdIndexedList[str, DestinationsItem]): + """Subclass of AvdIndexedList with `DestinationsItem` items. Primary key is `destination` (`str`).""" + + _primary_key: ClassVar[str] = "destination" + + Destinations._item_type = DestinationsItem + + _fields: ClassVar[dict] = { + "strip": {"type": bool}, + "protocols": {"type": Protocols}, + "destinations": {"type": Destinations}, + "_custom_data": {"type": dict}, + } + strip: bool | None + """Strip GRE encapsulation header for all GRE tunnels.""" + protocols: Protocols + """ + Protocols for all destinations; destination-specific protocols should be set under the + `destinations[].protocols` key. + + Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key + is `protocol` (`str`). + """ + destinations: Destinations + """ + In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are + mutually exclusive. + + Subclass of AvdIndexedList with `DestinationsItem` items. Primary key is + `destination` (`str`). + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + strip: bool | None | UndefinedType = Undefined, + protocols: Protocols | UndefinedType = Undefined, + destinations: Destinations | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Gre. + + + Subclass of AvdModel. + + Args: + strip: Strip GRE encapsulation header for all GRE tunnels. + protocols: + Protocols for all destinations; destination-specific protocols should be set under the + `destinations[].protocols` key. + + Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key + is `protocol` (`str`). + destinations: + In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are + mutually exclusive. + + Subclass of AvdIndexedList with `DestinationsItem` items. Primary key is + `destination` (`str`). + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = {"vxlan_strip": {"type": bool}, "gre": {"type": Gre}, "_custom_data": {"type": dict}} + vxlan_strip: bool | None + """ + Strip VXLAN encapsulation header. + `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually + exclusive. + `mpls_pop_all` takes precedence. + """ + gre: Gre + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + vxlan_strip: bool | None | UndefinedType = Undefined, + gre: Gre | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Encapsulation. + + + Subclass of AvdModel. + + Args: + vxlan_strip: + Strip VXLAN encapsulation header. + `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually + exclusive. + `mpls_pop_all` takes precedence. + gre: Subclass of AvdModel. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = { + "allowed_vlan": {"type": str}, + "default": {"type": Default}, + "identity": {"type": Identity}, + "mpls_pop_all": {"type": bool}, + "native_vlan": {"type": int}, + "truncation": {"type": Truncation}, + "mac_address": {"type": MacAddress}, + "encapsulation": {"type": Encapsulation}, + "_custom_data": {"type": dict}, + } + allowed_vlan: str | None + """VLAN ID or range(s) of VLAN IDs within range 1-4094.""" + default: Default + """ + Default tap destination config. + + Subclass of AvdModel. + """ + identity: Identity + """Subclass of AvdModel.""" + mpls_pop_all: bool | None + """Pop all MPLS labels.""" + native_vlan: int | None + """Native VLAN ID when interface is in tap mode.""" + truncation: Truncation + """Subclass of AvdModel.""" + mac_address: MacAddress + """Subclass of AvdModel.""" + encapsulation: Encapsulation + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + allowed_vlan: str | None | UndefinedType = Undefined, + default: Default | UndefinedType = Undefined, + identity: Identity | UndefinedType = Undefined, + mpls_pop_all: bool | None | UndefinedType = Undefined, + native_vlan: int | None | UndefinedType = Undefined, + truncation: Truncation | UndefinedType = Undefined, + mac_address: MacAddress | UndefinedType = Undefined, + encapsulation: Encapsulation | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Tap. + + + Subclass of AvdModel. + + Args: + allowed_vlan: VLAN ID or range(s) of VLAN IDs within range 1-4094. + default: + Default tap destination config. + + Subclass of AvdModel. + identity: Subclass of AvdModel. + mpls_pop_all: Pop all MPLS labels. + native_vlan: Native VLAN ID when interface is in tap mode. + truncation: Subclass of AvdModel. + mac_address: Subclass of AvdModel. + encapsulation: Subclass of AvdModel. + _custom_data: _custom_data + + """ + + class Tool(AvdModel): + """Subclass of AvdModel.""" + + class Encapsulation(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"dot1br_strip": {"type": bool}, "vn_tag_strip": {"type": bool}, "_custom_data": {"type": dict}} + dot1br_strip: bool | None + """Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS.""" + vn_tag_strip: bool | None + """Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + dot1br_strip: bool | None | UndefinedType = Undefined, + vn_tag_strip: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Encapsulation. + + + Subclass of AvdModel. + + Args: + dot1br_strip: Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. + vn_tag_strip: Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. + _custom_data: _custom_data + + """ + + class Identity(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = { + "tag": {"type": str}, + "dot1q_dzgre_source": {"type": str}, + "qinq_dzgre_source": {"type": str}, + "_custom_data": {"type": dict}, + } + tag: Literal["dot1q", "qinq"] | None + dot1q_dzgre_source: Literal["policy", "port"] | None + qinq_dzgre_source: Literal["policy inner port", "port inner policy"] | None + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + tag: Literal["dot1q", "qinq"] | None | UndefinedType = Undefined, + dot1q_dzgre_source: Literal["policy", "port"] | None | UndefinedType = Undefined, + qinq_dzgre_source: Literal["policy inner port", "port inner policy"] | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Identity. + + + Subclass of AvdModel. + + Args: + tag: tag + dot1q_dzgre_source: dot1q_dzgre_source + qinq_dzgre_source: qinq_dzgre_source + _custom_data: _custom_data + + """ + + class Groups(AvdList[str]): + """Subclass of AvdList with `str` items.""" + + Groups._item_type = str + + _fields: ClassVar[dict] = { + "mpls_pop_all": {"type": bool}, + "encapsulation": {"type": Encapsulation}, + "allowed_vlan": {"type": str}, + "identity": {"type": Identity}, + "groups": {"type": Groups}, + "dot1q_remove_outer_vlan_tag": {"type": str}, + "_custom_data": {"type": dict}, + } + mpls_pop_all: bool | None + """Pop all MPLS labels.""" + encapsulation: Encapsulation + """Subclass of AvdModel.""" + allowed_vlan: str | None + """VLAN ID or range of VLAN IDs within range 1-4094.""" + identity: Identity + """Subclass of AvdModel.""" + groups: Groups + """ + Tool groups for the interface. + + Subclass of AvdList with `str` items. + """ + dot1q_remove_outer_vlan_tag: str | None + """ + Indices of vlan tags to be removed. + Range: 1-2 + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + mpls_pop_all: bool | None | UndefinedType = Undefined, + encapsulation: Encapsulation | UndefinedType = Undefined, + allowed_vlan: str | None | UndefinedType = Undefined, + identity: Identity | UndefinedType = Undefined, + groups: Groups | UndefinedType = Undefined, + dot1q_remove_outer_vlan_tag: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Tool. + + + Subclass of AvdModel. + + Args: + mpls_pop_all: Pop all MPLS labels. + encapsulation: Subclass of AvdModel. + allowed_vlan: VLAN ID or range of VLAN IDs within range 1-4094. + identity: Subclass of AvdModel. + groups: + Tool groups for the interface. + + Subclass of AvdList with `str` items. + dot1q_remove_outer_vlan_tag: + Indices of vlan tags to be removed. + Range: 1-2 + _custom_data: _custom_data + + """ + _fields: ClassVar[dict] = { "enabled": {"type": bool}, "mode": {"type": str}, @@ -11331,6 +12031,8 @@ def __init__( "backup_link": {"type": BackupLink}, "backup": {"type": Backup}, "port_security": {"type": PortSecurity}, + "tap": {"type": Tap}, + "tool": {"type": Tool}, "_custom_data": {"type": dict}, } enabled: bool | None @@ -11384,6 +12086,24 @@ def __init__( """ port_security: PortSecurity """Subclass of AvdModel.""" + tap: Tap + """ + In tap mode, the interface operates as a tap port. + Tap ports receive traffic for replication on one + or more tool ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. + """ + tool: Tool + """ + In tool mode, the interface operates as a tool port. + Tool ports replicate traffic received by tap + ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. + """ _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -11404,6 +12124,8 @@ def __init__( backup_link: BackupLink | UndefinedType = Undefined, backup: Backup | UndefinedType = Undefined, port_security: PortSecurity | UndefinedType = Undefined, + tap: Tap | UndefinedType = Undefined, + tool: Tool | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -11446,6 +12168,20 @@ def __init__( Subclass of AvdModel. port_security: Subclass of AvdModel. + tap: + In tap mode, the interface operates as a tap port. + Tap ports receive traffic for replication on one + or more tool ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. + tool: + In tool mode, the interface operates as a tool port. + Tool ports replicate traffic received by tap + ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. _custom_data: _custom_data """ @@ -27481,24 +28217,262 @@ def __init__( copp_system_policy: Control-plane policy configuration. - Subclass of AvdModel. - _custom_data: _custom_data + Subclass of AvdModel. + _custom_data: _custom_data + + """ + + class PortChannelInterfacesItem(AvdModel): + """Subclass of AvdModel.""" + + class Logging(AvdModel): + """Subclass of AvdModel.""" + + class Event(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"link_status": {"type": bool}, "storm_control_discards": {"type": bool}, "_custom_data": {"type": dict}} + link_status: bool | None + storm_control_discards: bool | None + """Discards due to storm-control.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + link_status: bool | None | UndefinedType = Undefined, + storm_control_discards: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Event. + + + Subclass of AvdModel. + + Args: + link_status: link_status + storm_control_discards: Discards due to storm-control. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = {"event": {"type": Event}, "_custom_data": {"type": dict}} + event: Event + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__(self, *, event: Event | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Logging. + + + Subclass of AvdModel. + + Args: + event: Subclass of AvdModel. + _custom_data: _custom_data + + """ + + class EncapsulationDot1q(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"vlan": {"type": int}, "inner_vlan": {"type": int}, "_custom_data": {"type": dict}} + vlan: int + """VLAD ID.""" + inner_vlan: int | None + """Inner VLAN ID. This setting can only be applied to sub-interfaces on EOS.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + vlan: int | UndefinedType = Undefined, + inner_vlan: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + EncapsulationDot1q. + + + Subclass of AvdModel. + + Args: + vlan: VLAD ID. + inner_vlan: Inner VLAN ID. This setting can only be applied to sub-interfaces on EOS. + _custom_data: _custom_data + + """ + + class EncapsulationVlan(AvdModel): + """Subclass of AvdModel.""" + + class Client(AvdModel): + """Subclass of AvdModel.""" + + class Dot1q(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"vlan": {"type": int}, "outer": {"type": int}, "inner": {"type": int}, "_custom_data": {"type": dict}} + vlan: int | None + """Client VLAN ID.""" + outer: int | None + """Client Outer VLAN ID.""" + inner: int | None + """Client Inner VLAN ID.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + vlan: int | None | UndefinedType = Undefined, + outer: int | None | UndefinedType = Undefined, + inner: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Dot1q. + + + Subclass of AvdModel. + + Args: + vlan: Client VLAN ID. + outer: Client Outer VLAN ID. + inner: Client Inner VLAN ID. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = { + "dot1q": {"type": Dot1q}, + "unmatched": {"type": bool}, + "encapsulation": {"type": str}, + "vlan": {"type": int}, + "outer_vlan": {"type": int}, + "inner_vlan": {"type": int}, + "inner_encapsulation": {"type": str}, + "_custom_data": {"type": dict}, + } + dot1q: Dot1q + """Subclass of AvdModel.""" + unmatched: bool | None + encapsulation: Literal["dot1q", "dot1ad", "unmatched", "untagged"] | None + vlan: int | None + """Client VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`.""" + outer_vlan: int | None + """Client Outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`.""" + inner_vlan: int | None + """Client Inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`.""" + inner_encapsulation: Literal["dot1q", "dot1ad"] | None + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + dot1q: Dot1q | UndefinedType = Undefined, + unmatched: bool | None | UndefinedType = Undefined, + encapsulation: Literal["dot1q", "dot1ad", "unmatched", "untagged"] | None | UndefinedType = Undefined, + vlan: int | None | UndefinedType = Undefined, + outer_vlan: int | None | UndefinedType = Undefined, + inner_vlan: int | None | UndefinedType = Undefined, + inner_encapsulation: Literal["dot1q", "dot1ad"] | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Client. + + + Subclass of AvdModel. + + Args: + dot1q: Subclass of AvdModel. + unmatched: unmatched + encapsulation: encapsulation + vlan: Client VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`. + outer_vlan: Client Outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`. + inner_vlan: Client Inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`. + inner_encapsulation: inner_encapsulation + _custom_data: _custom_data + + """ + + class Network(AvdModel): + """Subclass of AvdModel.""" + + class Dot1q(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"vlan": {"type": int}, "outer": {"type": int}, "inner": {"type": int}, "_custom_data": {"type": dict}} + vlan: int | None + """Network VLAN ID.""" + outer: int | None + """Network Outer VLAN ID.""" + inner: int | None + """Network Inner VLAN ID.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + vlan: int | None | UndefinedType = Undefined, + outer: int | None | UndefinedType = Undefined, + inner: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Dot1q. + - """ + Subclass of AvdModel. - class PortChannelInterfacesItem(AvdModel): - """Subclass of AvdModel.""" + Args: + vlan: Network VLAN ID. + outer: Network Outer VLAN ID. + inner: Network Inner VLAN ID. + _custom_data: _custom_data - class Logging(AvdModel): - """Subclass of AvdModel.""" + """ - class Event(AvdModel): + _fields: ClassVar[dict] = { + "dot1q": {"type": Dot1q}, + "client": {"type": bool}, + "encapsulation": {"type": str}, + "vlan": {"type": int}, + "outer_vlan": {"type": int}, + "inner_vlan": {"type": int}, + "inner_encapsulation": {"type": str}, + "_custom_data": {"type": dict}, + } + dot1q: Dot1q """Subclass of AvdModel.""" - - _fields: ClassVar[dict] = {"link_status": {"type": bool}, "storm_control_discards": {"type": bool}, "_custom_data": {"type": dict}} - link_status: bool | None - storm_control_discards: bool | None - """Discards due to storm-control.""" + client: bool | None + encapsulation: Literal["dot1q", "dot1ad", "client", "client inner", "untagged"] | None + """ + `untagged` (no encapsulation) is applicable for `untagged` client only. + `client` and `client inner` + (retain client encapsulation) is not applicable for `untagged` client. + """ + vlan: int | None + """Network VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`.""" + outer_vlan: int | None + """Network outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`.""" + inner_vlan: int | None + """Network inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`.""" + inner_encapsulation: Literal["dot1q", "dot1ad"] | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27506,51 +28480,81 @@ class Event(AvdModel): def __init__( self, *, - link_status: bool | None | UndefinedType = Undefined, - storm_control_discards: bool | None | UndefinedType = Undefined, + dot1q: Dot1q | UndefinedType = Undefined, + client: bool | None | UndefinedType = Undefined, + encapsulation: Literal["dot1q", "dot1ad", "client", "client inner", "untagged"] | None | UndefinedType = Undefined, + vlan: int | None | UndefinedType = Undefined, + outer_vlan: int | None | UndefinedType = Undefined, + inner_vlan: int | None | UndefinedType = Undefined, + inner_encapsulation: Literal["dot1q", "dot1ad"] | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Event. + Network. Subclass of AvdModel. Args: - link_status: link_status - storm_control_discards: Discards due to storm-control. + dot1q: Subclass of AvdModel. + client: client + encapsulation: + `untagged` (no encapsulation) is applicable for `untagged` client only. + `client` and `client inner` + (retain client encapsulation) is not applicable for `untagged` client. + vlan: Network VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`. + outer_vlan: Network outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`. + inner_vlan: Network inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`. + inner_encapsulation: inner_encapsulation _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"event": {"type": Event}, "_custom_data": {"type": dict}} - event: Event + _fields: ClassVar[dict] = {"client": {"type": Client}, "network": {"type": Network}, "_custom_data": {"type": dict}} + client: Client """Subclass of AvdModel.""" + network: Network + """ + Network encapsulation are all optional, and skipped if using client unmatched. + + Subclass of + AvdModel. + """ _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__(self, *, event: Event | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + def __init__( + self, + *, + client: Client | UndefinedType = Undefined, + network: Network | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: """ - Logging. + EncapsulationVlan. Subclass of AvdModel. Args: - event: Subclass of AvdModel. + client: Subclass of AvdModel. + network: + Network encapsulation are all optional, and skipped if using client unmatched. + + Subclass of + AvdModel. _custom_data: _custom_data """ - class EncapsulationDot1q(AvdModel): + class LinkTrackingGroupsItem(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"vlan": {"type": int}, "inner_vlan": {"type": int}, "_custom_data": {"type": dict}} - vlan: int - """VLAD ID.""" - inner_vlan: int | None - """Inner VLAN ID. This setting can only be applied to sub-interfaces on EOS.""" + _fields: ClassVar[dict] = {"name": {"type": str}, "direction": {"type": str}, "_custom_data": {"type": dict}} + name: str + """Group name.""" + direction: Literal["upstream", "downstream"] | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27558,185 +28562,184 @@ class EncapsulationDot1q(AvdModel): def __init__( self, *, - vlan: int | UndefinedType = Undefined, - inner_vlan: int | None | UndefinedType = Undefined, + name: str | UndefinedType = Undefined, + direction: Literal["upstream", "downstream"] | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - EncapsulationDot1q. + LinkTrackingGroupsItem. Subclass of AvdModel. Args: - vlan: VLAD ID. - inner_vlan: Inner VLAN ID. This setting can only be applied to sub-interfaces on EOS. + name: Group name. + direction: direction _custom_data: _custom_data """ - class EncapsulationVlan(AvdModel): + class LinkTrackingGroups(AvdIndexedList[str, LinkTrackingGroupsItem]): + """Subclass of AvdIndexedList with `LinkTrackingGroupsItem` items. Primary key is `name` (`str`).""" + + _primary_key: ClassVar[str] = "name" + + LinkTrackingGroups._item_type = LinkTrackingGroupsItem + + class LinkTracking(AvdModel): """Subclass of AvdModel.""" - class Client(AvdModel): - """Subclass of AvdModel.""" + class Groups(AvdList[str]): + """Subclass of AvdList with `str` items.""" - class Dot1q(AvdModel): - """Subclass of AvdModel.""" + Groups._item_type = str - _fields: ClassVar[dict] = {"vlan": {"type": int}, "outer": {"type": int}, "inner": {"type": int}, "_custom_data": {"type": dict}} - vlan: int | None - """Client VLAN ID.""" - outer: int | None - """Client Outer VLAN ID.""" - inner: int | None - """Client Inner VLAN ID.""" - _custom_data: dict[str, Any] + _fields: ClassVar[dict] = {"direction": {"type": str}, "groups": {"type": Groups}, "_custom_data": {"type": dict}} + direction: Literal["upstream", "downstream"] | None + groups: Groups + """ + Link state group(s) an interface belongs to. - if TYPE_CHECKING: + Subclass of AvdList with `str` items. + """ + _custom_data: dict[str, Any] - def __init__( - self, - *, - vlan: int | None | UndefinedType = Undefined, - outer: int | None | UndefinedType = Undefined, - inner: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Dot1q. + if TYPE_CHECKING: + def __init__( + self, + *, + direction: Literal["upstream", "downstream"] | None | UndefinedType = Undefined, + groups: Groups | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + LinkTracking. - Subclass of AvdModel. - Args: - vlan: Client VLAN ID. - outer: Client Outer VLAN ID. - inner: Client Inner VLAN ID. - _custom_data: _custom_data + Subclass of AvdModel. - """ + Args: + direction: direction + groups: + Link state group(s) an interface belongs to. - _fields: ClassVar[dict] = { - "dot1q": {"type": Dot1q}, - "unmatched": {"type": bool}, - "encapsulation": {"type": str}, - "vlan": {"type": int}, - "outer_vlan": {"type": int}, - "inner_vlan": {"type": int}, - "inner_encapsulation": {"type": str}, - "_custom_data": {"type": dict}, - } - dot1q: Dot1q - """Subclass of AvdModel.""" - unmatched: bool | None - encapsulation: Literal["dot1q", "dot1ad", "unmatched", "untagged"] | None - vlan: int | None - """Client VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`.""" - outer_vlan: int | None - """Client Outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`.""" - inner_vlan: int | None - """Client Inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`.""" - inner_encapsulation: Literal["dot1q", "dot1ad"] | None - _custom_data: dict[str, Any] + Subclass of AvdList with `str` items. + _custom_data: _custom_data + + """ + + class Phone(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"trunk": {"type": str}, "vlan": {"type": int}, "_custom_data": {"type": dict}} + trunk: Literal["tagged", "untagged"] | None + vlan: int | None + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + trunk: Literal["tagged", "untagged"] | None | UndefinedType = Undefined, + vlan: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Phone. + + + Subclass of AvdModel. + + Args: + trunk: trunk + vlan: vlan + _custom_data: _custom_data + + """ + + class L2Protocol(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"encapsulation_dot1q_vlan": {"type": int}, "forwarding_profile": {"type": str}, "_custom_data": {"type": dict}} + encapsulation_dot1q_vlan: int | None + """Vlan tag to configure on sub-interface.""" + forwarding_profile: str | None + """L2 protocol forwarding profile.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + encapsulation_dot1q_vlan: int | None | UndefinedType = Undefined, + forwarding_profile: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + L2Protocol. - if TYPE_CHECKING: - def __init__( - self, - *, - dot1q: Dot1q | UndefinedType = Undefined, - unmatched: bool | None | UndefinedType = Undefined, - encapsulation: Literal["dot1q", "dot1ad", "unmatched", "untagged"] | None | UndefinedType = Undefined, - vlan: int | None | UndefinedType = Undefined, - outer_vlan: int | None | UndefinedType = Undefined, - inner_vlan: int | None | UndefinedType = Undefined, - inner_encapsulation: Literal["dot1q", "dot1ad"] | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Client. + Subclass of AvdModel. + Args: + encapsulation_dot1q_vlan: Vlan tag to configure on sub-interface. + forwarding_profile: L2 protocol forwarding profile. + _custom_data: _custom_data - Subclass of AvdModel. + """ - Args: - dot1q: Subclass of AvdModel. - unmatched: unmatched - encapsulation: encapsulation - vlan: Client VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`. - outer_vlan: Client Outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`. - inner_vlan: Client Inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: unmatched`. - inner_encapsulation: inner_encapsulation - _custom_data: _custom_data + class TrunkGroups(AvdList[str]): + """Subclass of AvdList with `str` items.""" - """ + TrunkGroups._item_type = str - class Network(AvdModel): - """Subclass of AvdModel.""" + class Qos(AvdModel): + """Subclass of AvdModel.""" - class Dot1q(AvdModel): - """Subclass of AvdModel.""" + _fields: ClassVar[dict] = {"trust": {"type": str}, "dscp": {"type": int}, "cos": {"type": int}, "_custom_data": {"type": dict}} + trust: Literal["dscp", "cos", "disabled"] | None + dscp: int | None + """DSCP value.""" + cos: int | None + """COS value.""" + _custom_data: dict[str, Any] - _fields: ClassVar[dict] = {"vlan": {"type": int}, "outer": {"type": int}, "inner": {"type": int}, "_custom_data": {"type": dict}} - vlan: int | None - """Network VLAN ID.""" - outer: int | None - """Network Outer VLAN ID.""" - inner: int | None - """Network Inner VLAN ID.""" - _custom_data: dict[str, Any] + if TYPE_CHECKING: - if TYPE_CHECKING: + def __init__( + self, + *, + trust: Literal["dscp", "cos", "disabled"] | None | UndefinedType = Undefined, + dscp: int | None | UndefinedType = Undefined, + cos: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Qos. - def __init__( - self, - *, - vlan: int | None | UndefinedType = Undefined, - outer: int | None | UndefinedType = Undefined, - inner: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Dot1q. + Subclass of AvdModel. - Subclass of AvdModel. + Args: + trust: trust + dscp: DSCP value. + cos: COS value. + _custom_data: _custom_data - Args: - vlan: Network VLAN ID. - outer: Network Outer VLAN ID. - inner: Network Inner VLAN ID. - _custom_data: _custom_data + """ - """ + class Bfd(AvdModel): + """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "dot1q": {"type": Dot1q}, - "client": {"type": bool}, - "encapsulation": {"type": str}, - "vlan": {"type": int}, - "outer_vlan": {"type": int}, - "inner_vlan": {"type": int}, - "inner_encapsulation": {"type": str}, - "_custom_data": {"type": dict}, - } - dot1q: Dot1q + class PerLink(AvdModel): """Subclass of AvdModel.""" - client: bool | None - encapsulation: Literal["dot1q", "dot1ad", "client", "client inner", "untagged"] | None - """ - `untagged` (no encapsulation) is applicable for `untagged` client only. - `client` and `client inner` - (retain client encapsulation) is not applicable for `untagged` client. - """ - vlan: int | None - """Network VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`.""" - outer_vlan: int | None - """Network outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`.""" - inner_vlan: int | None - """Network inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`.""" - inner_encapsulation: Literal["dot1q", "dot1ad"] | None + + _fields: ClassVar[dict] = {"enabled": {"type": bool}, "rfc_7130": {"type": bool}, "_custom_data": {"type": dict}} + enabled: bool | None + rfc_7130: bool | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27744,46 +28747,45 @@ def __init__( def __init__( self, *, - dot1q: Dot1q | UndefinedType = Undefined, - client: bool | None | UndefinedType = Undefined, - encapsulation: Literal["dot1q", "dot1ad", "client", "client inner", "untagged"] | None | UndefinedType = Undefined, - vlan: int | None | UndefinedType = Undefined, - outer_vlan: int | None | UndefinedType = Undefined, - inner_vlan: int | None | UndefinedType = Undefined, - inner_encapsulation: Literal["dot1q", "dot1ad"] | None | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + rfc_7130: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Network. + PerLink. Subclass of AvdModel. Args: - dot1q: Subclass of AvdModel. - client: client - encapsulation: - `untagged` (no encapsulation) is applicable for `untagged` client only. - `client` and `client inner` - (retain client encapsulation) is not applicable for `untagged` client. - vlan: Network VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`. - outer_vlan: Network outer VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`. - inner_vlan: Network inner VLAN ID. Not applicable for `encapsulation: untagged` or `encapsulation: client`. - inner_encapsulation: inner_encapsulation + enabled: enabled + rfc_7130: rfc_7130 _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"client": {"type": Client}, "network": {"type": Network}, "_custom_data": {"type": dict}} - client: Client - """Subclass of AvdModel.""" - network: Network + _fields: ClassVar[dict] = { + "echo": {"type": bool}, + "interval": {"type": int}, + "min_rx": {"type": int}, + "multiplier": {"type": int}, + "neighbor": {"type": str}, + "per_link": {"type": PerLink}, + "_custom_data": {"type": dict}, + } + echo: bool | None + interval: int | None + """Interval in milliseconds.""" + min_rx: int | None + """Rate in milliseconds.""" + multiplier: int | None + neighbor: str | None """ - Network encapsulation are all optional, and skipped if using client unmatched. - - Subclass of - AvdModel. + IPv4 or IPv6 address. When the Port-channel is a L2 interface, a local L3 BFD address + (router_bfd.local_address) has to be defined globally on the switch. """ + per_link: PerLink + """Subclass of AvdModel.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27791,81 +28793,87 @@ def __init__( def __init__( self, *, - client: Client | UndefinedType = Undefined, - network: Network | UndefinedType = Undefined, + echo: bool | None | UndefinedType = Undefined, + interval: int | None | UndefinedType = Undefined, + min_rx: int | None | UndefinedType = Undefined, + multiplier: int | None | UndefinedType = Undefined, + neighbor: str | None | UndefinedType = Undefined, + per_link: PerLink | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - EncapsulationVlan. + Bfd. Subclass of AvdModel. Args: - client: Subclass of AvdModel. - network: - Network encapsulation are all optional, and skipped if using client unmatched. - - Subclass of - AvdModel. + echo: echo + interval: Interval in milliseconds. + min_rx: Rate in milliseconds. + multiplier: multiplier + neighbor: + IPv4 or IPv6 address. When the Port-channel is a L2 interface, a local L3 BFD address + (router_bfd.local_address) has to be defined globally on the switch. + per_link: Subclass of AvdModel. _custom_data: _custom_data """ - class LinkTrackingGroupsItem(AvdModel): + class ServicePolicy(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"name": {"type": str}, "direction": {"type": str}, "_custom_data": {"type": dict}} - name: str - """Group name.""" - direction: Literal["upstream", "downstream"] | None - _custom_data: dict[str, Any] + class Pbr(AvdModel): + """Subclass of AvdModel.""" - if TYPE_CHECKING: + _fields: ClassVar[dict] = {"input": {"type": str}, "_custom_data": {"type": dict}} + input: str | None + """Policy Based Routing Policy-map name.""" + _custom_data: dict[str, Any] - def __init__( - self, - *, - name: str | UndefinedType = Undefined, - direction: Literal["upstream", "downstream"] | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - LinkTrackingGroupsItem. + if TYPE_CHECKING: + def __init__(self, *, input: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Pbr. - Subclass of AvdModel. - Args: - name: Group name. - direction: direction - _custom_data: _custom_data + Subclass of AvdModel. - """ + Args: + input: Policy Based Routing Policy-map name. + _custom_data: _custom_data - class LinkTrackingGroups(AvdIndexedList[str, LinkTrackingGroupsItem]): - """Subclass of AvdIndexedList with `LinkTrackingGroupsItem` items. Primary key is `name` (`str`).""" + """ - _primary_key: ClassVar[str] = "name" + class Qos(AvdModel): + """Subclass of AvdModel.""" - LinkTrackingGroups._item_type = LinkTrackingGroupsItem + _fields: ClassVar[dict] = {"input": {"type": str}, "_custom_data": {"type": dict}} + input: str + """Quality of Service Policy-map name.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__(self, *, input: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Qos. - class LinkTracking(AvdModel): - """Subclass of AvdModel.""" - class Groups(AvdList[str]): - """Subclass of AvdList with `str` items.""" + Subclass of AvdModel. - Groups._item_type = str + Args: + input: Quality of Service Policy-map name. + _custom_data: _custom_data - _fields: ClassVar[dict] = {"direction": {"type": str}, "groups": {"type": Groups}, "_custom_data": {"type": dict}} - direction: Literal["upstream", "downstream"] | None - groups: Groups - """ - Link state group(s) an interface belongs to. + """ - Subclass of AvdList with `str` items. - """ + _fields: ClassVar[dict] = {"pbr": {"type": Pbr}, "qos": {"type": Qos}, "_custom_data": {"type": dict}} + pbr: Pbr + """Subclass of AvdModel.""" + qos: Qos + """Subclass of AvdModel.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27873,32 +28881,60 @@ class Groups(AvdList[str]): def __init__( self, *, - direction: Literal["upstream", "downstream"] | None | UndefinedType = Undefined, - groups: Groups | UndefinedType = Undefined, + pbr: Pbr | UndefinedType = Undefined, + qos: Qos | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - LinkTracking. + ServicePolicy. Subclass of AvdModel. Args: - direction: direction - groups: - Link state group(s) an interface belongs to. - - Subclass of AvdList with `str` items. + pbr: Subclass of AvdModel. + qos: Subclass of AvdModel. _custom_data: _custom_data """ - class Phone(AvdModel): + class Mpls(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"trunk": {"type": str}, "vlan": {"type": int}, "_custom_data": {"type": dict}} - trunk: Literal["tagged", "untagged"] | None - vlan: int | None + class Ldp(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"interface": {"type": bool}, "igp_sync": {"type": bool}, "_custom_data": {"type": dict}} + interface: bool | None + igp_sync: bool | None + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + interface: bool | None | UndefinedType = Undefined, + igp_sync: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Ldp. + + + Subclass of AvdModel. + + Args: + interface: interface + igp_sync: igp_sync + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = {"ip": {"type": bool}, "ldp": {"type": Ldp}, "_custom_data": {"type": dict}} + ip: bool | None + ldp: Ldp + """Subclass of AvdModel.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27906,31 +28942,40 @@ class Phone(AvdModel): def __init__( self, *, - trunk: Literal["tagged", "untagged"] | None | UndefinedType = Undefined, - vlan: int | None | UndefinedType = Undefined, + ip: bool | None | UndefinedType = Undefined, + ldp: Ldp | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Phone. + Mpls. Subclass of AvdModel. Args: - trunk: trunk - vlan: vlan + ip: ip + ldp: Subclass of AvdModel. _custom_data: _custom_data """ - class L2Protocol(AvdModel): + class VlanTranslationsItem(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"encapsulation_dot1q_vlan": {"type": int}, "forwarding_profile": {"type": str}, "_custom_data": {"type": dict}} - encapsulation_dot1q_vlan: int | None - """Vlan tag to configure on sub-interface.""" - forwarding_profile: str | None - """L2 protocol forwarding profile.""" + _fields: ClassVar[dict] = { + "field_from": {"type": str}, + "to": {"type": int}, + "direction": {"type": str, "default": "both"}, + "_custom_data": {"type": dict}, + } + _field_to_key_map: ClassVar[dict] = {"field_from": "from"} + _key_to_field_map: ClassVar[dict] = {"from": "field_from"} + field_from: str | None + """List of vlans as string (only one vlan if direction is "both").""" + to: int | None + """VLAN ID.""" + direction: Literal["in", "out", "both"] + """Default value: `"both"`""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -27938,72 +28983,83 @@ class L2Protocol(AvdModel): def __init__( self, *, - encapsulation_dot1q_vlan: int | None | UndefinedType = Undefined, - forwarding_profile: str | None | UndefinedType = Undefined, + field_from: str | None | UndefinedType = Undefined, + to: int | None | UndefinedType = Undefined, + direction: Literal["in", "out", "both"] | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - L2Protocol. + VlanTranslationsItem. Subclass of AvdModel. Args: - encapsulation_dot1q_vlan: Vlan tag to configure on sub-interface. - forwarding_profile: L2 protocol forwarding profile. + field_from: List of vlans as string (only one vlan if direction is "both"). + to: VLAN ID. + direction: direction _custom_data: _custom_data """ - class TrunkGroups(AvdList[str]): - """Subclass of AvdList with `str` items.""" + class VlanTranslations(AvdList[VlanTranslationsItem]): + """Subclass of AvdList with `VlanTranslationsItem` items.""" - TrunkGroups._item_type = str + VlanTranslations._item_type = VlanTranslationsItem - class Qos(AvdModel): + class Shape(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"trust": {"type": str}, "dscp": {"type": int}, "cos": {"type": int}, "_custom_data": {"type": dict}} - trust: Literal["dscp", "cos", "disabled"] | None - dscp: int | None - """DSCP value.""" - cos: int | None - """COS value.""" + _fields: ClassVar[dict] = {"rate": {"type": str}, "_custom_data": {"type": dict}} + rate: str | None + """ + Rate in kbps, pps or percent. + Supported options are platform dependent. + Examples: + - "5000 kbps" + - + "1000 pps" + - "20 percent" + """ _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__( - self, - *, - trust: Literal["dscp", "cos", "disabled"] | None | UndefinedType = Undefined, - dscp: int | None | UndefinedType = Undefined, - cos: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + def __init__(self, *, rate: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: """ - Qos. + Shape. Subclass of AvdModel. Args: - trust: trust - dscp: DSCP value. - cos: COS value. + rate: + Rate in kbps, pps or percent. + Supported options are platform dependent. + Examples: # fmt: skip + - "5000 kbps" + - + "1000 pps" + - "20 percent" _custom_data: _custom_data """ - class Bfd(AvdModel): + class StormControl(AvdModel): """Subclass of AvdModel.""" - class PerLink(AvdModel): + class All(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"enabled": {"type": bool}, "rfc_7130": {"type": bool}, "_custom_data": {"type": dict}} - enabled: bool | None - rfc_7130: bool | None + _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} + level: str | None + """Configure maximum storm-control level.""" + unit: Literal["percent", "pps"] + """ + Optional field and is hardware dependent. + + Default value: `"percent"` + """ _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28011,166 +29067,107 @@ class PerLink(AvdModel): def __init__( self, *, - enabled: bool | None | UndefinedType = Undefined, - rfc_7130: bool | None | UndefinedType = Undefined, + level: str | None | UndefinedType = Undefined, + unit: Literal["percent", "pps"] | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - PerLink. + All. Subclass of AvdModel. Args: - enabled: enabled - rfc_7130: rfc_7130 + level: Configure maximum storm-control level. + unit: Optional field and is hardware dependent. _custom_data: _custom_data """ - _fields: ClassVar[dict] = { - "echo": {"type": bool}, - "interval": {"type": int}, - "min_rx": {"type": int}, - "multiplier": {"type": int}, - "neighbor": {"type": str}, - "per_link": {"type": PerLink}, - "_custom_data": {"type": dict}, - } - echo: bool | None - interval: int | None - """Interval in milliseconds.""" - min_rx: int | None - """Rate in milliseconds.""" - multiplier: int | None - neighbor: str | None - """ - IPv4 or IPv6 address. When the Port-channel is a L2 interface, a local L3 BFD address - (router_bfd.local_address) has to be defined globally on the switch. - """ - per_link: PerLink - """Subclass of AvdModel.""" - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__( - self, - *, - echo: bool | None | UndefinedType = Undefined, - interval: int | None | UndefinedType = Undefined, - min_rx: int | None | UndefinedType = Undefined, - multiplier: int | None | UndefinedType = Undefined, - neighbor: str | None | UndefinedType = Undefined, - per_link: PerLink | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Bfd. - - - Subclass of AvdModel. - - Args: - echo: echo - interval: Interval in milliseconds. - min_rx: Rate in milliseconds. - multiplier: multiplier - neighbor: - IPv4 or IPv6 address. When the Port-channel is a L2 interface, a local L3 BFD address - (router_bfd.local_address) has to be defined globally on the switch. - per_link: Subclass of AvdModel. - _custom_data: _custom_data - - """ - - class ServicePolicy(AvdModel): - """Subclass of AvdModel.""" - - class Pbr(AvdModel): + class Broadcast(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"input": {"type": str}, "_custom_data": {"type": dict}} - input: str | None - """Policy Based Routing Policy-map name.""" + _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} + level: str | None + """Configure maximum storm-control level.""" + unit: Literal["percent", "pps"] + """ + Optional field and is hardware dependent. + + Default value: `"percent"` + """ _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__(self, *, input: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + def __init__( + self, + *, + level: str | None | UndefinedType = Undefined, + unit: Literal["percent", "pps"] | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: """ - Pbr. + Broadcast. Subclass of AvdModel. Args: - input: Policy Based Routing Policy-map name. + level: Configure maximum storm-control level. + unit: Optional field and is hardware dependent. _custom_data: _custom_data """ - class Qos(AvdModel): + class Multicast(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"input": {"type": str}, "_custom_data": {"type": dict}} - input: str - """Quality of Service Policy-map name.""" + _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} + level: str | None + """Configure maximum storm-control level.""" + unit: Literal["percent", "pps"] + """ + Optional field and is hardware dependent. + + Default value: `"percent"` + """ _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__(self, *, input: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + def __init__( + self, + *, + level: str | None | UndefinedType = Undefined, + unit: Literal["percent", "pps"] | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: """ - Qos. + Multicast. Subclass of AvdModel. Args: - input: Quality of Service Policy-map name. + level: Configure maximum storm-control level. + unit: Optional field and is hardware dependent. _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"pbr": {"type": Pbr}, "qos": {"type": Qos}, "_custom_data": {"type": dict}} - pbr: Pbr - """Subclass of AvdModel.""" - qos: Qos - """Subclass of AvdModel.""" - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__( - self, - *, - pbr: Pbr | UndefinedType = Undefined, - qos: Qos | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - ServicePolicy. - - - Subclass of AvdModel. - - Args: - pbr: Subclass of AvdModel. - qos: Subclass of AvdModel. - _custom_data: _custom_data - - """ - - class Mpls(AvdModel): - """Subclass of AvdModel.""" - - class Ldp(AvdModel): + class UnknownUnicast(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"interface": {"type": bool}, "igp_sync": {"type": bool}, "_custom_data": {"type": dict}} - interface: bool | None - igp_sync: bool | None + _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} + level: str | None + """Configure maximum storm-control level.""" + unit: Literal["percent", "pps"] + """ + Optional field and is hardware dependent. + + Default value: `"percent"` + """ _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28178,26 +29175,37 @@ class Ldp(AvdModel): def __init__( self, *, - interface: bool | None | UndefinedType = Undefined, - igp_sync: bool | None | UndefinedType = Undefined, + level: str | None | UndefinedType = Undefined, + unit: Literal["percent", "pps"] | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Ldp. + UnknownUnicast. Subclass of AvdModel. Args: - interface: interface - igp_sync: igp_sync + level: Configure maximum storm-control level. + unit: Optional field and is hardware dependent. _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"ip": {"type": bool}, "ldp": {"type": Ldp}, "_custom_data": {"type": dict}} - ip: bool | None - ldp: Ldp + _fields: ClassVar[dict] = { + "all": {"type": All}, + "broadcast": {"type": Broadcast}, + "multicast": {"type": Multicast}, + "unknown_unicast": {"type": UnknownUnicast}, + "_custom_data": {"type": dict}, + } + all: All + """Subclass of AvdModel.""" + broadcast: Broadcast + """Subclass of AvdModel.""" + multicast: Multicast + """Subclass of AvdModel.""" + unknown_unicast: UnknownUnicast """Subclass of AvdModel.""" _custom_data: dict[str, Any] @@ -28206,124 +29214,174 @@ def __init__( def __init__( self, *, - ip: bool | None | UndefinedType = Undefined, - ldp: Ldp | UndefinedType = Undefined, + all: All | UndefinedType = Undefined, + broadcast: Broadcast | UndefinedType = Undefined, + multicast: Multicast | UndefinedType = Undefined, + unknown_unicast: UnknownUnicast | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Mpls. + StormControl. Subclass of AvdModel. Args: - ip: ip - ldp: Subclass of AvdModel. + all: Subclass of AvdModel. + broadcast: Subclass of AvdModel. + multicast: Subclass of AvdModel. + unknown_unicast: Subclass of AvdModel. _custom_data: _custom_data """ - class VlanTranslationsItem(AvdModel): + class IsisAuthentication(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "field_from": {"type": str}, - "to": {"type": int}, - "direction": {"type": str, "default": "both"}, - "_custom_data": {"type": dict}, - } - _field_to_key_map: ClassVar[dict] = {"field_from": "from"} - _key_to_field_map: ClassVar[dict] = {"from": "field_from"} - field_from: str | None - """List of vlans as string (only one vlan if direction is "both").""" - to: int | None - """VLAN ID.""" - direction: Literal["in", "out", "both"] - """Default value: `"both"`""" - _custom_data: dict[str, Any] + class Both(AvdModel): + """Subclass of AvdModel.""" - if TYPE_CHECKING: + class KeyIdsItem(AvdModel): + """Subclass of AvdModel.""" - def __init__( - self, - *, - field_from: str | None | UndefinedType = Undefined, - to: int | None | UndefinedType = Undefined, - direction: Literal["in", "out", "both"] | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - VlanTranslationsItem. + _fields: ClassVar[dict] = { + "id": {"type": int}, + "algorithm": {"type": str}, + "key_type": {"type": str}, + "key": {"type": str}, + "rfc_5310": {"type": bool}, + "_custom_data": {"type": dict}, + } + id: int + """Configure authentication key-id.""" + algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] + key_type: Literal["0", "7", "8a"] + """Configure authentication key type.""" + key: str + """Password string.""" + rfc_5310: bool | None + """SHA digest computation according to rfc5310.""" + _custom_data: dict[str, Any] + if TYPE_CHECKING: - Subclass of AvdModel. + def __init__( + self, + *, + id: int | UndefinedType = Undefined, + algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, + key_type: Literal["0", "7", "8a"] | UndefinedType = Undefined, + key: str | UndefinedType = Undefined, + rfc_5310: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + KeyIdsItem. - Args: - field_from: List of vlans as string (only one vlan if direction is "both"). - to: VLAN ID. - direction: direction - _custom_data: _custom_data - """ + Subclass of AvdModel. - class VlanTranslations(AvdList[VlanTranslationsItem]): - """Subclass of AvdList with `VlanTranslationsItem` items.""" + Args: + id: Configure authentication key-id. + algorithm: algorithm + key_type: Configure authentication key type. + key: Password string. + rfc_5310: SHA digest computation according to rfc5310. + _custom_data: _custom_data - VlanTranslations._item_type = VlanTranslationsItem + """ - class Shape(AvdModel): - """Subclass of AvdModel.""" + class KeyIds(AvdIndexedList[int, KeyIdsItem]): + """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" - _fields: ClassVar[dict] = {"rate": {"type": str}, "_custom_data": {"type": dict}} - rate: str | None - """ - Rate in kbps, pps or percent. - Supported options are platform dependent. - Examples: - - "5000 kbps" - - - "1000 pps" - - "20 percent" - """ - _custom_data: dict[str, Any] + _primary_key: ClassVar[str] = "id" + + KeyIds._item_type = KeyIdsItem + + class Sha(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"key_id": {"type": int}, "_custom_data": {"type": dict}} + key_id: int + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__(self, *, key_id: int | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Sha. + + + Subclass of AvdModel. + + Args: + key_id: key_id + _custom_data: _custom_data - if TYPE_CHECKING: + """ - def __init__(self, *, rate: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: - """ - Shape. + class SharedSecret(AvdModel): + """Subclass of AvdModel.""" + _fields: ClassVar[dict] = {"profile": {"type": str}, "algorithm": {"type": str}, "_custom_data": {"type": dict}} + profile: str + algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] + _custom_data: dict[str, Any] - Subclass of AvdModel. + if TYPE_CHECKING: - Args: - rate: - Rate in kbps, pps or percent. - Supported options are platform dependent. - Examples: # fmt: skip - - "5000 kbps" - - - "1000 pps" - - "20 percent" - _custom_data: _custom_data + def __init__( + self, + *, + profile: str | UndefinedType = Undefined, + algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + SharedSecret. - """ - class StormControl(AvdModel): - """Subclass of AvdModel.""" + Subclass of AvdModel. - class All(AvdModel): - """Subclass of AvdModel.""" + Args: + profile: profile + algorithm: algorithm + _custom_data: _custom_data - _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} - level: str | None - """Configure maximum storm-control level.""" - unit: Literal["percent", "pps"] + """ + + _fields: ClassVar[dict] = { + "key_type": {"type": str}, + "key": {"type": str}, + "key_ids": {"type": KeyIds}, + "mode": {"type": str}, + "sha": {"type": Sha}, + "shared_secret": {"type": SharedSecret}, + "rx_disabled": {"type": bool}, + "_custom_data": {"type": dict}, + } + key_type: Literal["0", "7", "8a"] | None + """Configure authentication key type.""" + key: str | None + """Password string. `key_type` is required for this setting.""" + key_ids: KeyIds + """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" + mode: Literal["md5", "sha", "text", "shared-secret"] | None + """Authentication mode.""" + sha: Sha """ - Optional field and is hardware dependent. + Required settings for authentication mode 'sha'. - Default value: `"percent"` + Subclass of AvdModel. """ + shared_secret: SharedSecret + """ + Required settings for authentication mode 'shared_secret'. + + Subclass of AvdModel. + """ + rx_disabled: bool | None + """Disable authentication check on the receive side.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28331,107 +29389,183 @@ class All(AvdModel): def __init__( self, *, - level: str | None | UndefinedType = Undefined, - unit: Literal["percent", "pps"] | UndefinedType = Undefined, + key_type: Literal["0", "7", "8a"] | None | UndefinedType = Undefined, + key: str | None | UndefinedType = Undefined, + key_ids: KeyIds | UndefinedType = Undefined, + mode: Literal["md5", "sha", "text", "shared-secret"] | None | UndefinedType = Undefined, + sha: Sha | UndefinedType = Undefined, + shared_secret: SharedSecret | UndefinedType = Undefined, + rx_disabled: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - All. + Both. Subclass of AvdModel. Args: - level: Configure maximum storm-control level. - unit: Optional field and is hardware dependent. + key_type: Configure authentication key type. + key: Password string. `key_type` is required for this setting. + key_ids: Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`). + mode: Authentication mode. + sha: + Required settings for authentication mode 'sha'. + + Subclass of AvdModel. + shared_secret: + Required settings for authentication mode 'shared_secret'. + + Subclass of AvdModel. + rx_disabled: Disable authentication check on the receive side. _custom_data: _custom_data """ - class Broadcast(AvdModel): + class Level1(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} - level: str | None - """Configure maximum storm-control level.""" - unit: Literal["percent", "pps"] - """ - Optional field and is hardware dependent. + class KeyIdsItem(AvdModel): + """Subclass of AvdModel.""" - Default value: `"percent"` - """ - _custom_data: dict[str, Any] + _fields: ClassVar[dict] = { + "id": {"type": int}, + "algorithm": {"type": str}, + "key_type": {"type": str}, + "key": {"type": str}, + "rfc_5310": {"type": bool}, + "_custom_data": {"type": dict}, + } + id: int + """Configure authentication key-id.""" + algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] + key_type: Literal["0", "7", "8a"] + """Configure authentication key type.""" + key: str + """Password string.""" + rfc_5310: bool | None + """SHA digest computation according to rfc5310.""" + _custom_data: dict[str, Any] - if TYPE_CHECKING: + if TYPE_CHECKING: - def __init__( - self, - *, - level: str | None | UndefinedType = Undefined, - unit: Literal["percent", "pps"] | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Broadcast. + def __init__( + self, + *, + id: int | UndefinedType = Undefined, + algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, + key_type: Literal["0", "7", "8a"] | UndefinedType = Undefined, + key: str | UndefinedType = Undefined, + rfc_5310: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + KeyIdsItem. - Subclass of AvdModel. + Subclass of AvdModel. - Args: - level: Configure maximum storm-control level. - unit: Optional field and is hardware dependent. - _custom_data: _custom_data + Args: + id: Configure authentication key-id. + algorithm: algorithm + key_type: Configure authentication key type. + key: Password string. + rfc_5310: SHA digest computation according to rfc5310. + _custom_data: _custom_data - """ + """ - class Multicast(AvdModel): - """Subclass of AvdModel.""" + class KeyIds(AvdIndexedList[int, KeyIdsItem]): + """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" - _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} - level: str | None - """Configure maximum storm-control level.""" - unit: Literal["percent", "pps"] - """ - Optional field and is hardware dependent. + _primary_key: ClassVar[str] = "id" - Default value: `"percent"` - """ - _custom_data: dict[str, Any] + KeyIds._item_type = KeyIdsItem - if TYPE_CHECKING: + class Sha(AvdModel): + """Subclass of AvdModel.""" - def __init__( - self, - *, - level: str | None | UndefinedType = Undefined, - unit: Literal["percent", "pps"] | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Multicast. + _fields: ClassVar[dict] = {"key_id": {"type": int}, "_custom_data": {"type": dict}} + key_id: int + _custom_data: dict[str, Any] + if TYPE_CHECKING: - Subclass of AvdModel. + def __init__(self, *, key_id: int | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Sha. - Args: - level: Configure maximum storm-control level. - unit: Optional field and is hardware dependent. - _custom_data: _custom_data - """ + Subclass of AvdModel. - class UnknownUnicast(AvdModel): - """Subclass of AvdModel.""" + Args: + key_id: key_id + _custom_data: _custom_data + + """ + + class SharedSecret(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"profile": {"type": str}, "algorithm": {"type": str}, "_custom_data": {"type": dict}} + profile: str + algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + profile: str | UndefinedType = Undefined, + algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + SharedSecret. + + + Subclass of AvdModel. + + Args: + profile: profile + algorithm: algorithm + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = { + "key_type": {"type": str}, + "key": {"type": str}, + "key_ids": {"type": KeyIds}, + "mode": {"type": str}, + "sha": {"type": Sha}, + "shared_secret": {"type": SharedSecret}, + "rx_disabled": {"type": bool}, + "_custom_data": {"type": dict}, + } + key_type: Literal["0", "7", "8a"] | None + """Configure authentication key type.""" + key: str | None + """Password string. `key_type` is required for this setting.""" + key_ids: KeyIds + """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" + mode: Literal["md5", "sha", "text", "shared-secret"] | None + """Authentication mode.""" + sha: Sha + """ + Required settings for authentication mode 'sha'. - _fields: ClassVar[dict] = {"level": {"type": str}, "unit": {"type": str, "default": "percent"}, "_custom_data": {"type": dict}} - level: str | None - """Configure maximum storm-control level.""" - unit: Literal["percent", "pps"] + Subclass of AvdModel. """ - Optional field and is hardware dependent. + shared_secret: SharedSecret + """ + Required settings for authentication mode 'shared_secret'. - Default value: `"percent"` + Subclass of AvdModel. """ + rx_disabled: bool | None + """Disable authentication check on the receive side.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28439,70 +29573,40 @@ class UnknownUnicast(AvdModel): def __init__( self, *, - level: str | None | UndefinedType = Undefined, - unit: Literal["percent", "pps"] | UndefinedType = Undefined, + key_type: Literal["0", "7", "8a"] | None | UndefinedType = Undefined, + key: str | None | UndefinedType = Undefined, + key_ids: KeyIds | UndefinedType = Undefined, + mode: Literal["md5", "sha", "text", "shared-secret"] | None | UndefinedType = Undefined, + sha: Sha | UndefinedType = Undefined, + shared_secret: SharedSecret | UndefinedType = Undefined, + rx_disabled: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - UnknownUnicast. + Level1. Subclass of AvdModel. Args: - level: Configure maximum storm-control level. - unit: Optional field and is hardware dependent. - _custom_data: _custom_data - - """ - - _fields: ClassVar[dict] = { - "all": {"type": All}, - "broadcast": {"type": Broadcast}, - "multicast": {"type": Multicast}, - "unknown_unicast": {"type": UnknownUnicast}, - "_custom_data": {"type": dict}, - } - all: All - """Subclass of AvdModel.""" - broadcast: Broadcast - """Subclass of AvdModel.""" - multicast: Multicast - """Subclass of AvdModel.""" - unknown_unicast: UnknownUnicast - """Subclass of AvdModel.""" - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__( - self, - *, - all: All | UndefinedType = Undefined, - broadcast: Broadcast | UndefinedType = Undefined, - multicast: Multicast | UndefinedType = Undefined, - unknown_unicast: UnknownUnicast | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - StormControl. - - - Subclass of AvdModel. + key_type: Configure authentication key type. + key: Password string. `key_type` is required for this setting. + key_ids: Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`). + mode: Authentication mode. + sha: + Required settings for authentication mode 'sha'. - Args: - all: Subclass of AvdModel. - broadcast: Subclass of AvdModel. - multicast: Subclass of AvdModel. - unknown_unicast: Subclass of AvdModel. - _custom_data: _custom_data + Subclass of AvdModel. + shared_secret: + Required settings for authentication mode 'shared_secret'. - """ + Subclass of AvdModel. + rx_disabled: Disable authentication check on the receive side. + _custom_data: _custom_data - class IsisAuthentication(AvdModel): - """Subclass of AvdModel.""" + """ - class Both(AvdModel): + class Level2(AvdModel): """Subclass of AvdModel.""" class KeyIdsItem(AvdModel): @@ -28640,12 +29744,163 @@ def __init__( """ shared_secret: SharedSecret """ - Required settings for authentication mode 'shared_secret'. + Required settings for authentication mode 'shared_secret'. + + Subclass of AvdModel. + """ + rx_disabled: bool | None + """Disable authentication check on the receive side.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + key_type: Literal["0", "7", "8a"] | None | UndefinedType = Undefined, + key: str | None | UndefinedType = Undefined, + key_ids: KeyIds | UndefinedType = Undefined, + mode: Literal["md5", "sha", "text", "shared-secret"] | None | UndefinedType = Undefined, + sha: Sha | UndefinedType = Undefined, + shared_secret: SharedSecret | UndefinedType = Undefined, + rx_disabled: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Level2. + + + Subclass of AvdModel. + + Args: + key_type: Configure authentication key type. + key: Password string. `key_type` is required for this setting. + key_ids: Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`). + mode: Authentication mode. + sha: + Required settings for authentication mode 'sha'. + + Subclass of AvdModel. + shared_secret: + Required settings for authentication mode 'shared_secret'. + + Subclass of AvdModel. + rx_disabled: Disable authentication check on the receive side. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = {"both": {"type": Both}, "level_1": {"type": Level1}, "level_2": {"type": Level2}, "_custom_data": {"type": dict}} + both: Both + """ + Authentication settings for level-1 and level-2. 'both' takes precedence over 'level_1' and + 'level_2' settings. + + Subclass of AvdModel. + """ + level_1: Level1 + """ + Authentication settings for level-1. 'both' takes precedence over 'level_1' and 'level_2' settings. + Subclass of AvdModel. + """ + level_2: Level2 + """ + Authentication settings for level-2. 'both' takes precedence over 'level_1' and 'level_2' settings. + Subclass of AvdModel. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + both: Both | UndefinedType = Undefined, + level_1: Level1 | UndefinedType = Undefined, + level_2: Level2 | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + IsisAuthentication. + + + Subclass of AvdModel. + + Args: + both: + Authentication settings for level-1 and level-2. 'both' takes precedence over 'level_1' and + 'level_2' settings. + + Subclass of AvdModel. + level_1: + Authentication settings for level-1. 'both' takes precedence over 'level_1' and 'level_2' settings. + Subclass of AvdModel. + level_2: + Authentication settings for level-2. 'both' takes precedence over 'level_1' and 'level_2' settings. + Subclass of AvdModel. + _custom_data: _custom_data + + """ + + class TrafficPolicy(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"input": {"type": str}, "output": {"type": str}, "_custom_data": {"type": dict}} + input: str | None + """Ingress traffic policy.""" + output: str | None + """Egress traffic policy.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + input: str | None | UndefinedType = Undefined, + output: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + TrafficPolicy. + + + Subclass of AvdModel. + + Args: + input: Ingress traffic policy. + output: Egress traffic policy. + _custom_data: _custom_data + + """ + + class EvpnEthernetSegment(AvdModel): + """Subclass of AvdModel.""" + + class DesignatedForwarderElection(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = { + "algorithm": {"type": str}, + "preference_value": {"type": int}, + "dont_preempt": {"type": bool, "default": False}, + "hold_time": {"type": int}, + "subsequent_hold_time": {"type": int}, + "candidate_reachability_required": {"type": bool}, + "_custom_data": {"type": dict}, + } + algorithm: Literal["modulus", "preference"] | None + preference_value: int | None + """Preference_value is only used when "algorithm" is "preference".""" + dont_preempt: bool + """ + Dont_preempt is only used when "algorithm" is "preference". - Subclass of AvdModel. + Default value: `False` """ - rx_disabled: bool | None - """Disable authentication check on the receive side.""" + hold_time: int | None + subsequent_hold_time: int | None + candidate_reachability_required: bool | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28653,127 +29908,149 @@ def __init__( def __init__( self, *, - key_type: Literal["0", "7", "8a"] | None | UndefinedType = Undefined, - key: str | None | UndefinedType = Undefined, - key_ids: KeyIds | UndefinedType = Undefined, - mode: Literal["md5", "sha", "text", "shared-secret"] | None | UndefinedType = Undefined, - sha: Sha | UndefinedType = Undefined, - shared_secret: SharedSecret | UndefinedType = Undefined, - rx_disabled: bool | None | UndefinedType = Undefined, + algorithm: Literal["modulus", "preference"] | None | UndefinedType = Undefined, + preference_value: int | None | UndefinedType = Undefined, + dont_preempt: bool | UndefinedType = Undefined, + hold_time: int | None | UndefinedType = Undefined, + subsequent_hold_time: int | None | UndefinedType = Undefined, + candidate_reachability_required: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Both. + DesignatedForwarderElection. Subclass of AvdModel. Args: - key_type: Configure authentication key type. - key: Password string. `key_type` is required for this setting. - key_ids: Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`). - mode: Authentication mode. - sha: - Required settings for authentication mode 'sha'. - - Subclass of AvdModel. - shared_secret: - Required settings for authentication mode 'shared_secret'. - - Subclass of AvdModel. - rx_disabled: Disable authentication check on the receive side. + algorithm: algorithm + preference_value: Preference_value is only used when "algorithm" is "preference". + dont_preempt: Dont_preempt is only used when "algorithm" is "preference". + hold_time: hold_time + subsequent_hold_time: subsequent_hold_time + candidate_reachability_required: candidate_reachability_required _custom_data: _custom_data """ - class Level1(AvdModel): + class Mpls(AvdModel): """Subclass of AvdModel.""" - class KeyIdsItem(AvdModel): - """Subclass of AvdModel.""" + _fields: ClassVar[dict] = {"shared_index": {"type": int}, "tunnel_flood_filter_time": {"type": int}, "_custom_data": {"type": dict}} + shared_index: int | None + tunnel_flood_filter_time: int | None + _custom_data: dict[str, Any] - _fields: ClassVar[dict] = { - "id": {"type": int}, - "algorithm": {"type": str}, - "key_type": {"type": str}, - "key": {"type": str}, - "rfc_5310": {"type": bool}, - "_custom_data": {"type": dict}, - } - id: int - """Configure authentication key-id.""" - algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] - key_type: Literal["0", "7", "8a"] - """Configure authentication key type.""" - key: str - """Password string.""" - rfc_5310: bool | None - """SHA digest computation according to rfc5310.""" - _custom_data: dict[str, Any] + if TYPE_CHECKING: - if TYPE_CHECKING: + def __init__( + self, + *, + shared_index: int | None | UndefinedType = Undefined, + tunnel_flood_filter_time: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Mpls. - def __init__( - self, - *, - id: int | UndefinedType = Undefined, - algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, - key_type: Literal["0", "7", "8a"] | UndefinedType = Undefined, - key: str | UndefinedType = Undefined, - rfc_5310: bool | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - KeyIdsItem. + Subclass of AvdModel. - Subclass of AvdModel. + Args: + shared_index: shared_index + tunnel_flood_filter_time: tunnel_flood_filter_time + _custom_data: _custom_data - Args: - id: Configure authentication key-id. - algorithm: algorithm - key_type: Configure authentication key type. - key: Password string. - rfc_5310: SHA digest computation according to rfc5310. - _custom_data: _custom_data + """ - """ + _fields: ClassVar[dict] = { + "identifier": {"type": str}, + "redundancy": {"type": str}, + "designated_forwarder_election": {"type": DesignatedForwarderElection}, + "mpls": {"type": Mpls}, + "route_target": {"type": str}, + "_custom_data": {"type": dict}, + } + identifier: str | None + """EVPN Ethernet Segment Identifier (Type 1 format).""" + redundancy: Literal["all-active", "single-active"] | None + designated_forwarder_election: DesignatedForwarderElection + """Subclass of AvdModel.""" + mpls: Mpls + """Subclass of AvdModel.""" + route_target: str | None + """EVPN Route Target for ESI with format xx:xx:xx:xx:xx:xx.""" + _custom_data: dict[str, Any] - class KeyIds(AvdIndexedList[int, KeyIdsItem]): - """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" + if TYPE_CHECKING: - _primary_key: ClassVar[str] = "id" + def __init__( + self, + *, + identifier: str | None | UndefinedType = Undefined, + redundancy: Literal["all-active", "single-active"] | None | UndefinedType = Undefined, + designated_forwarder_election: DesignatedForwarderElection | UndefinedType = Undefined, + mpls: Mpls | UndefinedType = Undefined, + route_target: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + EvpnEthernetSegment. - KeyIds._item_type = KeyIdsItem - class Sha(AvdModel): - """Subclass of AvdModel.""" + Subclass of AvdModel. - _fields: ClassVar[dict] = {"key_id": {"type": int}, "_custom_data": {"type": dict}} - key_id: int - _custom_data: dict[str, Any] + Args: + identifier: EVPN Ethernet Segment Identifier (Type 1 format). + redundancy: redundancy + designated_forwarder_election: Subclass of AvdModel. + mpls: Subclass of AvdModel. + route_target: EVPN Route Target for ESI with format xx:xx:xx:xx:xx:xx. + _custom_data: _custom_data - if TYPE_CHECKING: + """ - def __init__(self, *, key_id: int | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: - """ - Sha. + class Ptp(AvdModel): + """Subclass of AvdModel.""" + class Announce(AvdModel): + """Subclass of AvdModel.""" - Subclass of AvdModel. + _fields: ClassVar[dict] = {"interval": {"type": int}, "timeout": {"type": int}, "_custom_data": {"type": dict}} + interval: int | None + timeout: int | None + _custom_data: dict[str, Any] - Args: - key_id: key_id - _custom_data: _custom_data + if TYPE_CHECKING: - """ + def __init__( + self, + *, + interval: int | None | UndefinedType = Undefined, + timeout: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Announce. - class SharedSecret(AvdModel): + + Subclass of AvdModel. + + Args: + interval: interval + timeout: timeout + _custom_data: _custom_data + + """ + + class Profile(AvdModel): + """Subclass of AvdModel.""" + + class G82751(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"profile": {"type": str}, "algorithm": {"type": str}, "_custom_data": {"type": dict}} - profile: str - algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] + _fields: ClassVar[dict] = {"destination_mac_address": {"type": str}, "_custom_data": {"type": dict}} + destination_mac_address: Literal["forwardable", "non-forwardable"] | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28781,118 +30058,164 @@ class SharedSecret(AvdModel): def __init__( self, *, - profile: str | UndefinedType = Undefined, - algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, + destination_mac_address: Literal["forwardable", "non-forwardable"] | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - SharedSecret. + G82751. Subclass of AvdModel. Args: - profile: profile - algorithm: algorithm + destination_mac_address: destination_mac_address _custom_data: _custom_data """ - _fields: ClassVar[dict] = { - "key_type": {"type": str}, - "key": {"type": str}, - "key_ids": {"type": KeyIds}, - "mode": {"type": str}, - "sha": {"type": Sha}, - "shared_secret": {"type": SharedSecret}, - "rx_disabled": {"type": bool}, - "_custom_data": {"type": dict}, - } - key_type: Literal["0", "7", "8a"] | None - """Configure authentication key type.""" - key: str | None - """Password string. `key_type` is required for this setting.""" - key_ids: KeyIds - """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" - mode: Literal["md5", "sha", "text", "shared-secret"] | None - """Authentication mode.""" - sha: Sha - """ - Required settings for authentication mode 'sha'. + _fields: ClassVar[dict] = {"g8275_1": {"type": G82751}, "_custom_data": {"type": dict}} + g8275_1: G82751 + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] - Subclass of AvdModel. - """ - shared_secret: SharedSecret - """ - Required settings for authentication mode 'shared_secret'. + if TYPE_CHECKING: + + def __init__(self, *, g8275_1: G82751 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Profile. + + + Subclass of AvdModel. + + Args: + g8275_1: Subclass of AvdModel. + _custom_data: _custom_data + + """ + + class SyncMessage(AvdModel): + """Subclass of AvdModel.""" - Subclass of AvdModel. - """ - rx_disabled: bool | None - """Disable authentication check on the receive side.""" + _fields: ClassVar[dict] = {"interval": {"type": int}, "_custom_data": {"type": dict}} + interval: int | None _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__( - self, - *, - key_type: Literal["0", "7", "8a"] | None | UndefinedType = Undefined, - key: str | None | UndefinedType = Undefined, - key_ids: KeyIds | UndefinedType = Undefined, - mode: Literal["md5", "sha", "text", "shared-secret"] | None | UndefinedType = Undefined, - sha: Sha | UndefinedType = Undefined, - shared_secret: SharedSecret | UndefinedType = Undefined, - rx_disabled: bool | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + def __init__(self, *, interval: int | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: """ - Level1. + SyncMessage. Subclass of AvdModel. Args: - key_type: Configure authentication key type. - key: Password string. `key_type` is required for this setting. - key_ids: Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`). - mode: Authentication mode. - sha: - Required settings for authentication mode 'sha'. - - Subclass of AvdModel. - shared_secret: - Required settings for authentication mode 'shared_secret'. - - Subclass of AvdModel. - rx_disabled: Disable authentication check on the receive side. + interval: interval _custom_data: _custom_data """ - class Level2(AvdModel): + _fields: ClassVar[dict] = { + "enable": {"type": bool}, + "announce": {"type": Announce}, + "delay_req": {"type": int}, + "delay_mechanism": {"type": str}, + "profile": {"type": Profile}, + "sync_message": {"type": SyncMessage}, + "role": {"type": str}, + "vlan": {"type": str}, + "transport": {"type": str}, + "mpass": {"type": bool}, + "_custom_data": {"type": dict}, + } + enable: bool | None + announce: Announce + """Subclass of AvdModel.""" + delay_req: int | None + delay_mechanism: Literal["e2e", "p2p"] | None + profile: Profile + """Subclass of AvdModel.""" + sync_message: SyncMessage + """Subclass of AvdModel.""" + role: Literal["master", "dynamic"] | None + vlan: str | None + """VLAN can be 'all' or list of vlans as string.""" + transport: Literal["ipv4", "ipv6", "layer2"] | None + mpass: bool | None + """ + When MPASS is enabled on an MLAG port-channel, MLAG peers coordinate to function as a single PTP + logical device. + Arista PTP enabled devices always place PTP messages on the same physical link + within the port-channel. + Hence, MPASS is needed only on MLAG port-channels connected to non-Arista + devices. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + enable: bool | None | UndefinedType = Undefined, + announce: Announce | UndefinedType = Undefined, + delay_req: int | None | UndefinedType = Undefined, + delay_mechanism: Literal["e2e", "p2p"] | None | UndefinedType = Undefined, + profile: Profile | UndefinedType = Undefined, + sync_message: SyncMessage | UndefinedType = Undefined, + role: Literal["master", "dynamic"] | None | UndefinedType = Undefined, + vlan: str | None | UndefinedType = Undefined, + transport: Literal["ipv4", "ipv6", "layer2"] | None | UndefinedType = Undefined, + mpass: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Ptp. + + + Subclass of AvdModel. + + Args: + enable: enable + announce: Subclass of AvdModel. + delay_req: delay_req + delay_mechanism: delay_mechanism + profile: Subclass of AvdModel. + sync_message: Subclass of AvdModel. + role: role + vlan: VLAN can be 'all' or list of vlans as string. + transport: transport + mpass: + When MPASS is enabled on an MLAG port-channel, MLAG peers coordinate to function as a single PTP + logical device. + Arista PTP enabled devices always place PTP messages on the same physical link + within the port-channel. + Hence, MPASS is needed only on MLAG port-channels connected to non-Arista + devices. + _custom_data: _custom_data + + """ + + class IpNat(AvdModel): + """Subclass of AvdModel.""" + + class Destination(AvdModel): """Subclass of AvdModel.""" - class KeyIdsItem(AvdModel): + class DynamicItem(AvdModel): """Subclass of AvdModel.""" _fields: ClassVar[dict] = { - "id": {"type": int}, - "algorithm": {"type": str}, - "key_type": {"type": str}, - "key": {"type": str}, - "rfc_5310": {"type": bool}, + "access_list": {"type": str}, + "comment": {"type": str}, + "pool_name": {"type": str}, + "priority": {"type": int}, "_custom_data": {"type": dict}, } - id: int - """Configure authentication key-id.""" - algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] - key_type: Literal["0", "7", "8a"] - """Configure authentication key type.""" - key: str - """Password string.""" - rfc_5310: bool | None - """SHA digest computation according to rfc5310.""" + access_list: str + comment: str | None + pool_name: str + priority: int | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28900,64 +30223,72 @@ class KeyIdsItem(AvdModel): def __init__( self, *, - id: int | UndefinedType = Undefined, - algorithm: Literal["sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, - key_type: Literal["0", "7", "8a"] | UndefinedType = Undefined, - key: str | UndefinedType = Undefined, - rfc_5310: bool | None | UndefinedType = Undefined, + access_list: str | UndefinedType = Undefined, + comment: str | None | UndefinedType = Undefined, + pool_name: str | UndefinedType = Undefined, + priority: int | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - KeyIdsItem. + DynamicItem. Subclass of AvdModel. Args: - id: Configure authentication key-id. - algorithm: algorithm - key_type: Configure authentication key type. - key: Password string. - rfc_5310: SHA digest computation according to rfc5310. + access_list: access_list + comment: comment + pool_name: pool_name + priority: priority _custom_data: _custom_data """ - class KeyIds(AvdIndexedList[int, KeyIdsItem]): - """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" - - _primary_key: ClassVar[str] = "id" - - KeyIds._item_type = KeyIdsItem - - class Sha(AvdModel): - """Subclass of AvdModel.""" - - _fields: ClassVar[dict] = {"key_id": {"type": int}, "_custom_data": {"type": dict}} - key_id: int - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__(self, *, key_id: int | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: - """ - Sha. - - - Subclass of AvdModel. + class Dynamic(AvdIndexedList[str, DynamicItem]): + """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" - Args: - key_id: key_id - _custom_data: _custom_data + _primary_key: ClassVar[str] = "access_list" - """ + Dynamic._item_type = DynamicItem - class SharedSecret(AvdModel): + class StaticItem(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"profile": {"type": str}, "algorithm": {"type": str}, "_custom_data": {"type": dict}} - profile: str - algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] + _fields: ClassVar[dict] = { + "access_list": {"type": str}, + "comment": {"type": str}, + "direction": {"type": str}, + "group": {"type": int}, + "original_ip": {"type": str}, + "original_port": {"type": int}, + "priority": {"type": int}, + "protocol": {"type": str}, + "translated_ip": {"type": str}, + "translated_port": {"type": int}, + "_custom_data": {"type": dict}, + } + access_list: str | None + """'access_list' and 'group' are mutual exclusive.""" + comment: str | None + direction: Literal["egress", "ingress"] | None + """ + Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware + platform. + EOS might remove this keyword in the configuration. So, check the configuration on + targeted HW/SW. + """ + group: int | None + """'access_list' and 'group' are mutual exclusive.""" + original_ip: str | None + """IPv4 address. The combination of `original_ip` and `original_port` must be unique.""" + original_port: int | None + """TCP/UDP port. The combination of `original_ip` and `original_port` must be unique.""" + priority: int | None + protocol: Literal["udp", "tcp"] | None + translated_ip: str + """IPv4 address.""" + translated_port: int | None + """requires 'original_port'.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -28965,55 +30296,53 @@ class SharedSecret(AvdModel): def __init__( self, *, - profile: str | UndefinedType = Undefined, - algorithm: Literal["md5", "sha-1", "sha-224", "sha-256", "sha-384", "sha-512"] | UndefinedType = Undefined, + access_list: str | None | UndefinedType = Undefined, + comment: str | None | UndefinedType = Undefined, + direction: Literal["egress", "ingress"] | None | UndefinedType = Undefined, + group: int | None | UndefinedType = Undefined, + original_ip: str | None | UndefinedType = Undefined, + original_port: int | None | UndefinedType = Undefined, + priority: int | None | UndefinedType = Undefined, + protocol: Literal["udp", "tcp"] | None | UndefinedType = Undefined, + translated_ip: str | UndefinedType = Undefined, + translated_port: int | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - SharedSecret. + StaticItem. Subclass of AvdModel. Args: - profile: profile - algorithm: algorithm + access_list: 'access_list' and 'group' are mutual exclusive. + comment: comment + direction: + Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware + platform. + EOS might remove this keyword in the configuration. So, check the configuration on + targeted HW/SW. + group: 'access_list' and 'group' are mutual exclusive. + original_ip: IPv4 address. The combination of `original_ip` and `original_port` must be unique. + original_port: TCP/UDP port. The combination of `original_ip` and `original_port` must be unique. + priority: priority + protocol: protocol + translated_ip: IPv4 address. + translated_port: requires 'original_port'. _custom_data: _custom_data """ - _fields: ClassVar[dict] = { - "key_type": {"type": str}, - "key": {"type": str}, - "key_ids": {"type": KeyIds}, - "mode": {"type": str}, - "sha": {"type": Sha}, - "shared_secret": {"type": SharedSecret}, - "rx_disabled": {"type": bool}, - "_custom_data": {"type": dict}, - } - key_type: Literal["0", "7", "8a"] | None - """Configure authentication key type.""" - key: str | None - """Password string. `key_type` is required for this setting.""" - key_ids: KeyIds - """Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`).""" - mode: Literal["md5", "sha", "text", "shared-secret"] | None - """Authentication mode.""" - sha: Sha - """ - Required settings for authentication mode 'sha'. + class Static(AvdList[StaticItem]): + """Subclass of AvdList with `StaticItem` items.""" - Subclass of AvdModel. - """ - shared_secret: SharedSecret - """ - Required settings for authentication mode 'shared_secret'. + Static._item_type = StaticItem - Subclass of AvdModel. - """ - rx_disabled: bool | None - """Disable authentication check on the receive side.""" + _fields: ClassVar[dict] = {"dynamic": {"type": Dynamic}, "static": {"type": Static}, "_custom_data": {"type": dict}} + dynamic: Dynamic + """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" + static: Static + """Subclass of AvdList with `StaticItem` items.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29021,150 +30350,179 @@ def __init__( def __init__( self, *, - key_type: Literal["0", "7", "8a"] | None | UndefinedType = Undefined, - key: str | None | UndefinedType = Undefined, - key_ids: KeyIds | UndefinedType = Undefined, - mode: Literal["md5", "sha", "text", "shared-secret"] | None | UndefinedType = Undefined, - sha: Sha | UndefinedType = Undefined, - shared_secret: SharedSecret | UndefinedType = Undefined, - rx_disabled: bool | None | UndefinedType = Undefined, + dynamic: Dynamic | UndefinedType = Undefined, + static: Static | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Level2. + Destination. Subclass of AvdModel. Args: - key_type: Configure authentication key type. - key: Password string. `key_type` is required for this setting. - key_ids: Subclass of AvdIndexedList with `KeyIdsItem` items. Primary key is `id` (`int`). - mode: Authentication mode. - sha: - Required settings for authentication mode 'sha'. - - Subclass of AvdModel. - shared_secret: - Required settings for authentication mode 'shared_secret'. - - Subclass of AvdModel. - rx_disabled: Disable authentication check on the receive side. + dynamic: Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`). + static: Subclass of AvdList with `StaticItem` items. _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"both": {"type": Both}, "level_1": {"type": Level1}, "level_2": {"type": Level2}, "_custom_data": {"type": dict}} - both: Both - """ - Authentication settings for level-1 and level-2. 'both' takes precedence over 'level_1' and - 'level_2' settings. - - Subclass of AvdModel. - """ - level_1: Level1 - """ - Authentication settings for level-1. 'both' takes precedence over 'level_1' and 'level_2' settings. - Subclass of AvdModel. - """ - level_2: Level2 - """ - Authentication settings for level-2. 'both' takes precedence over 'level_1' and 'level_2' settings. - Subclass of AvdModel. - """ - _custom_data: dict[str, Any] + class Source(AvdModel): + """Subclass of AvdModel.""" - if TYPE_CHECKING: + class DynamicItem(AvdModel): + """Subclass of AvdModel.""" - def __init__( - self, - *, - both: Both | UndefinedType = Undefined, - level_1: Level1 | UndefinedType = Undefined, - level_2: Level2 | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + _fields: ClassVar[dict] = { + "access_list": {"type": str}, + "comment": {"type": str}, + "nat_type": {"type": str}, + "pool_name": {"type": str}, + "priority": {"type": int}, + "_custom_data": {"type": dict}, + } + access_list: str + comment: str | None + nat_type: Literal["overload", "pool", "pool-address-only", "pool-full-cone"] + pool_name: str | None """ - IsisAuthentication. + required if 'nat_type' is pool, pool-address-only or pool-full-cone. + ignored if 'nat_type' is + overload. + """ + priority: int | None + _custom_data: dict[str, Any] + if TYPE_CHECKING: - Subclass of AvdModel. + def __init__( + self, + *, + access_list: str | UndefinedType = Undefined, + comment: str | None | UndefinedType = Undefined, + nat_type: Literal["overload", "pool", "pool-address-only", "pool-full-cone"] | UndefinedType = Undefined, + pool_name: str | None | UndefinedType = Undefined, + priority: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + DynamicItem. - Args: - both: - Authentication settings for level-1 and level-2. 'both' takes precedence over 'level_1' and - 'level_2' settings. - Subclass of AvdModel. - level_1: - Authentication settings for level-1. 'both' takes precedence over 'level_1' and 'level_2' settings. - Subclass of AvdModel. - level_2: - Authentication settings for level-2. 'both' takes precedence over 'level_1' and 'level_2' settings. - Subclass of AvdModel. - _custom_data: _custom_data + Subclass of AvdModel. - """ + Args: + access_list: access_list + comment: comment + nat_type: nat_type + pool_name: + required if 'nat_type' is pool, pool-address-only or pool-full-cone. + ignored if 'nat_type' is + overload. + priority: priority + _custom_data: _custom_data - class TrafficPolicy(AvdModel): - """Subclass of AvdModel.""" + """ - _fields: ClassVar[dict] = {"input": {"type": str}, "output": {"type": str}, "_custom_data": {"type": dict}} - input: str | None - """Ingress traffic policy.""" - output: str | None - """Egress traffic policy.""" - _custom_data: dict[str, Any] + class Dynamic(AvdIndexedList[str, DynamicItem]): + """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" - if TYPE_CHECKING: + _primary_key: ClassVar[str] = "access_list" - def __init__( - self, - *, - input: str | None | UndefinedType = Undefined, - output: str | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + Dynamic._item_type = DynamicItem + + class StaticItem(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = { + "access_list": {"type": str}, + "comment": {"type": str}, + "direction": {"type": str}, + "group": {"type": int}, + "original_ip": {"type": str}, + "original_port": {"type": int}, + "priority": {"type": int}, + "protocol": {"type": str}, + "translated_ip": {"type": str}, + "translated_port": {"type": int}, + "_custom_data": {"type": dict}, + } + access_list: str | None + """'access_list' and 'group' are mutual exclusive.""" + comment: str | None + direction: Literal["egress", "ingress"] | None """ - TrafficPolicy. + Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware + platform. + EOS might remove this keyword in the configuration. So, check the configuration on + targeted HW/SW. + """ + group: int | None + """'access_list' and 'group' are mutual exclusive.""" + original_ip: str | None + """IPv4 address. The combination of `original_ip` and `original_port` must be unique.""" + original_port: int | None + """TCP/UDP port. The combination of `original_ip` and `original_port` must be unique.""" + priority: int | None + protocol: Literal["udp", "tcp"] | None + translated_ip: str + """IPv4 address.""" + translated_port: int | None + """requires 'original_port'.""" + _custom_data: dict[str, Any] + if TYPE_CHECKING: - Subclass of AvdModel. + def __init__( + self, + *, + access_list: str | None | UndefinedType = Undefined, + comment: str | None | UndefinedType = Undefined, + direction: Literal["egress", "ingress"] | None | UndefinedType = Undefined, + group: int | None | UndefinedType = Undefined, + original_ip: str | None | UndefinedType = Undefined, + original_port: int | None | UndefinedType = Undefined, + priority: int | None | UndefinedType = Undefined, + protocol: Literal["udp", "tcp"] | None | UndefinedType = Undefined, + translated_ip: str | UndefinedType = Undefined, + translated_port: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + StaticItem. - Args: - input: Ingress traffic policy. - output: Egress traffic policy. - _custom_data: _custom_data - """ + Subclass of AvdModel. + + Args: + access_list: 'access_list' and 'group' are mutual exclusive. + comment: comment + direction: + Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware + platform. + EOS might remove this keyword in the configuration. So, check the configuration on + targeted HW/SW. + group: 'access_list' and 'group' are mutual exclusive. + original_ip: IPv4 address. The combination of `original_ip` and `original_port` must be unique. + original_port: TCP/UDP port. The combination of `original_ip` and `original_port` must be unique. + priority: priority + protocol: protocol + translated_ip: IPv4 address. + translated_port: requires 'original_port'. + _custom_data: _custom_data - class EvpnEthernetSegment(AvdModel): - """Subclass of AvdModel.""" + """ - class DesignatedForwarderElection(AvdModel): - """Subclass of AvdModel.""" + class Static(AvdList[StaticItem]): + """Subclass of AvdList with `StaticItem` items.""" - _fields: ClassVar[dict] = { - "algorithm": {"type": str}, - "preference_value": {"type": int}, - "dont_preempt": {"type": bool, "default": False}, - "hold_time": {"type": int}, - "subsequent_hold_time": {"type": int}, - "candidate_reachability_required": {"type": bool}, - "_custom_data": {"type": dict}, - } - algorithm: Literal["modulus", "preference"] | None - preference_value: int | None - """Preference_value is only used when "algorithm" is "preference".""" - dont_preempt: bool - """ - Dont_preempt is only used when "algorithm" is "preference". + Static._item_type = StaticItem - Default value: `False` - """ - hold_time: int | None - subsequent_hold_time: int | None - candidate_reachability_required: bool | None + _fields: ClassVar[dict] = {"dynamic": {"type": Dynamic}, "static": {"type": Static}, "_custom_data": {"type": dict}} + dynamic: Dynamic + """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" + static: Static + """Subclass of AvdList with `StaticItem` items.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29172,78 +30530,68 @@ class DesignatedForwarderElection(AvdModel): def __init__( self, *, - algorithm: Literal["modulus", "preference"] | None | UndefinedType = Undefined, - preference_value: int | None | UndefinedType = Undefined, - dont_preempt: bool | UndefinedType = Undefined, - hold_time: int | None | UndefinedType = Undefined, - subsequent_hold_time: int | None | UndefinedType = Undefined, - candidate_reachability_required: bool | None | UndefinedType = Undefined, + dynamic: Dynamic | UndefinedType = Undefined, + static: Static | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - DesignatedForwarderElection. + Source. Subclass of AvdModel. Args: - algorithm: algorithm - preference_value: Preference_value is only used when "algorithm" is "preference". - dont_preempt: Dont_preempt is only used when "algorithm" is "preference". - hold_time: hold_time - subsequent_hold_time: subsequent_hold_time - candidate_reachability_required: candidate_reachability_required + dynamic: Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`). + static: Subclass of AvdList with `StaticItem` items. _custom_data: _custom_data """ - class Mpls(AvdModel): - """Subclass of AvdModel.""" + _fields: ClassVar[dict] = {"destination": {"type": Destination}, "source": {"type": Source}, "_custom_data": {"type": dict}} + destination: Destination + """Subclass of AvdModel.""" + source: Source + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] - _fields: ClassVar[dict] = {"shared_index": {"type": int}, "tunnel_flood_filter_time": {"type": int}, "_custom_data": {"type": dict}} - shared_index: int | None - tunnel_flood_filter_time: int | None - _custom_data: dict[str, Any] + if TYPE_CHECKING: - if TYPE_CHECKING: + def __init__( + self, + *, + destination: Destination | UndefinedType = Undefined, + source: Source | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + IpNat. - def __init__( - self, - *, - shared_index: int | None | UndefinedType = Undefined, - tunnel_flood_filter_time: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Mpls. + Subclass of AvdModel. - Subclass of AvdModel. + Args: + destination: Subclass of AvdModel. + source: Subclass of AvdModel. + _custom_data: _custom_data - Args: - shared_index: shared_index - tunnel_flood_filter_time: tunnel_flood_filter_time - _custom_data: _custom_data + """ - """ + class Ipv6NdPrefixesItem(AvdModel): + """Subclass of AvdModel.""" _fields: ClassVar[dict] = { - "identifier": {"type": str}, - "redundancy": {"type": str}, - "designated_forwarder_election": {"type": DesignatedForwarderElection}, - "mpls": {"type": Mpls}, - "route_target": {"type": str}, + "ipv6_prefix": {"type": str}, + "valid_lifetime": {"type": str}, + "preferred_lifetime": {"type": str}, + "no_autoconfig_flag": {"type": bool}, "_custom_data": {"type": dict}, } - identifier: str | None - """EVPN Ethernet Segment Identifier (Type 1 format).""" - redundancy: Literal["all-active", "single-active"] | None - designated_forwarder_election: DesignatedForwarderElection - """Subclass of AvdModel.""" - mpls: Mpls - """Subclass of AvdModel.""" - route_target: str | None - """EVPN Route Target for ESI with format xx:xx:xx:xx:xx:xx.""" + ipv6_prefix: str + valid_lifetime: str | None + """Infinite or lifetime in seconds.""" + preferred_lifetime: str | None + """Infinite or lifetime in seconds.""" + no_autoconfig_flag: bool | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29251,38 +30599,90 @@ def __init__( def __init__( self, *, - identifier: str | None | UndefinedType = Undefined, - redundancy: Literal["all-active", "single-active"] | None | UndefinedType = Undefined, - designated_forwarder_election: DesignatedForwarderElection | UndefinedType = Undefined, - mpls: Mpls | UndefinedType = Undefined, - route_target: str | None | UndefinedType = Undefined, + ipv6_prefix: str | UndefinedType = Undefined, + valid_lifetime: str | None | UndefinedType = Undefined, + preferred_lifetime: str | None | UndefinedType = Undefined, + no_autoconfig_flag: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - EvpnEthernetSegment. + Ipv6NdPrefixesItem. Subclass of AvdModel. Args: - identifier: EVPN Ethernet Segment Identifier (Type 1 format). - redundancy: redundancy - designated_forwarder_election: Subclass of AvdModel. - mpls: Subclass of AvdModel. - route_target: EVPN Route Target for ESI with format xx:xx:xx:xx:xx:xx. + ipv6_prefix: ipv6_prefix + valid_lifetime: Infinite or lifetime in seconds. + preferred_lifetime: Infinite or lifetime in seconds. + no_autoconfig_flag: no_autoconfig_flag _custom_data: _custom_data """ - class Ptp(AvdModel): + class Ipv6NdPrefixes(AvdIndexedList[str, Ipv6NdPrefixesItem]): + """Subclass of AvdIndexedList with `Ipv6NdPrefixesItem` items. Primary key is `ipv6_prefix` (`str`).""" + + _primary_key: ClassVar[str] = "ipv6_prefix" + + Ipv6NdPrefixes._item_type = Ipv6NdPrefixesItem + + class Pim(AvdModel): """Subclass of AvdModel.""" - class Announce(AvdModel): + class Ipv4(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"interval": {"type": int}, "timeout": {"type": int}, "_custom_data": {"type": dict}} - interval: int | None - timeout: int | None + class Hello(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"count": {"type": str}, "interval": {"type": int}, "_custom_data": {"type": dict}} + count: str | None + """Number of missed hellos after which the neighbor expires. Range <1.5-65535>.""" + interval: int | None + """PIM hello interval in seconds.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + count: str | None | UndefinedType = Undefined, + interval: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Hello. + + + Subclass of AvdModel. + + Args: + count: Number of missed hellos after which the neighbor expires. Range <1.5-65535>. + interval: PIM hello interval in seconds. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = { + "border_router": {"type": bool}, + "dr_priority": {"type": int}, + "sparse_mode": {"type": bool}, + "bfd": {"type": bool}, + "bidirectional": {"type": bool}, + "hello": {"type": Hello}, + "_custom_data": {"type": dict}, + } + border_router: bool | None + """Configure PIM border router. EOS default is false.""" + dr_priority: int | None + sparse_mode: bool | None + bfd: bool | None + """Set the default for whether Bidirectional Forwarding Detection is enabled for PIM.""" + bidirectional: bool | None + hello: Hello + """Subclass of AvdModel.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29290,130 +30690,100 @@ class Announce(AvdModel): def __init__( self, *, - interval: int | None | UndefinedType = Undefined, - timeout: int | None | UndefinedType = Undefined, + border_router: bool | None | UndefinedType = Undefined, + dr_priority: int | None | UndefinedType = Undefined, + sparse_mode: bool | None | UndefinedType = Undefined, + bfd: bool | None | UndefinedType = Undefined, + bidirectional: bool | None | UndefinedType = Undefined, + hello: Hello | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Announce. + Ipv4. Subclass of AvdModel. Args: - interval: interval - timeout: timeout + border_router: Configure PIM border router. EOS default is false. + dr_priority: dr_priority + sparse_mode: sparse_mode + bfd: Set the default for whether Bidirectional Forwarding Detection is enabled for PIM. + bidirectional: bidirectional + hello: Subclass of AvdModel. _custom_data: _custom_data """ - class Profile(AvdModel): - """Subclass of AvdModel.""" - - class G82751(AvdModel): - """Subclass of AvdModel.""" - - _fields: ClassVar[dict] = {"destination_mac_address": {"type": str}, "_custom_data": {"type": dict}} - destination_mac_address: Literal["forwardable", "non-forwardable"] | None - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__( - self, - *, - destination_mac_address: Literal["forwardable", "non-forwardable"] | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - G82751. - - - Subclass of AvdModel. + _fields: ClassVar[dict] = {"ipv4": {"type": Ipv4}, "_custom_data": {"type": dict}} + ipv4: Ipv4 + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] - Args: - destination_mac_address: destination_mac_address - _custom_data: _custom_data + if TYPE_CHECKING: - """ + def __init__(self, *, ipv4: Ipv4 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + Pim. - _fields: ClassVar[dict] = {"g8275_1": {"type": G82751}, "_custom_data": {"type": dict}} - g8275_1: G82751 - """Subclass of AvdModel.""" - _custom_data: dict[str, Any] - if TYPE_CHECKING: + Subclass of AvdModel. - def __init__(self, *, g8275_1: G82751 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: - """ - Profile. + Args: + ipv4: Subclass of AvdModel. + _custom_data: _custom_data + """ - Subclass of AvdModel. + class OspfMessageDigestKeysItem(AvdModel): + """Subclass of AvdModel.""" - Args: - g8275_1: Subclass of AvdModel. - _custom_data: _custom_data + _fields: ClassVar[dict] = {"id": {"type": int}, "hash_algorithm": {"type": str}, "key": {"type": str}, "_custom_data": {"type": dict}} + id: int + hash_algorithm: Literal["md5", "sha1", "sha256", "sha384", "sha512"] | None + key: str | None + """Encrypted password.""" + _custom_data: dict[str, Any] - """ + if TYPE_CHECKING: - class SyncMessage(AvdModel): - """Subclass of AvdModel.""" + def __init__( + self, + *, + id: int | UndefinedType = Undefined, + hash_algorithm: Literal["md5", "sha1", "sha256", "sha384", "sha512"] | None | UndefinedType = Undefined, + key: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + OspfMessageDigestKeysItem. - _fields: ClassVar[dict] = {"interval": {"type": int}, "_custom_data": {"type": dict}} - interval: int | None - _custom_data: dict[str, Any] - if TYPE_CHECKING: + Subclass of AvdModel. - def __init__(self, *, interval: int | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: - """ - SyncMessage. + Args: + id: id + hash_algorithm: hash_algorithm + key: Encrypted password. + _custom_data: _custom_data + """ - Subclass of AvdModel. + class OspfMessageDigestKeys(AvdIndexedList[int, OspfMessageDigestKeysItem]): + """Subclass of AvdIndexedList with `OspfMessageDigestKeysItem` items. Primary key is `id` (`int`).""" - Args: - interval: interval - _custom_data: _custom_data + _primary_key: ClassVar[str] = "id" - """ + OspfMessageDigestKeys._item_type = OspfMessageDigestKeysItem - _fields: ClassVar[dict] = { - "enable": {"type": bool}, - "announce": {"type": Announce}, - "delay_req": {"type": int}, - "delay_mechanism": {"type": str}, - "profile": {"type": Profile}, - "sync_message": {"type": SyncMessage}, - "role": {"type": str}, - "vlan": {"type": str}, - "transport": {"type": str}, - "mpass": {"type": bool}, - "_custom_data": {"type": dict}, - } - enable: bool | None - announce: Announce - """Subclass of AvdModel.""" - delay_req: int | None - delay_mechanism: Literal["e2e", "p2p"] | None - profile: Profile - """Subclass of AvdModel.""" - sync_message: SyncMessage + class FlowTracker(AvdModel): """Subclass of AvdModel.""" - role: Literal["master", "dynamic"] | None - vlan: str | None - """VLAN can be 'all' or list of vlans as string.""" - transport: Literal["ipv4", "ipv6", "layer2"] | None - mpass: bool | None - """ - When MPASS is enabled on an MLAG port-channel, MLAG peers coordinate to function as a single PTP - logical device. - Arista PTP enabled devices always place PTP messages on the same physical link - within the port-channel. - Hence, MPASS is needed only on MLAG port-channels connected to non-Arista - devices. - """ + + _fields: ClassVar[dict] = {"sampled": {"type": str}, "hardware": {"type": str}, "_custom_data": {"type": dict}} + sampled: str | None + """Sampled flow tracker name.""" + hardware: str | None + """Hardware flow tracker name.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29421,192 +30791,129 @@ def __init__(self, *, interval: int | None | UndefinedType = Undefined, _custom_ def __init__( self, *, - enable: bool | None | UndefinedType = Undefined, - announce: Announce | UndefinedType = Undefined, - delay_req: int | None | UndefinedType = Undefined, - delay_mechanism: Literal["e2e", "p2p"] | None | UndefinedType = Undefined, - profile: Profile | UndefinedType = Undefined, - sync_message: SyncMessage | UndefinedType = Undefined, - role: Literal["master", "dynamic"] | None | UndefinedType = Undefined, - vlan: str | None | UndefinedType = Undefined, - transport: Literal["ipv4", "ipv6", "layer2"] | None | UndefinedType = Undefined, - mpass: bool | None | UndefinedType = Undefined, + sampled: str | None | UndefinedType = Undefined, + hardware: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Ptp. + FlowTracker. Subclass of AvdModel. Args: - enable: enable - announce: Subclass of AvdModel. - delay_req: delay_req - delay_mechanism: delay_mechanism - profile: Subclass of AvdModel. - sync_message: Subclass of AvdModel. - role: role - vlan: VLAN can be 'all' or list of vlans as string. - transport: transport - mpass: - When MPASS is enabled on an MLAG port-channel, MLAG peers coordinate to function as a single PTP - logical device. - Arista PTP enabled devices always place PTP messages on the same physical link - within the port-channel. - Hence, MPASS is needed only on MLAG port-channels connected to non-Arista - devices. + sampled: Sampled flow tracker name. + hardware: Hardware flow tracker name. _custom_data: _custom_data """ - class IpNat(AvdModel): + class Bgp(AvdModel): """Subclass of AvdModel.""" - class Destination(AvdModel): + _fields: ClassVar[dict] = {"session_tracker": {"type": str}, "_custom_data": {"type": dict}} + session_tracker: str | None + """Name of session tracker.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, *, session_tracker: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined + ) -> None: + """ + Bgp. + + + Subclass of AvdModel. + + Args: + session_tracker: Name of session tracker. + _custom_data: _custom_data + + """ + + class IpIgmpHostProxy(AvdModel): + """Subclass of AvdModel.""" + + class GroupsItem(AvdModel): """Subclass of AvdModel.""" - class DynamicItem(AvdModel): + class ExcludeItem(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "access_list": {"type": str}, - "comment": {"type": str}, - "pool_name": {"type": str}, - "priority": {"type": int}, - "_custom_data": {"type": dict}, - } - access_list: str - comment: str | None - pool_name: str - priority: int | None + _fields: ClassVar[dict] = {"source": {"type": str}, "_custom_data": {"type": dict}} + source: str _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__( - self, - *, - access_list: str | UndefinedType = Undefined, - comment: str | None | UndefinedType = Undefined, - pool_name: str | UndefinedType = Undefined, - priority: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + def __init__(self, *, source: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: """ - DynamicItem. + ExcludeItem. Subclass of AvdModel. Args: - access_list: access_list - comment: comment - pool_name: pool_name - priority: priority + source: source _custom_data: _custom_data """ - class Dynamic(AvdIndexedList[str, DynamicItem]): - """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" - - _primary_key: ClassVar[str] = "access_list" + class Exclude(AvdIndexedList[str, ExcludeItem]): + """Subclass of AvdIndexedList with `ExcludeItem` items. Primary key is `source` (`str`).""" - Dynamic._item_type = DynamicItem + _primary_key: ClassVar[str] = "source" - class StaticItem(AvdModel): - """Subclass of AvdModel.""" + Exclude._item_type = ExcludeItem - _fields: ClassVar[dict] = { - "access_list": {"type": str}, - "comment": {"type": str}, - "direction": {"type": str}, - "group": {"type": int}, - "original_ip": {"type": str}, - "original_port": {"type": int}, - "priority": {"type": int}, - "protocol": {"type": str}, - "translated_ip": {"type": str}, - "translated_port": {"type": int}, - "_custom_data": {"type": dict}, - } - access_list: str | None - """'access_list' and 'group' are mutual exclusive.""" - comment: str | None - direction: Literal["egress", "ingress"] | None - """ - Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware - platform. - EOS might remove this keyword in the configuration. So, check the configuration on - targeted HW/SW. - """ - group: int | None - """'access_list' and 'group' are mutual exclusive.""" - original_ip: str | None - """IPv4 address. The combination of `original_ip` and `original_port` must be unique.""" - original_port: int | None - """TCP/UDP port. The combination of `original_ip` and `original_port` must be unique.""" - priority: int | None - protocol: Literal["udp", "tcp"] | None - translated_ip: str - """IPv4 address.""" - translated_port: int | None - """requires 'original_port'.""" + class IncludeItem(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"source": {"type": str}, "_custom_data": {"type": dict}} + source: str _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__( - self, - *, - access_list: str | None | UndefinedType = Undefined, - comment: str | None | UndefinedType = Undefined, - direction: Literal["egress", "ingress"] | None | UndefinedType = Undefined, - group: int | None | UndefinedType = Undefined, - original_ip: str | None | UndefinedType = Undefined, - original_port: int | None | UndefinedType = Undefined, - priority: int | None | UndefinedType = Undefined, - protocol: Literal["udp", "tcp"] | None | UndefinedType = Undefined, - translated_ip: str | UndefinedType = Undefined, - translated_port: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + def __init__(self, *, source: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: """ - StaticItem. + IncludeItem. Subclass of AvdModel. Args: - access_list: 'access_list' and 'group' are mutual exclusive. - comment: comment - direction: - Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware - platform. - EOS might remove this keyword in the configuration. So, check the configuration on - targeted HW/SW. - group: 'access_list' and 'group' are mutual exclusive. - original_ip: IPv4 address. The combination of `original_ip` and `original_port` must be unique. - original_port: TCP/UDP port. The combination of `original_ip` and `original_port` must be unique. - priority: priority - protocol: protocol - translated_ip: IPv4 address. - translated_port: requires 'original_port'. + source: source _custom_data: _custom_data """ - class Static(AvdList[StaticItem]): - """Subclass of AvdList with `StaticItem` items.""" + class Include(AvdIndexedList[str, IncludeItem]): + """Subclass of AvdIndexedList with `IncludeItem` items. Primary key is `source` (`str`).""" - Static._item_type = StaticItem + _primary_key: ClassVar[str] = "source" - _fields: ClassVar[dict] = {"dynamic": {"type": Dynamic}, "static": {"type": Static}, "_custom_data": {"type": dict}} - dynamic: Dynamic - """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" - static: Static - """Subclass of AvdList with `StaticItem` items.""" + Include._item_type = IncludeItem + + _fields: ClassVar[dict] = {"group": {"type": str}, "exclude": {"type": Exclude}, "include": {"type": Include}, "_custom_data": {"type": dict}} + group: str + """Multicast Address.""" + exclude: Exclude + """ + The same source must not be present both in `exclude` and `include` list. + + Subclass of + AvdIndexedList with `ExcludeItem` items. Primary key is `source` (`str`). + """ + include: Include + """ + The same source must not be present both in `exclude` and `include` list. + + Subclass of + AvdIndexedList with `IncludeItem` items. Primary key is `source` (`str`). + """ _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29614,179 +30921,134 @@ class Static(AvdList[StaticItem]): def __init__( self, *, - dynamic: Dynamic | UndefinedType = Undefined, - static: Static | UndefinedType = Undefined, + group: str | UndefinedType = Undefined, + exclude: Exclude | UndefinedType = Undefined, + include: Include | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Destination. + GroupsItem. Subclass of AvdModel. Args: - dynamic: Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`). - static: Subclass of AvdList with `StaticItem` items. + group: Multicast Address. + exclude: + The same source must not be present both in `exclude` and `include` list. + + Subclass of + AvdIndexedList with `ExcludeItem` items. Primary key is `source` (`str`). + include: + The same source must not be present both in `exclude` and `include` list. + + Subclass of + AvdIndexedList with `IncludeItem` items. Primary key is `source` (`str`). _custom_data: _custom_data """ - class Source(AvdModel): + class Groups(AvdIndexedList[str, GroupsItem]): + """Subclass of AvdIndexedList with `GroupsItem` items. Primary key is `group` (`str`).""" + + _primary_key: ClassVar[str] = "group" + + Groups._item_type = GroupsItem + + class AccessListsItem(AvdModel): """Subclass of AvdModel.""" - class DynamicItem(AvdModel): - """Subclass of AvdModel.""" + _fields: ClassVar[dict] = {"name": {"type": str}, "_custom_data": {"type": dict}} + name: str + _custom_data: dict[str, Any] - _fields: ClassVar[dict] = { - "access_list": {"type": str}, - "comment": {"type": str}, - "nat_type": {"type": str}, - "pool_name": {"type": str}, - "priority": {"type": int}, - "_custom_data": {"type": dict}, - } - access_list: str - comment: str | None - nat_type: Literal["overload", "pool", "pool-address-only", "pool-full-cone"] - pool_name: str | None - """ - required if 'nat_type' is pool, pool-address-only or pool-full-cone. - ignored if 'nat_type' is - overload. - """ - priority: int | None - _custom_data: dict[str, Any] + if TYPE_CHECKING: - if TYPE_CHECKING: + def __init__(self, *, name: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + """ + AccessListsItem. - def __init__( - self, - *, - access_list: str | UndefinedType = Undefined, - comment: str | None | UndefinedType = Undefined, - nat_type: Literal["overload", "pool", "pool-address-only", "pool-full-cone"] | UndefinedType = Undefined, - pool_name: str | None | UndefinedType = Undefined, - priority: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - DynamicItem. + Subclass of AvdModel. - Subclass of AvdModel. + Args: + name: name + _custom_data: _custom_data - Args: - access_list: access_list - comment: comment - nat_type: nat_type - pool_name: - required if 'nat_type' is pool, pool-address-only or pool-full-cone. - ignored if 'nat_type' is - overload. - priority: priority - _custom_data: _custom_data + """ - """ + class AccessLists(AvdIndexedList[str, AccessListsItem]): + """Subclass of AvdIndexedList with `AccessListsItem` items. Primary key is `name` (`str`).""" - class Dynamic(AvdIndexedList[str, DynamicItem]): - """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" + _primary_key: ClassVar[str] = "name" - _primary_key: ClassVar[str] = "access_list" + AccessLists._item_type = AccessListsItem - Dynamic._item_type = DynamicItem + _fields: ClassVar[dict] = { + "enabled": {"type": bool}, + "groups": {"type": Groups}, + "report_interval": {"type": int}, + "access_lists": {"type": AccessLists}, + "version": {"type": int}, + "_custom_data": {"type": dict}, + } + enabled: bool | None + groups: Groups + """Subclass of AvdIndexedList with `GroupsItem` items. Primary key is `group` (`str`).""" + report_interval: int | None + """Time interval between unsolicited reports.""" + access_lists: AccessLists + """ + Non-standard Access List name. - class StaticItem(AvdModel): - """Subclass of AvdModel.""" + Subclass of AvdIndexedList with `AccessListsItem` items. Primary key + is `name` (`str`). + """ + version: int | None + """IGMP version on IGMP host-proxy interface.""" + _custom_data: dict[str, Any] - _fields: ClassVar[dict] = { - "access_list": {"type": str}, - "comment": {"type": str}, - "direction": {"type": str}, - "group": {"type": int}, - "original_ip": {"type": str}, - "original_port": {"type": int}, - "priority": {"type": int}, - "protocol": {"type": str}, - "translated_ip": {"type": str}, - "translated_port": {"type": int}, - "_custom_data": {"type": dict}, - } - access_list: str | None - """'access_list' and 'group' are mutual exclusive.""" - comment: str | None - direction: Literal["egress", "ingress"] | None - """ - Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware - platform. - EOS might remove this keyword in the configuration. So, check the configuration on - targeted HW/SW. - """ - group: int | None - """'access_list' and 'group' are mutual exclusive.""" - original_ip: str | None - """IPv4 address. The combination of `original_ip` and `original_port` must be unique.""" - original_port: int | None - """TCP/UDP port. The combination of `original_ip` and `original_port` must be unique.""" - priority: int | None - protocol: Literal["udp", "tcp"] | None - translated_ip: str - """IPv4 address.""" - translated_port: int | None - """requires 'original_port'.""" - _custom_data: dict[str, Any] + if TYPE_CHECKING: - if TYPE_CHECKING: + def __init__( + self, + *, + enabled: bool | None | UndefinedType = Undefined, + groups: Groups | UndefinedType = Undefined, + report_interval: int | None | UndefinedType = Undefined, + access_lists: AccessLists | UndefinedType = Undefined, + version: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + IpIgmpHostProxy. - def __init__( - self, - *, - access_list: str | None | UndefinedType = Undefined, - comment: str | None | UndefinedType = Undefined, - direction: Literal["egress", "ingress"] | None | UndefinedType = Undefined, - group: int | None | UndefinedType = Undefined, - original_ip: str | None | UndefinedType = Undefined, - original_port: int | None | UndefinedType = Undefined, - priority: int | None | UndefinedType = Undefined, - protocol: Literal["udp", "tcp"] | None | UndefinedType = Undefined, - translated_ip: str | UndefinedType = Undefined, - translated_port: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - StaticItem. + Subclass of AvdModel. - Subclass of AvdModel. + Args: + enabled: enabled + groups: Subclass of AvdIndexedList with `GroupsItem` items. Primary key is `group` (`str`). + report_interval: Time interval between unsolicited reports. + access_lists: + Non-standard Access List name. - Args: - access_list: 'access_list' and 'group' are mutual exclusive. - comment: comment - direction: - Egress or ingress can be the default. This depends on source/destination, EOS version, and hardware - platform. - EOS might remove this keyword in the configuration. So, check the configuration on - targeted HW/SW. - group: 'access_list' and 'group' are mutual exclusive. - original_ip: IPv4 address. The combination of `original_ip` and `original_port` must be unique. - original_port: TCP/UDP port. The combination of `original_ip` and `original_port` must be unique. - priority: priority - protocol: protocol - translated_ip: IPv4 address. - translated_port: requires 'original_port'. - _custom_data: _custom_data + Subclass of AvdIndexedList with `AccessListsItem` items. Primary key + is `name` (`str`). + version: IGMP version on IGMP host-proxy interface. + _custom_data: _custom_data - """ + """ - class Static(AvdList[StaticItem]): - """Subclass of AvdList with `StaticItem` items.""" + class Sflow(AvdModel): + """Subclass of AvdModel.""" - Static._item_type = StaticItem + class Egress(AvdModel): + """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"dynamic": {"type": Dynamic}, "static": {"type": Static}, "_custom_data": {"type": dict}} - dynamic: Dynamic - """Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`).""" - static: Static - """Subclass of AvdList with `StaticItem` items.""" + _fields: ClassVar[dict] = {"enable": {"type": bool}, "unmodified_enable": {"type": bool}, "_custom_data": {"type": dict}} + enable: bool | None + unmodified_enable: bool | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29794,27 +31056,26 @@ class Static(AvdList[StaticItem]): def __init__( self, *, - dynamic: Dynamic | UndefinedType = Undefined, - static: Static | UndefinedType = Undefined, + enable: bool | None | UndefinedType = Undefined, + unmodified_enable: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Source. + Egress. Subclass of AvdModel. Args: - dynamic: Subclass of AvdIndexedList with `DynamicItem` items. Primary key is `access_list` (`str`). - static: Subclass of AvdList with `StaticItem` items. + enable: enable + unmodified_enable: unmodified_enable _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"destination": {"type": Destination}, "source": {"type": Source}, "_custom_data": {"type": dict}} - destination: Destination - """Subclass of AvdModel.""" - source: Source + _fields: ClassVar[dict] = {"enable": {"type": bool}, "egress": {"type": Egress}, "_custom_data": {"type": dict}} + enable: bool | None + egress: Egress """Subclass of AvdModel.""" _custom_data: dict[str, Any] @@ -29823,130 +31084,129 @@ def __init__( def __init__( self, *, - destination: Destination | UndefinedType = Undefined, - source: Source | UndefinedType = Undefined, + enable: bool | None | UndefinedType = Undefined, + egress: Egress | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - IpNat. + Sflow. Subclass of AvdModel. Args: - destination: Subclass of AvdModel. - source: Subclass of AvdModel. + enable: enable + egress: Subclass of AvdModel. _custom_data: _custom_data """ - class Ipv6NdPrefixesItem(AvdModel): + class Switchport(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "ipv6_prefix": {"type": str}, - "valid_lifetime": {"type": str}, - "preferred_lifetime": {"type": str}, - "no_autoconfig_flag": {"type": bool}, - "_custom_data": {"type": dict}, - } - ipv6_prefix: str - valid_lifetime: str | None - """Infinite or lifetime in seconds.""" - preferred_lifetime: str | None - """Infinite or lifetime in seconds.""" - no_autoconfig_flag: bool | None - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__( - self, - *, - ipv6_prefix: str | UndefinedType = Undefined, - valid_lifetime: str | None | UndefinedType = Undefined, - preferred_lifetime: str | None | UndefinedType = Undefined, - no_autoconfig_flag: bool | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Ipv6NdPrefixesItem. - - - Subclass of AvdModel. - - Args: - ipv6_prefix: ipv6_prefix - valid_lifetime: Infinite or lifetime in seconds. - preferred_lifetime: Infinite or lifetime in seconds. - no_autoconfig_flag: no_autoconfig_flag - _custom_data: _custom_data - - """ + class Trunk(AvdModel): + """Subclass of AvdModel.""" - class Ipv6NdPrefixes(AvdIndexedList[str, Ipv6NdPrefixesItem]): - """Subclass of AvdIndexedList with `Ipv6NdPrefixesItem` items. Primary key is `ipv6_prefix` (`str`).""" + class Groups(AvdList[str]): + """Subclass of AvdList with `str` items.""" - _primary_key: ClassVar[str] = "ipv6_prefix" + Groups._item_type = str - Ipv6NdPrefixes._item_type = Ipv6NdPrefixesItem + _fields: ClassVar[dict] = { + "allowed_vlan": {"type": str}, + "native_vlan": {"type": int}, + "native_vlan_tag": {"type": bool}, + "private_vlan_secondary": {"type": bool}, + "groups": {"type": Groups}, + "_custom_data": {"type": dict}, + } + allowed_vlan: str | None + """ + VLAN ID or range(s) of VLAN IDs (1-4094). + Warning: This should not be combined with + `port_channel_interfaces[].mode = trunk` and `port_channel_interfaces[].vlans`. + """ + native_vlan: int | None + """ + Set native VLAN when interface is in trunking mode. + Warning: This should not be combined with + `port_channel_interfaces[].native_vlan`. + """ + native_vlan_tag: bool | None + """ + If setting both native_vlan and native_vlan_tag, native_vlan_tag takes precedence. + Warning: This + should not be combined with `port_channel_interfaces[].native_vlan_tag`. + """ + private_vlan_secondary: bool | None + """ + Enable secondary VLAN mapping for a private vlan. + Warning: This should not be combined with + `port_channel_interfaces[].trunk_private_vlan_secondary`. + """ + groups: Groups + """ + Warning: This should not be combined with `port_channel_interfaces[].trunk_groups`. - class Pim(AvdModel): - """Subclass of AvdModel.""" - class Ipv4(AvdModel): - """Subclass of AvdModel.""" + Subclass of + AvdList with `str` items. + """ + _custom_data: dict[str, Any] - class Hello(AvdModel): - """Subclass of AvdModel.""" + if TYPE_CHECKING: - _fields: ClassVar[dict] = {"count": {"type": str}, "interval": {"type": int}, "_custom_data": {"type": dict}} - count: str | None - """Number of missed hellos after which the neighbor expires. Range <1.5-65535>.""" - interval: int | None - """PIM hello interval in seconds.""" - _custom_data: dict[str, Any] + def __init__( + self, + *, + allowed_vlan: str | None | UndefinedType = Undefined, + native_vlan: int | None | UndefinedType = Undefined, + native_vlan_tag: bool | None | UndefinedType = Undefined, + private_vlan_secondary: bool | None | UndefinedType = Undefined, + groups: Groups | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Trunk. - if TYPE_CHECKING: - def __init__( - self, - *, - count: str | None | UndefinedType = Undefined, - interval: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Hello. + Subclass of AvdModel. + Args: + allowed_vlan: + VLAN ID or range(s) of VLAN IDs (1-4094). + Warning: This should not be combined with + `port_channel_interfaces[].mode = trunk` and `port_channel_interfaces[].vlans`. + native_vlan: + Set native VLAN when interface is in trunking mode. + Warning: This should not be combined with + `port_channel_interfaces[].native_vlan`. + native_vlan_tag: + If setting both native_vlan and native_vlan_tag, native_vlan_tag takes precedence. + Warning: This + should not be combined with `port_channel_interfaces[].native_vlan_tag`. + private_vlan_secondary: + Enable secondary VLAN mapping for a private vlan. + Warning: This should not be combined with + `port_channel_interfaces[].trunk_private_vlan_secondary`. + groups: + Warning: This should not be combined with `port_channel_interfaces[].trunk_groups`. - Subclass of AvdModel. - Args: - count: Number of missed hellos after which the neighbor expires. Range <1.5-65535>. - interval: PIM hello interval in seconds. - _custom_data: _custom_data + Subclass of + AvdList with `str` items. + _custom_data: _custom_data - """ + """ - _fields: ClassVar[dict] = { - "border_router": {"type": bool}, - "dr_priority": {"type": int}, - "sparse_mode": {"type": bool}, - "bfd": {"type": bool}, - "bidirectional": {"type": bool}, - "hello": {"type": Hello}, - "_custom_data": {"type": dict}, - } - border_router: bool | None - """Configure PIM border router. EOS default is false.""" - dr_priority: int | None - sparse_mode: bool | None - bfd: bool | None - """Set the default for whether Bidirectional Forwarding Detection is enabled for PIM.""" - bidirectional: bool | None - hello: Hello + class Phone(AvdModel): """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"vlan": {"type": int}, "trunk": {"type": str}, "_custom_data": {"type": dict}} + vlan: int | None + """Warning: This should not be combined with `port_channel_interfaces[].phone.vlan`.""" + trunk: Literal["tagged", "tagged phone", "untagged", "untagged phone"] | None + """Warning: This should not be combined with `port_channel_interfaces[].phone.trunk`""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -29954,229 +31214,262 @@ def __init__( def __init__( self, *, - border_router: bool | None | UndefinedType = Undefined, - dr_priority: int | None | UndefinedType = Undefined, - sparse_mode: bool | None | UndefinedType = Undefined, - bfd: bool | None | UndefinedType = Undefined, - bidirectional: bool | None | UndefinedType = Undefined, - hello: Hello | UndefinedType = Undefined, + vlan: int | None | UndefinedType = Undefined, + trunk: Literal["tagged", "tagged phone", "untagged", "untagged phone"] | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Ipv4. + Phone. Subclass of AvdModel. Args: - border_router: Configure PIM border router. EOS default is false. - dr_priority: dr_priority - sparse_mode: sparse_mode - bfd: Set the default for whether Bidirectional Forwarding Detection is enabled for PIM. - bidirectional: bidirectional - hello: Subclass of AvdModel. + vlan: Warning: This should not be combined with `port_channel_interfaces[].phone.vlan`. + trunk: Warning: This should not be combined with `port_channel_interfaces[].phone.trunk` _custom_data: _custom_data """ - _fields: ClassVar[dict] = {"ipv4": {"type": Ipv4}, "_custom_data": {"type": dict}} - ipv4: Ipv4 - """Subclass of AvdModel.""" - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__(self, *, ipv4: Ipv4 | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: - """ - Pim. - - - Subclass of AvdModel. - - Args: - ipv4: Subclass of AvdModel. - _custom_data: _custom_data - - """ - - class OspfMessageDigestKeysItem(AvdModel): - """Subclass of AvdModel.""" - - _fields: ClassVar[dict] = {"id": {"type": int}, "hash_algorithm": {"type": str}, "key": {"type": str}, "_custom_data": {"type": dict}} - id: int - hash_algorithm: Literal["md5", "sha1", "sha256", "sha384", "sha512"] | None - key: str | None - """Encrypted password.""" - _custom_data: dict[str, Any] - - if TYPE_CHECKING: + class Dot1q(AvdModel): + """Subclass of AvdModel.""" - def __init__( - self, - *, - id: int | UndefinedType = Undefined, - hash_algorithm: Literal["md5", "sha1", "sha256", "sha384", "sha512"] | None | UndefinedType = Undefined, - key: str | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - OspfMessageDigestKeysItem. + _fields: ClassVar[dict] = {"ethertype": {"type": int}, "vlan_tag": {"type": str}, "_custom_data": {"type": dict}} + ethertype: int | None + """Ethertype/TPID (Tag Protocol IDentifier) for VLAN tagged frames.""" + vlan_tag: Literal["disallowed", "required"] | None + _custom_data: dict[str, Any] + if TYPE_CHECKING: - Subclass of AvdModel. + def __init__( + self, + *, + ethertype: int | None | UndefinedType = Undefined, + vlan_tag: Literal["disallowed", "required"] | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Dot1q. - Args: - id: id - hash_algorithm: hash_algorithm - key: Encrypted password. - _custom_data: _custom_data - """ + Subclass of AvdModel. - class OspfMessageDigestKeys(AvdIndexedList[int, OspfMessageDigestKeysItem]): - """Subclass of AvdIndexedList with `OspfMessageDigestKeysItem` items. Primary key is `id` (`int`).""" + Args: + ethertype: Ethertype/TPID (Tag Protocol IDentifier) for VLAN tagged frames. + vlan_tag: vlan_tag + _custom_data: _custom_data - _primary_key: ClassVar[str] = "id" + """ - OspfMessageDigestKeys._item_type = OspfMessageDigestKeysItem + class VlanTranslations(AvdModel): + """Subclass of AvdModel.""" - class FlowTracker(AvdModel): - """Subclass of AvdModel.""" + class DirectionInItem(AvdModel): + """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"sampled": {"type": str}, "hardware": {"type": str}, "_custom_data": {"type": dict}} - sampled: str | None - """Sampled flow tracker name.""" - hardware: str | None - """Hardware flow tracker name.""" - _custom_data: dict[str, Any] + _fields: ClassVar[dict] = { + "field_from": {"type": str}, + "to": {"type": int}, + "dot1q_tunnel": {"type": bool}, + "inner_vlan_from": {"type": int}, + "_custom_data": {"type": dict}, + } + _field_to_key_map: ClassVar[dict] = {"field_from": "from"} + _key_to_field_map: ClassVar[dict] = {"from": "field_from"} + field_from: str | None + """VLAN ID or range of VLAN IDs to map from. Range 1-4094.""" + to: int | None + """VLAN ID to map to.""" + dot1q_tunnel: bool | None + inner_vlan_from: int | None + """Inner VLAN ID to map from.""" + _custom_data: dict[str, Any] - if TYPE_CHECKING: + if TYPE_CHECKING: - def __init__( - self, - *, - sampled: str | None | UndefinedType = Undefined, - hardware: str | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - FlowTracker. + def __init__( + self, + *, + field_from: str | None | UndefinedType = Undefined, + to: int | None | UndefinedType = Undefined, + dot1q_tunnel: bool | None | UndefinedType = Undefined, + inner_vlan_from: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + DirectionInItem. - Subclass of AvdModel. + Subclass of AvdModel. - Args: - sampled: Sampled flow tracker name. - hardware: Hardware flow tracker name. - _custom_data: _custom_data + Args: + field_from: VLAN ID or range of VLAN IDs to map from. Range 1-4094. + to: VLAN ID to map to. + dot1q_tunnel: dot1q_tunnel + inner_vlan_from: Inner VLAN ID to map from. + _custom_data: _custom_data - """ + """ - class Bgp(AvdModel): - """Subclass of AvdModel.""" + class DirectionIn(AvdList[DirectionInItem]): + """Subclass of AvdList with `DirectionInItem` items.""" - _fields: ClassVar[dict] = {"session_tracker": {"type": str}, "_custom_data": {"type": dict}} - session_tracker: str | None - """Name of session tracker.""" - _custom_data: dict[str, Any] + DirectionIn._item_type = DirectionInItem - if TYPE_CHECKING: + class DirectionOutItem(AvdModel): + """Subclass of AvdModel.""" - def __init__( - self, *, session_tracker: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined - ) -> None: + _fields: ClassVar[dict] = { + "field_from": {"type": str}, + "to": {"type": int}, + "dot1q_tunnel_to": {"type": str}, + "inner_vlan_to": {"type": int}, + "_custom_data": {"type": dict}, + } + _field_to_key_map: ClassVar[dict] = {"field_from": "from"} + _key_to_field_map: ClassVar[dict] = {"from": "field_from"} + field_from: str + """VLAN ID or range of VLAN IDs to map from. Range 1-4094.""" + to: int | None + """VLAN ID to map to.""" + dot1q_tunnel_to: str | None """ - Bgp. - - - Subclass of AvdModel. - - Args: - session_tracker: Name of session tracker. - _custom_data: _custom_data - + VLAN ID or range of VLAN IDs or "all". Range 1-4094. + This takes precedence over `to` and + `inner_vlan_to`. """ - - class IpIgmpHostProxy(AvdModel): - """Subclass of AvdModel.""" - - class GroupsItem(AvdModel): - """Subclass of AvdModel.""" - - class ExcludeItem(AvdModel): - """Subclass of AvdModel.""" - - _fields: ClassVar[dict] = {"source": {"type": str}, "_custom_data": {"type": dict}} - source: str + inner_vlan_to: int | None + """Inner VLAN ID to map to.""" _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__(self, *, source: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + def __init__( + self, + *, + field_from: str | UndefinedType = Undefined, + to: int | None | UndefinedType = Undefined, + dot1q_tunnel_to: str | None | UndefinedType = Undefined, + inner_vlan_to: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: """ - ExcludeItem. + DirectionOutItem. Subclass of AvdModel. Args: - source: source + field_from: VLAN ID or range of VLAN IDs to map from. Range 1-4094. + to: VLAN ID to map to. + dot1q_tunnel_to: + VLAN ID or range of VLAN IDs or "all". Range 1-4094. + This takes precedence over `to` and + `inner_vlan_to`. + inner_vlan_to: Inner VLAN ID to map to. _custom_data: _custom_data """ - class Exclude(AvdIndexedList[str, ExcludeItem]): - """Subclass of AvdIndexedList with `ExcludeItem` items. Primary key is `source` (`str`).""" - - _primary_key: ClassVar[str] = "source" + class DirectionOut(AvdList[DirectionOutItem]): + """Subclass of AvdList with `DirectionOutItem` items.""" - Exclude._item_type = ExcludeItem + DirectionOut._item_type = DirectionOutItem - class IncludeItem(AvdModel): + class DirectionBothItem(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"source": {"type": str}, "_custom_data": {"type": dict}} - source: str + _fields: ClassVar[dict] = { + "field_from": {"type": str}, + "to": {"type": int}, + "dot1q_tunnel": {"type": bool}, + "inner_vlan_from": {"type": int}, + "network": {"type": bool}, + "_custom_data": {"type": dict}, + } + _field_to_key_map: ClassVar[dict] = {"field_from": "from"} + _key_to_field_map: ClassVar[dict] = {"from": "field_from"} + field_from: str + """VLAN ID or range of VLAN IDs to map from. Range 1-4094.""" + to: int + """VLAN ID to map to.""" + dot1q_tunnel: bool | None + inner_vlan_from: int | None + """Inner VLAN ID to map from.""" + network: bool | None + """ + Enable use of network-side VLAN ID. + This setting can only be enabled when `inner_vlan_from` is + defined. + """ _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__(self, *, source: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + def __init__( + self, + *, + field_from: str | UndefinedType = Undefined, + to: int | UndefinedType = Undefined, + dot1q_tunnel: bool | None | UndefinedType = Undefined, + inner_vlan_from: int | None | UndefinedType = Undefined, + network: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: """ - IncludeItem. + DirectionBothItem. Subclass of AvdModel. Args: - source: source + field_from: VLAN ID or range of VLAN IDs to map from. Range 1-4094. + to: VLAN ID to map to. + dot1q_tunnel: dot1q_tunnel + inner_vlan_from: Inner VLAN ID to map from. + network: + Enable use of network-side VLAN ID. + This setting can only be enabled when `inner_vlan_from` is + defined. _custom_data: _custom_data """ - class Include(AvdIndexedList[str, IncludeItem]): - """Subclass of AvdIndexedList with `IncludeItem` items. Primary key is `source` (`str`).""" + class DirectionBoth(AvdList[DirectionBothItem]): + """Subclass of AvdList with `DirectionBothItem` items.""" - _primary_key: ClassVar[str] = "source" + DirectionBoth._item_type = DirectionBothItem - Include._item_type = IncludeItem + _fields: ClassVar[dict] = { + "in_required": {"type": bool}, + "out_required": {"type": bool}, + "direction_in": {"type": DirectionIn}, + "direction_out": {"type": DirectionOut}, + "direction_both": {"type": DirectionBoth}, + "_custom_data": {"type": dict}, + } + in_required: bool | None + """Drop the ingress traffic that do not match any VLAN mapping.""" + out_required: bool | None + """Drop the egress traffic that do not match any VLAN mapping.""" + direction_in: DirectionIn + """ + Map ingress traffic only. - _fields: ClassVar[dict] = {"group": {"type": str}, "exclude": {"type": Exclude}, "include": {"type": Include}, "_custom_data": {"type": dict}} - group: str - """Multicast Address.""" - exclude: Exclude + Subclass of AvdList with `DirectionInItem` items. """ - The same source must not be present both in `exclude` and `include` list. + direction_out: DirectionOut + """ + Map egress traffic only. - Subclass of - AvdIndexedList with `ExcludeItem` items. Primary key is `source` (`str`). + Subclass of AvdList with `DirectionOutItem` items. """ - include: Include + direction_both: DirectionBoth """ - The same source must not be present both in `exclude` and `include` list. + Map both egress and ingress traffic. - Subclass of - AvdIndexedList with `IncludeItem` items. Primary key is `source` (`str`). + Subclass of AvdList with `DirectionBothItem` items. """ _custom_data: dict[str, Any] @@ -30185,237 +31478,269 @@ class Include(AvdIndexedList[str, IncludeItem]): def __init__( self, *, - group: str | UndefinedType = Undefined, - exclude: Exclude | UndefinedType = Undefined, - include: Include | UndefinedType = Undefined, + in_required: bool | None | UndefinedType = Undefined, + out_required: bool | None | UndefinedType = Undefined, + direction_in: DirectionIn | UndefinedType = Undefined, + direction_out: DirectionOut | UndefinedType = Undefined, + direction_both: DirectionBoth | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - GroupsItem. + VlanTranslations. Subclass of AvdModel. Args: - group: Multicast Address. - exclude: - The same source must not be present both in `exclude` and `include` list. + in_required: Drop the ingress traffic that do not match any VLAN mapping. + out_required: Drop the egress traffic that do not match any VLAN mapping. + direction_in: + Map ingress traffic only. - Subclass of - AvdIndexedList with `ExcludeItem` items. Primary key is `source` (`str`). - include: - The same source must not be present both in `exclude` and `include` list. + Subclass of AvdList with `DirectionInItem` items. + direction_out: + Map egress traffic only. - Subclass of - AvdIndexedList with `IncludeItem` items. Primary key is `source` (`str`). + Subclass of AvdList with `DirectionOutItem` items. + direction_both: + Map both egress and ingress traffic. + + Subclass of AvdList with `DirectionBothItem` items. _custom_data: _custom_data """ - class Groups(AvdIndexedList[str, GroupsItem]): - """Subclass of AvdIndexedList with `GroupsItem` items. Primary key is `group` (`str`).""" - - _primary_key: ClassVar[str] = "group" - - Groups._item_type = GroupsItem - - class AccessListsItem(AvdModel): + class BackupLink(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"name": {"type": str}, "_custom_data": {"type": dict}} - name: str + _fields: ClassVar[dict] = {"interface": {"type": str}, "prefer_vlan": {"type": str}, "_custom_data": {"type": dict}} + interface: str + """Backup interface. Example - Ethernet4, Vlan10 etc.""" + prefer_vlan: str | None + """VLANs to carry on the backup interface (1-4094).""" _custom_data: dict[str, Any] if TYPE_CHECKING: - def __init__(self, *, name: str | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined) -> None: + def __init__( + self, + *, + interface: str | UndefinedType = Undefined, + prefer_vlan: str | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: """ - AccessListsItem. + BackupLink. Subclass of AvdModel. Args: - name: name + interface: Backup interface. Example - Ethernet4, Vlan10 etc. + prefer_vlan: VLANs to carry on the backup interface (1-4094). _custom_data: _custom_data """ - class AccessLists(AvdIndexedList[str, AccessListsItem]): - """Subclass of AvdIndexedList with `AccessListsItem` items. Primary key is `name` (`str`).""" + class Backup(AvdModel): + """Subclass of AvdModel.""" - _primary_key: ClassVar[str] = "name" + _fields: ClassVar[dict] = { + "dest_macaddr": {"type": str}, + "initial_mac_move_delay": {"type": int}, + "mac_move_burst": {"type": int}, + "mac_move_burst_interval": {"type": int}, + "preemption_delay": {"type": int}, + "_custom_data": {"type": dict}, + } + dest_macaddr: str | None + """ + Destination MAC address for MAC move updates. + The mac address should be multicast or broadcast. + Example: 01:00:00:00:00:00 + """ + initial_mac_move_delay: int | None + """Initial MAC move delay in milliseconds.""" + mac_move_burst: int | None + """Size of MAC move bursts.""" + mac_move_burst_interval: int | None + """MAC move burst interval in milliseconds.""" + preemption_delay: int | None + """Preemption delay in milliseconds.""" + _custom_data: dict[str, Any] - AccessLists._item_type = AccessListsItem + if TYPE_CHECKING: - _fields: ClassVar[dict] = { - "enabled": {"type": bool}, - "groups": {"type": Groups}, - "report_interval": {"type": int}, - "access_lists": {"type": AccessLists}, - "version": {"type": int}, - "_custom_data": {"type": dict}, - } - enabled: bool | None - groups: Groups - """Subclass of AvdIndexedList with `GroupsItem` items. Primary key is `group` (`str`).""" - report_interval: int | None - """Time interval between unsolicited reports.""" - access_lists: AccessLists - """ - Non-standard Access List name. + def __init__( + self, + *, + dest_macaddr: str | None | UndefinedType = Undefined, + initial_mac_move_delay: int | None | UndefinedType = Undefined, + mac_move_burst: int | None | UndefinedType = Undefined, + mac_move_burst_interval: int | None | UndefinedType = Undefined, + preemption_delay: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Backup. - Subclass of AvdIndexedList with `AccessListsItem` items. Primary key - is `name` (`str`). - """ - version: int | None - """IGMP version on IGMP host-proxy interface.""" - _custom_data: dict[str, Any] - if TYPE_CHECKING: + Subclass of AvdModel. - def __init__( - self, - *, - enabled: bool | None | UndefinedType = Undefined, - groups: Groups | UndefinedType = Undefined, - report_interval: int | None | UndefinedType = Undefined, - access_lists: AccessLists | UndefinedType = Undefined, - version: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - IpIgmpHostProxy. + Args: + dest_macaddr: + Destination MAC address for MAC move updates. + The mac address should be multicast or broadcast. + Example: 01:00:00:00:00:00 + initial_mac_move_delay: Initial MAC move delay in milliseconds. + mac_move_burst: Size of MAC move bursts. + mac_move_burst_interval: MAC move burst interval in milliseconds. + preemption_delay: Preemption delay in milliseconds. + _custom_data: _custom_data + """ - Subclass of AvdModel. + class PortSecurity(AvdModel): + """Subclass of AvdModel.""" - Args: - enabled: enabled - groups: Subclass of AvdIndexedList with `GroupsItem` items. Primary key is `group` (`str`). - report_interval: Time interval between unsolicited reports. - access_lists: - Non-standard Access List name. + class MacAddressMaximum(AvdModel): + """Subclass of AvdModel.""" - Subclass of AvdIndexedList with `AccessListsItem` items. Primary key - is `name` (`str`). - version: IGMP version on IGMP host-proxy interface. - _custom_data: _custom_data + _fields: ClassVar[dict] = {"disabled": {"type": bool}, "limit": {"type": int}, "_custom_data": {"type": dict}} + disabled: bool | None + """Disable port level check for port security (only in violation 'shutdown' mode).""" + limit: int | None + """MAC address limit.""" + _custom_data: dict[str, Any] - """ + if TYPE_CHECKING: - class Sflow(AvdModel): - """Subclass of AvdModel.""" + def __init__( + self, + *, + disabled: bool | None | UndefinedType = Undefined, + limit: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + MacAddressMaximum. + + + Subclass of AvdModel. + + Args: + disabled: Disable port level check for port security (only in violation 'shutdown' mode). + limit: MAC address limit. + _custom_data: _custom_data - class Egress(AvdModel): - """Subclass of AvdModel.""" + """ - _fields: ClassVar[dict] = {"enable": {"type": bool}, "unmodified_enable": {"type": bool}, "_custom_data": {"type": dict}} - enable: bool | None - unmodified_enable: bool | None - _custom_data: dict[str, Any] + class Violation(AvdModel): + """Subclass of AvdModel.""" - if TYPE_CHECKING: + _fields: ClassVar[dict] = {"mode": {"type": str}, "protect_log": {"type": bool}, "_custom_data": {"type": dict}} + mode: Literal["shutdown", "protect"] | None + """Configure port security mode.""" + protect_log: bool | None + """Log new addresses seen after limit is reached in protect mode.""" + _custom_data: dict[str, Any] - def __init__( - self, - *, - enable: bool | None | UndefinedType = Undefined, - unmodified_enable: bool | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Egress. + if TYPE_CHECKING: + def __init__( + self, + *, + mode: Literal["shutdown", "protect"] | None | UndefinedType = Undefined, + protect_log: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Violation. - Subclass of AvdModel. - Args: - enable: enable - unmodified_enable: unmodified_enable - _custom_data: _custom_data + Subclass of AvdModel. - """ + Args: + mode: Configure port security mode. + protect_log: Log new addresses seen after limit is reached in protect mode. + _custom_data: _custom_data - _fields: ClassVar[dict] = {"enable": {"type": bool}, "egress": {"type": Egress}, "_custom_data": {"type": dict}} - enable: bool | None - egress: Egress - """Subclass of AvdModel.""" - _custom_data: dict[str, Any] + """ - if TYPE_CHECKING: + class VlansItem(AvdModel): + """Subclass of AvdModel.""" - def __init__( - self, - *, - enable: bool | None | UndefinedType = Undefined, - egress: Egress | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: + _fields: ClassVar[dict] = {"range": {"type": str}, "mac_address_maximum": {"type": int}, "_custom_data": {"type": dict}} + range: str """ - Sflow. + VLAN ID or range(s) of VLAN IDs, <1-4094>. + Example: + - 3 + - 1,3 + - 1-10 + """ + mac_address_maximum: int | None + _custom_data: dict[str, Any] + if TYPE_CHECKING: - Subclass of AvdModel. + def __init__( + self, + *, + range: str | UndefinedType = Undefined, + mac_address_maximum: int | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + VlansItem. - Args: - enable: enable - egress: Subclass of AvdModel. - _custom_data: _custom_data - """ + Subclass of AvdModel. - class Switchport(AvdModel): - """Subclass of AvdModel.""" + Args: + range: + VLAN ID or range(s) of VLAN IDs, <1-4094>. + Example: # fmt: skip + - 3 + - 1,3 + - 1-10 + mac_address_maximum: mac_address_maximum + _custom_data: _custom_data - class Trunk(AvdModel): - """Subclass of AvdModel.""" + """ - class Groups(AvdList[str]): - """Subclass of AvdList with `str` items.""" + class Vlans(AvdIndexedList[str, VlansItem]): + """Subclass of AvdIndexedList with `VlansItem` items. Primary key is `range` (`str`).""" - Groups._item_type = str + _primary_key: ClassVar[str] = "range" + + Vlans._item_type = VlansItem _fields: ClassVar[dict] = { - "allowed_vlan": {"type": str}, - "native_vlan": {"type": int}, - "native_vlan_tag": {"type": bool}, - "private_vlan_secondary": {"type": bool}, - "groups": {"type": Groups}, + "enabled": {"type": bool}, + "mac_address_maximum": {"type": MacAddressMaximum}, + "violation": {"type": Violation}, + "vlan_default_mac_address_maximum": {"type": int}, + "vlans": {"type": Vlans}, "_custom_data": {"type": dict}, } - allowed_vlan: str | None - """ - VLAN ID or range(s) of VLAN IDs (1-4094). - Warning: This should not be combined with - `port_channel_interfaces[].mode = trunk` and `port_channel_interfaces[].vlans`. - """ - native_vlan: int | None - """ - Set native VLAN when interface is in trunking mode. - Warning: This should not be combined with - `port_channel_interfaces[].native_vlan`. - """ - native_vlan_tag: bool | None - """ - If setting both native_vlan and native_vlan_tag, native_vlan_tag takes precedence. - Warning: This - should not be combined with `port_channel_interfaces[].native_vlan_tag`. - """ - private_vlan_secondary: bool | None + enabled: bool | None + mac_address_maximum: MacAddressMaximum """ - Enable secondary VLAN mapping for a private vlan. - Warning: This should not be combined with - `port_channel_interfaces[].trunk_private_vlan_secondary`. + Maximum number of MAC addresses allowed on the interface. + + Subclass of AvdModel. """ - groups: Groups + violation: Violation """ - Warning: This should not be combined with `port_channel_interfaces[].trunk_groups`. - + Configure violation mode (shutdown or protect), EOS default is 'shutdown'. - Subclass of - AvdList with `str` items. + Subclass of AvdModel. """ + vlan_default_mac_address_maximum: int | None + """Default maximum MAC addresses for all VLANs on this interface.""" + vlans: Vlans + """Subclass of AvdIndexedList with `VlansItem` items. Primary key is `range` (`str`).""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30423,131 +31748,125 @@ class Groups(AvdList[str]): def __init__( self, *, - allowed_vlan: str | None | UndefinedType = Undefined, - native_vlan: int | None | UndefinedType = Undefined, - native_vlan_tag: bool | None | UndefinedType = Undefined, - private_vlan_secondary: bool | None | UndefinedType = Undefined, - groups: Groups | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + mac_address_maximum: MacAddressMaximum | UndefinedType = Undefined, + violation: Violation | UndefinedType = Undefined, + vlan_default_mac_address_maximum: int | None | UndefinedType = Undefined, + vlans: Vlans | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Trunk. + PortSecurity. Subclass of AvdModel. Args: - allowed_vlan: - VLAN ID or range(s) of VLAN IDs (1-4094). - Warning: This should not be combined with - `port_channel_interfaces[].mode = trunk` and `port_channel_interfaces[].vlans`. - native_vlan: - Set native VLAN when interface is in trunking mode. - Warning: This should not be combined with - `port_channel_interfaces[].native_vlan`. - native_vlan_tag: - If setting both native_vlan and native_vlan_tag, native_vlan_tag takes precedence. - Warning: This - should not be combined with `port_channel_interfaces[].native_vlan_tag`. - private_vlan_secondary: - Enable secondary VLAN mapping for a private vlan. - Warning: This should not be combined with - `port_channel_interfaces[].trunk_private_vlan_secondary`. - groups: - Warning: This should not be combined with `port_channel_interfaces[].trunk_groups`. + enabled: enabled + mac_address_maximum: + Maximum number of MAC addresses allowed on the interface. + Subclass of AvdModel. + violation: + Configure violation mode (shutdown or protect), EOS default is 'shutdown'. - Subclass of - AvdList with `str` items. + Subclass of AvdModel. + vlan_default_mac_address_maximum: Default maximum MAC addresses for all VLANs on this interface. + vlans: Subclass of AvdIndexedList with `VlansItem` items. Primary key is `range` (`str`). _custom_data: _custom_data """ - class Phone(AvdModel): + class Tap(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"vlan": {"type": int}, "trunk": {"type": str}, "_custom_data": {"type": dict}} - vlan: int | None - """Warning: This should not be combined with `port_channel_interfaces[].phone.vlan`.""" - trunk: Literal["tagged", "tagged phone", "untagged", "untagged phone"] | None - """Warning: This should not be combined with `port_channel_interfaces[].phone.trunk`""" - _custom_data: dict[str, Any] + class Default(AvdModel): + """Subclass of AvdModel.""" - if TYPE_CHECKING: + class Groups(AvdList[str]): + """Subclass of AvdList with `str` items.""" - def __init__( - self, - *, - vlan: int | None | UndefinedType = Undefined, - trunk: Literal["tagged", "tagged phone", "untagged", "untagged phone"] | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Phone. + Groups._item_type = str + class Interfaces(AvdList[str]): + """Subclass of AvdList with `str` items.""" - Subclass of AvdModel. + Interfaces._item_type = str - Args: - vlan: Warning: This should not be combined with `port_channel_interfaces[].phone.vlan`. - trunk: Warning: This should not be combined with `port_channel_interfaces[].phone.trunk` - _custom_data: _custom_data + class NexthopGroups(AvdList[str]): + """Subclass of AvdList with `str` items.""" - """ + NexthopGroups._item_type = str - class Dot1q(AvdModel): - """Subclass of AvdModel.""" + _fields: ClassVar[dict] = { + "groups": {"type": Groups}, + "interfaces": {"type": Interfaces}, + "nexthop_groups": {"type": NexthopGroups}, + "_custom_data": {"type": dict}, + } + groups: Groups + """ + Tap group names for the interface. + + Subclass of AvdList with `str` items. + """ + interfaces: Interfaces + """ + Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. + + Subclass of AvdList + with `str` items. + """ + nexthop_groups: NexthopGroups + """ + Default nexthop-group names. + + Subclass of AvdList with `str` items. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: - _fields: ClassVar[dict] = {"ethertype": {"type": int}, "vlan_tag": {"type": str}, "_custom_data": {"type": dict}} - ethertype: int | None - """Ethertype/TPID (Tag Protocol IDentifier) for VLAN tagged frames.""" - vlan_tag: Literal["disallowed", "required"] | None - _custom_data: dict[str, Any] + def __init__( + self, + *, + groups: Groups | UndefinedType = Undefined, + interfaces: Interfaces | UndefinedType = Undefined, + nexthop_groups: NexthopGroups | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Default. - if TYPE_CHECKING: - def __init__( - self, - *, - ethertype: int | None | UndefinedType = Undefined, - vlan_tag: Literal["disallowed", "required"] | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - Dot1q. + Subclass of AvdModel. + Args: + groups: + Tap group names for the interface. - Subclass of AvdModel. + Subclass of AvdList with `str` items. + interfaces: + Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. - Args: - ethertype: Ethertype/TPID (Tag Protocol IDentifier) for VLAN tagged frames. - vlan_tag: vlan_tag - _custom_data: _custom_data + Subclass of AvdList + with `str` items. + nexthop_groups: + Default nexthop-group names. - """ + Subclass of AvdList with `str` items. + _custom_data: _custom_data - class VlanTranslations(AvdModel): - """Subclass of AvdModel.""" + """ - class DirectionInItem(AvdModel): + class Identity(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "field_from": {"type": str}, - "to": {"type": int}, - "dot1q_tunnel": {"type": bool}, - "inner_vlan_from": {"type": int}, - "_custom_data": {"type": dict}, - } - _field_to_key_map: ClassVar[dict] = {"field_from": "from"} - _key_to_field_map: ClassVar[dict] = {"from": "field_from"} - field_from: str | None - """VLAN ID or range of VLAN IDs to map from. Range 1-4094.""" - to: int | None - """VLAN ID to map to.""" - dot1q_tunnel: bool | None - inner_vlan_from: int | None - """Inner VLAN ID to map from.""" + _fields: ClassVar[dict] = {"id": {"type": int}, "inner_vlan": {"type": int}, "_custom_data": {"type": dict}} + id: int | None + """Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535).""" + inner_vlan: int | None + """Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094).""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30555,56 +31874,30 @@ class DirectionInItem(AvdModel): def __init__( self, *, - field_from: str | None | UndefinedType = Undefined, - to: int | None | UndefinedType = Undefined, - dot1q_tunnel: bool | None | UndefinedType = Undefined, - inner_vlan_from: int | None | UndefinedType = Undefined, + id: int | None | UndefinedType = Undefined, + inner_vlan: int | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - DirectionInItem. + Identity. Subclass of AvdModel. Args: - field_from: VLAN ID or range of VLAN IDs to map from. Range 1-4094. - to: VLAN ID to map to. - dot1q_tunnel: dot1q_tunnel - inner_vlan_from: Inner VLAN ID to map from. + id: Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). + inner_vlan: Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). _custom_data: _custom_data """ - class DirectionIn(AvdList[DirectionInItem]): - """Subclass of AvdList with `DirectionInItem` items.""" - - DirectionIn._item_type = DirectionInItem - - class DirectionOutItem(AvdModel): + class Truncation(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "field_from": {"type": str}, - "to": {"type": int}, - "dot1q_tunnel_to": {"type": str}, - "inner_vlan_to": {"type": int}, - "_custom_data": {"type": dict}, - } - _field_to_key_map: ClassVar[dict] = {"field_from": "from"} - _key_to_field_map: ClassVar[dict] = {"from": "field_from"} - field_from: str - """VLAN ID or range of VLAN IDs to map from. Range 1-4094.""" - to: int | None - """VLAN ID to map to.""" - dot1q_tunnel_to: str | None - """ - VLAN ID or range of VLAN IDs or "all". Range 1-4094. - This takes precedence over `to` and - `inner_vlan_to`. - """ - inner_vlan_to: int | None - """Inner VLAN ID to map to.""" + _fields: ClassVar[dict] = {"enabled": {"type": bool}, "size": {"type": int}, "_custom_data": {"type": dict}} + enabled: bool | None + size: int | None + """Ingress packet truncation size in bytes.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30612,61 +31905,31 @@ class DirectionOutItem(AvdModel): def __init__( self, *, - field_from: str | UndefinedType = Undefined, - to: int | None | UndefinedType = Undefined, - dot1q_tunnel_to: str | None | UndefinedType = Undefined, - inner_vlan_to: int | None | UndefinedType = Undefined, + enabled: bool | None | UndefinedType = Undefined, + size: int | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - DirectionOutItem. + Truncation. Subclass of AvdModel. Args: - field_from: VLAN ID or range of VLAN IDs to map from. Range 1-4094. - to: VLAN ID to map to. - dot1q_tunnel_to: - VLAN ID or range of VLAN IDs or "all". Range 1-4094. - This takes precedence over `to` and - `inner_vlan_to`. - inner_vlan_to: Inner VLAN ID to map to. + enabled: enabled + size: Ingress packet truncation size in bytes. _custom_data: _custom_data """ - class DirectionOut(AvdList[DirectionOutItem]): - """Subclass of AvdList with `DirectionOutItem` items.""" - - DirectionOut._item_type = DirectionOutItem - - class DirectionBothItem(AvdModel): + class MacAddress(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "field_from": {"type": str}, - "to": {"type": int}, - "dot1q_tunnel": {"type": bool}, - "inner_vlan_from": {"type": int}, - "network": {"type": bool}, - "_custom_data": {"type": dict}, - } - _field_to_key_map: ClassVar[dict] = {"field_from": "from"} - _key_to_field_map: ClassVar[dict] = {"from": "field_from"} - field_from: str - """VLAN ID or range of VLAN IDs to map from. Range 1-4094.""" - to: int - """VLAN ID to map to.""" - dot1q_tunnel: bool | None - inner_vlan_from: int | None - """Inner VLAN ID to map from.""" - network: bool | None - """ - Enable use of network-side VLAN ID. - This setting can only be enabled when `inner_vlan_from` is - defined. - """ + _fields: ClassVar[dict] = {"source": {"type": str}, "destination": {"type": str}, "_custom_data": {"type": dict}} + source: str | None + """MAC address for the source.""" + destination: str | None + """MAC address for the destination.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30674,163 +31937,364 @@ class DirectionBothItem(AvdModel): def __init__( self, *, - field_from: str | UndefinedType = Undefined, - to: int | UndefinedType = Undefined, - dot1q_tunnel: bool | None | UndefinedType = Undefined, - inner_vlan_from: int | None | UndefinedType = Undefined, - network: bool | None | UndefinedType = Undefined, + source: str | None | UndefinedType = Undefined, + destination: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - DirectionBothItem. + MacAddress. Subclass of AvdModel. Args: - field_from: VLAN ID or range of VLAN IDs to map from. Range 1-4094. - to: VLAN ID to map to. - dot1q_tunnel: dot1q_tunnel - inner_vlan_from: Inner VLAN ID to map from. - network: - Enable use of network-side VLAN ID. - This setting can only be enabled when `inner_vlan_from` is - defined. + source: MAC address for the source. + destination: MAC address for the destination. _custom_data: _custom_data """ - class DirectionBoth(AvdList[DirectionBothItem]): - """Subclass of AvdList with `DirectionBothItem` items.""" + class Encapsulation(AvdModel): + """Subclass of AvdModel.""" - DirectionBoth._item_type = DirectionBothItem + class Gre(AvdModel): + """Subclass of AvdModel.""" - _fields: ClassVar[dict] = { - "in_required": {"type": bool}, - "out_required": {"type": bool}, - "direction_in": {"type": DirectionIn}, - "direction_out": {"type": DirectionOut}, - "direction_both": {"type": DirectionBoth}, - "_custom_data": {"type": dict}, - } - in_required: bool | None - """Drop the ingress traffic that do not match any VLAN mapping.""" - out_required: bool | None - """Drop the egress traffic that do not match any VLAN mapping.""" - direction_in: DirectionIn - """ - Map ingress traffic only. + class ProtocolsItem(AvdModel): + """Subclass of AvdModel.""" - Subclass of AvdList with `DirectionInItem` items. - """ - direction_out: DirectionOut - """ - Map egress traffic only. + _fields: ClassVar[dict] = { + "protocol": {"type": str}, + "strip": {"type": bool}, + "feature_header_length": {"type": int}, + "re_encapsulation_ethernet_header": {"type": bool}, + "_custom_data": {"type": dict}, + } + protocol: str + """ + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + """ + strip: bool | None + """This is a required key to strip GRE encapsulation header with protocols.""" + feature_header_length: int | None + """ + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + """ + re_encapsulation_ethernet_header: bool | None + """ + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + """ + _custom_data: dict[str, Any] - Subclass of AvdList with `DirectionOutItem` items. - """ - direction_both: DirectionBoth - """ - Map both egress and ingress traffic. + if TYPE_CHECKING: - Subclass of AvdList with `DirectionBothItem` items. - """ - _custom_data: dict[str, Any] + def __init__( + self, + *, + protocol: str | UndefinedType = Undefined, + strip: bool | None | UndefinedType = Undefined, + feature_header_length: int | None | UndefinedType = Undefined, + re_encapsulation_ethernet_header: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + ProtocolsItem. - if TYPE_CHECKING: - def __init__( - self, - *, - in_required: bool | None | UndefinedType = Undefined, - out_required: bool | None | UndefinedType = Undefined, - direction_in: DirectionIn | UndefinedType = Undefined, - direction_out: DirectionOut | UndefinedType = Undefined, - direction_both: DirectionBoth | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - VlanTranslations. + Subclass of AvdModel. + + Args: + protocol: + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + strip: This is a required key to strip GRE encapsulation header with protocols. + feature_header_length: + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + re_encapsulation_ethernet_header: + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + _custom_data: _custom_data + + """ + + class Protocols(AvdIndexedList[str, ProtocolsItem]): + """Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`).""" + + _primary_key: ClassVar[str] = "protocol" + + Protocols._item_type = ProtocolsItem + + class DestinationsItem(AvdModel): + """Subclass of AvdModel.""" + + class ProtocolsItem(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = { + "protocol": {"type": str}, + "strip": {"type": bool}, + "feature_header_length": {"type": int}, + "re_encapsulation_ethernet_header": {"type": bool}, + "_custom_data": {"type": dict}, + } + protocol: str + """ + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + """ + strip: bool | None + """This is a required key to strip GRE encapsulation header for specific destination with protocols.""" + feature_header_length: int | None + """ + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + """ + re_encapsulation_ethernet_header: bool | None + """ + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + protocol: str | UndefinedType = Undefined, + strip: bool | None | UndefinedType = Undefined, + feature_header_length: int | None | UndefinedType = Undefined, + re_encapsulation_ethernet_header: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + ProtocolsItem. + + + Subclass of AvdModel. + + Args: + protocol: + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., + "0x0". + strip: This is a required key to strip GRE encapsulation header for specific destination with protocols. + feature_header_length: + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for + protocol 0x0. + re_encapsulation_ethernet_header: + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the + EOS running-config for protocol 0x0. + _custom_data: _custom_data + + """ + + class Protocols(AvdIndexedList[str, ProtocolsItem]): + """Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`).""" + + _primary_key: ClassVar[str] = "protocol" + + Protocols._item_type = ProtocolsItem + + _fields: ClassVar[dict] = { + "destination": {"type": str}, + "source": {"type": str}, + "strip": {"type": bool}, + "protocols": {"type": Protocols}, + "_custom_data": {"type": dict}, + } + destination: str + """Destination IP address of tunnel packets.""" + source: str | None + """ + Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any + GRE packet that matches the `destination` is terminated. + """ + strip: bool | None + """Strip GRE encapsulation header for specific destination.""" + protocols: Protocols + """Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`).""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + destination: str | UndefinedType = Undefined, + source: str | None | UndefinedType = Undefined, + strip: bool | None | UndefinedType = Undefined, + protocols: Protocols | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + DestinationsItem. + + + Subclass of AvdModel. + + Args: + destination: Destination IP address of tunnel packets. + source: + Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any + GRE packet that matches the `destination` is terminated. + strip: Strip GRE encapsulation header for specific destination. + protocols: Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key is `protocol` (`str`). + _custom_data: _custom_data + """ - Subclass of AvdModel. + class Destinations(AvdIndexedList[str, DestinationsItem]): + """Subclass of AvdIndexedList with `DestinationsItem` items. Primary key is `destination` (`str`).""" - Args: - in_required: Drop the ingress traffic that do not match any VLAN mapping. - out_required: Drop the egress traffic that do not match any VLAN mapping. - direction_in: - Map ingress traffic only. + _primary_key: ClassVar[str] = "destination" - Subclass of AvdList with `DirectionInItem` items. - direction_out: - Map egress traffic only. + Destinations._item_type = DestinationsItem - Subclass of AvdList with `DirectionOutItem` items. - direction_both: - Map both egress and ingress traffic. + _fields: ClassVar[dict] = { + "strip": {"type": bool}, + "protocols": {"type": Protocols}, + "destinations": {"type": Destinations}, + "_custom_data": {"type": dict}, + } + strip: bool | None + """Strip GRE encapsulation header for all GRE tunnels.""" + protocols: Protocols + """ + Protocols for all destinations; destination-specific protocols should be set under the + `destinations[].protocols` key. - Subclass of AvdList with `DirectionBothItem` items. - _custom_data: _custom_data + Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key + is `protocol` (`str`). + """ + destinations: Destinations + """ + In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are + mutually exclusive. + Subclass of AvdIndexedList with `DestinationsItem` items. Primary key is + `destination` (`str`). """ + _custom_data: dict[str, Any] - class BackupLink(AvdModel): - """Subclass of AvdModel.""" + if TYPE_CHECKING: - _fields: ClassVar[dict] = {"interface": {"type": str}, "prefer_vlan": {"type": str}, "_custom_data": {"type": dict}} - interface: str - """Backup interface. Example - Ethernet4, Vlan10 etc.""" - prefer_vlan: str | None - """VLANs to carry on the backup interface (1-4094).""" - _custom_data: dict[str, Any] + def __init__( + self, + *, + strip: bool | None | UndefinedType = Undefined, + protocols: Protocols | UndefinedType = Undefined, + destinations: Destinations | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Gre. - if TYPE_CHECKING: - def __init__( - self, - *, - interface: str | UndefinedType = Undefined, - prefer_vlan: str | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - BackupLink. + Subclass of AvdModel. + Args: + strip: Strip GRE encapsulation header for all GRE tunnels. + protocols: + Protocols for all destinations; destination-specific protocols should be set under the + `destinations[].protocols` key. + + Subclass of AvdIndexedList with `ProtocolsItem` items. Primary key + is `protocol` (`str`). + destinations: + In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are + mutually exclusive. + + Subclass of AvdIndexedList with `DestinationsItem` items. Primary key is + `destination` (`str`). + _custom_data: _custom_data - Subclass of AvdModel. + """ - Args: - interface: Backup interface. Example - Ethernet4, Vlan10 etc. - prefer_vlan: VLANs to carry on the backup interface (1-4094). - _custom_data: _custom_data + _fields: ClassVar[dict] = {"vxlan_strip": {"type": bool}, "gre": {"type": Gre}, "_custom_data": {"type": dict}} + vxlan_strip: bool | None + """ + Strip VXLAN encapsulation header. + `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually + exclusive. + `mpls_pop_all` takes precedence. + """ + gre: Gre + """Subclass of AvdModel.""" + _custom_data: dict[str, Any] - """ + if TYPE_CHECKING: - class Backup(AvdModel): - """Subclass of AvdModel.""" + def __init__( + self, + *, + vxlan_strip: bool | None | UndefinedType = Undefined, + gre: Gre | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + Encapsulation. + + + Subclass of AvdModel. + + Args: + vxlan_strip: + Strip VXLAN encapsulation header. + `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually + exclusive. + `mpls_pop_all` takes precedence. + gre: Subclass of AvdModel. + _custom_data: _custom_data + + """ _fields: ClassVar[dict] = { - "dest_macaddr": {"type": str}, - "initial_mac_move_delay": {"type": int}, - "mac_move_burst": {"type": int}, - "mac_move_burst_interval": {"type": int}, - "preemption_delay": {"type": int}, + "allowed_vlan": {"type": str}, + "default": {"type": Default}, + "identity": {"type": Identity}, + "mpls_pop_all": {"type": bool}, + "native_vlan": {"type": int}, + "truncation": {"type": Truncation}, + "mac_address": {"type": MacAddress}, + "encapsulation": {"type": Encapsulation}, "_custom_data": {"type": dict}, } - dest_macaddr: str | None + allowed_vlan: str | None + """VLAN ID or range(s) of VLAN IDs within range 1-4094.""" + default: Default """ - Destination MAC address for MAC move updates. - The mac address should be multicast or broadcast. - Example: 01:00:00:00:00:00 + Default tap destination config. + + Subclass of AvdModel. """ - initial_mac_move_delay: int | None - """Initial MAC move delay in milliseconds.""" - mac_move_burst: int | None - """Size of MAC move bursts.""" - mac_move_burst_interval: int | None - """MAC move burst interval in milliseconds.""" - preemption_delay: int | None - """Preemption delay in milliseconds.""" + identity: Identity + """Subclass of AvdModel.""" + mpls_pop_all: bool | None + """Pop all MPLS labels.""" + native_vlan: int | None + """Native VLAN ID when interface is in tap mode.""" + truncation: Truncation + """Subclass of AvdModel.""" + mac_address: MacAddress + """Subclass of AvdModel.""" + encapsulation: Encapsulation + """Subclass of AvdModel.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30838,75 +32302,49 @@ class Backup(AvdModel): def __init__( self, *, - dest_macaddr: str | None | UndefinedType = Undefined, - initial_mac_move_delay: int | None | UndefinedType = Undefined, - mac_move_burst: int | None | UndefinedType = Undefined, - mac_move_burst_interval: int | None | UndefinedType = Undefined, - preemption_delay: int | None | UndefinedType = Undefined, + allowed_vlan: str | None | UndefinedType = Undefined, + default: Default | UndefinedType = Undefined, + identity: Identity | UndefinedType = Undefined, + mpls_pop_all: bool | None | UndefinedType = Undefined, + native_vlan: int | None | UndefinedType = Undefined, + truncation: Truncation | UndefinedType = Undefined, + mac_address: MacAddress | UndefinedType = Undefined, + encapsulation: Encapsulation | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Backup. + Tap. Subclass of AvdModel. Args: - dest_macaddr: - Destination MAC address for MAC move updates. - The mac address should be multicast or broadcast. - Example: 01:00:00:00:00:00 - initial_mac_move_delay: Initial MAC move delay in milliseconds. - mac_move_burst: Size of MAC move bursts. - mac_move_burst_interval: MAC move burst interval in milliseconds. - preemption_delay: Preemption delay in milliseconds. + allowed_vlan: VLAN ID or range(s) of VLAN IDs within range 1-4094. + default: + Default tap destination config. + + Subclass of AvdModel. + identity: Subclass of AvdModel. + mpls_pop_all: Pop all MPLS labels. + native_vlan: Native VLAN ID when interface is in tap mode. + truncation: Subclass of AvdModel. + mac_address: Subclass of AvdModel. + encapsulation: Subclass of AvdModel. _custom_data: _custom_data """ - class PortSecurity(AvdModel): + class Tool(AvdModel): """Subclass of AvdModel.""" - class MacAddressMaximum(AvdModel): - """Subclass of AvdModel.""" - - _fields: ClassVar[dict] = {"disabled": {"type": bool}, "limit": {"type": int}, "_custom_data": {"type": dict}} - disabled: bool | None - """Disable port level check for port security (only in violation 'shutdown' mode).""" - limit: int | None - """MAC address limit.""" - _custom_data: dict[str, Any] - - if TYPE_CHECKING: - - def __init__( - self, - *, - disabled: bool | None | UndefinedType = Undefined, - limit: int | None | UndefinedType = Undefined, - _custom_data: dict[str, Any] | UndefinedType = Undefined, - ) -> None: - """ - MacAddressMaximum. - - - Subclass of AvdModel. - - Args: - disabled: Disable port level check for port security (only in violation 'shutdown' mode). - limit: MAC address limit. - _custom_data: _custom_data - - """ - - class Violation(AvdModel): + class Encapsulation(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"mode": {"type": str}, "protect_log": {"type": bool}, "_custom_data": {"type": dict}} - mode: Literal["shutdown", "protect"] | None - """Configure port security mode.""" - protect_log: bool | None - """Log new addresses seen after limit is reached in protect mode.""" + _fields: ClassVar[dict] = {"dot1br_strip": {"type": bool}, "vn_tag_strip": {"type": bool}, "_custom_data": {"type": dict}} + dot1br_strip: bool | None + """Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS.""" + vn_tag_strip: bool | None + """Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30914,36 +32352,35 @@ class Violation(AvdModel): def __init__( self, *, - mode: Literal["shutdown", "protect"] | None | UndefinedType = Undefined, - protect_log: bool | None | UndefinedType = Undefined, + dot1br_strip: bool | None | UndefinedType = Undefined, + vn_tag_strip: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - Violation. + Encapsulation. Subclass of AvdModel. Args: - mode: Configure port security mode. - protect_log: Log new addresses seen after limit is reached in protect mode. + dot1br_strip: Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. + vn_tag_strip: Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. _custom_data: _custom_data """ - class VlansItem(AvdModel): + class Identity(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"range": {"type": str}, "mac_address_maximum": {"type": int}, "_custom_data": {"type": dict}} - range: str - """ - VLAN ID or range(s) of VLAN IDs, <1-4094>. - Example: - - 3 - - 1,3 - - 1-10 - """ - mac_address_maximum: int | None + _fields: ClassVar[dict] = { + "tag": {"type": str}, + "dot1q_dzgre_source": {"type": str}, + "qinq_dzgre_source": {"type": str}, + "_custom_data": {"type": dict}, + } + tag: Literal["dot1q", "qinq"] | None + dot1q_dzgre_source: Literal["policy", "port"] | None + qinq_dzgre_source: Literal["policy inner port", "port inner policy"] | None _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -30951,60 +32388,58 @@ class VlansItem(AvdModel): def __init__( self, *, - range: str | UndefinedType = Undefined, - mac_address_maximum: int | None | UndefinedType = Undefined, + tag: Literal["dot1q", "qinq"] | None | UndefinedType = Undefined, + dot1q_dzgre_source: Literal["policy", "port"] | None | UndefinedType = Undefined, + qinq_dzgre_source: Literal["policy inner port", "port inner policy"] | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - VlansItem. + Identity. Subclass of AvdModel. Args: - range: - VLAN ID or range(s) of VLAN IDs, <1-4094>. - Example: # fmt: skip - - 3 - - 1,3 - - 1-10 - mac_address_maximum: mac_address_maximum + tag: tag + dot1q_dzgre_source: dot1q_dzgre_source + qinq_dzgre_source: qinq_dzgre_source _custom_data: _custom_data """ - class Vlans(AvdIndexedList[str, VlansItem]): - """Subclass of AvdIndexedList with `VlansItem` items. Primary key is `range` (`str`).""" - - _primary_key: ClassVar[str] = "range" + class Groups(AvdList[str]): + """Subclass of AvdList with `str` items.""" - Vlans._item_type = VlansItem + Groups._item_type = str _fields: ClassVar[dict] = { - "enabled": {"type": bool}, - "mac_address_maximum": {"type": MacAddressMaximum}, - "violation": {"type": Violation}, - "vlan_default_mac_address_maximum": {"type": int}, - "vlans": {"type": Vlans}, + "mpls_pop_all": {"type": bool}, + "encapsulation": {"type": Encapsulation}, + "allowed_vlan": {"type": str}, + "identity": {"type": Identity}, + "groups": {"type": Groups}, + "dot1q_remove_outer_vlan_tag": {"type": str}, "_custom_data": {"type": dict}, } - enabled: bool | None - mac_address_maximum: MacAddressMaximum + mpls_pop_all: bool | None + """Pop all MPLS labels.""" + encapsulation: Encapsulation + """Subclass of AvdModel.""" + allowed_vlan: str | None + """VLAN ID or range of VLAN IDs within range 1-4094.""" + identity: Identity + """Subclass of AvdModel.""" + groups: Groups """ - Maximum number of MAC addresses allowed on the interface. + Tool groups for the interface. - Subclass of AvdModel. + Subclass of AvdList with `str` items. """ - violation: Violation + dot1q_remove_outer_vlan_tag: str | None """ - Configure violation mode (shutdown or protect), EOS default is 'shutdown'. - - Subclass of AvdModel. + Indices of vlan tags to be removed. + Range: 1-2 """ - vlan_default_mac_address_maximum: int | None - """Default maximum MAC addresses for all VLANs on this interface.""" - vlans: Vlans - """Subclass of AvdIndexedList with `VlansItem` items. Primary key is `range` (`str`).""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -31012,31 +32447,32 @@ class Vlans(AvdIndexedList[str, VlansItem]): def __init__( self, *, - enabled: bool | None | UndefinedType = Undefined, - mac_address_maximum: MacAddressMaximum | UndefinedType = Undefined, - violation: Violation | UndefinedType = Undefined, - vlan_default_mac_address_maximum: int | None | UndefinedType = Undefined, - vlans: Vlans | UndefinedType = Undefined, + mpls_pop_all: bool | None | UndefinedType = Undefined, + encapsulation: Encapsulation | UndefinedType = Undefined, + allowed_vlan: str | None | UndefinedType = Undefined, + identity: Identity | UndefinedType = Undefined, + groups: Groups | UndefinedType = Undefined, + dot1q_remove_outer_vlan_tag: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ - PortSecurity. + Tool. Subclass of AvdModel. Args: - enabled: enabled - mac_address_maximum: - Maximum number of MAC addresses allowed on the interface. - - Subclass of AvdModel. - violation: - Configure violation mode (shutdown or protect), EOS default is 'shutdown'. + mpls_pop_all: Pop all MPLS labels. + encapsulation: Subclass of AvdModel. + allowed_vlan: VLAN ID or range of VLAN IDs within range 1-4094. + identity: Subclass of AvdModel. + groups: + Tool groups for the interface. - Subclass of AvdModel. - vlan_default_mac_address_maximum: Default maximum MAC addresses for all VLANs on this interface. - vlans: Subclass of AvdIndexedList with `VlansItem` items. Primary key is `range` (`str`). + Subclass of AvdList with `str` items. + dot1q_remove_outer_vlan_tag: + Indices of vlan tags to be removed. + Range: 1-2 _custom_data: _custom_data """ @@ -31055,6 +32491,8 @@ def __init__( "backup_link": {"type": BackupLink}, "backup": {"type": Backup}, "port_security": {"type": PortSecurity}, + "tap": {"type": Tap}, + "tool": {"type": Tool}, "_custom_data": {"type": dict}, } enabled: bool | None @@ -31104,6 +32542,24 @@ def __init__( """ port_security: PortSecurity """Subclass of AvdModel.""" + tap: Tap + """ + In tap mode, the interface operates as a tap port. + Tap ports receive traffic for replication on one + or more tool ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. + """ + tool: Tool + """ + In tool mode, the interface operates as a tool port. + Tool ports replicate traffic received by tap + ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. + """ _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -31124,6 +32580,8 @@ def __init__( backup_link: BackupLink | UndefinedType = Undefined, backup: Backup | UndefinedType = Undefined, port_security: PortSecurity | UndefinedType = Undefined, + tap: Tap | UndefinedType = Undefined, + tool: Tool | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -31163,6 +32621,20 @@ def __init__( Subclass of AvdModel. port_security: Subclass of AvdModel. + tap: + In tap mode, the interface operates as a tap port. + Tap ports receive traffic for replication on one + or more tool ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. + tool: + In tool mode, the interface operates as a tool port. + Tool ports replicate traffic received by tap + ports. + This setting applies only to parent interfaces. + + Subclass of AvdModel. _custom_data: _custom_data """ diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml index 537d6b04e01..4b0702d8bba 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml @@ -4140,6 +4140,257 @@ keys: required: true convert_types: - str + tap: + description: 'In tap mode, the interface operates as a tap port. + + Tap ports receive traffic for replication on one or more tool ports. + + This setting applies only to parent interfaces.' + type: dict + keys: + allowed_vlan: + type: str + convert_types: + - int + description: VLAN ID or range(s) of VLAN IDs within range 1-4094. + default: + description: Default tap destination config. + type: dict + keys: + groups: + description: Tap group names for the interface. + type: list + items: + type: str + interfaces: + description: Interfaces like - Ethernet1, InternalRecirc1, + Port-Channel1, Recirc-Channel1. + type: list + items: + type: str + nexthop_groups: + type: list + description: Default nexthop-group names. + items: + type: str + identity: + type: dict + keys: + id: + type: int + convert_types: + - str + min: 1 + max: 65535 + description: Tap port VLAN ID (1-4094) or DzGRE extended ID + (1-65535). + inner_vlan: + description: Tap port inner VLAN ID. Only applicable if `id` + is a VLAN ID (1-4094). + type: int + convert_types: + - str + min: 1 + max: 4094 + mpls_pop_all: + type: bool + description: Pop all MPLS labels. + native_vlan: + type: int + convert_types: + - str + min: 1 + max: 4094 + description: Native VLAN ID when interface is in tap mode. + truncation: + type: dict + keys: + enabled: + type: bool + size: + description: Ingress packet truncation size in bytes. + type: int + convert_types: + - str + min: 100 + max: 9236 + mac_address: + type: dict + keys: + source: + type: str + pattern: ^([0-9a-f]{2}:){5}[0-9a-f]{2}$ + description: MAC address for the source. + destination: + type: str + pattern: ^([0-9a-f]{2}:){5}[0-9a-f]{2}$ + description: MAC address for the destination. + encapsulation: + type: dict + keys: + vxlan_strip: + type: bool + description: 'Strip VXLAN encapsulation header. + + `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually + exclusive. + + `mpls_pop_all` takes precedence.' + gre: + type: dict + keys: + strip: + type: bool + description: Strip GRE encapsulation header for all GRE + tunnels. + protocols: + type: list + primary_key: protocol + description: Protocols for all destinations; destination-specific + protocols should be set under the `destinations[].protocols` + key. + items: + type: dict + keys: + protocol: + type: str + description: 'Protocol type in GRE header. + + Valid range: 0x0-0xFFFF. The value must be enclosed + in quotes, e.g., "0x0".' + strip: + type: bool + description: This is a required key to strip GRE encapsulation + header with protocols. + feature_header_length: + description: 'Feature header length in bytes. + + Note: This setting does not appear in the EOS running-config + for protocol 0x0.' + type: int + convert_types: + - str + min: 1 + max: 16 + re_encapsulation_ethernet_header: + type: bool + description: 'Extra ethernet header to prepend to + the terminated packet. + + Note: This setting does not appear in the EOS running-config + for protocol 0x0.' + destinations: + type: list + description: In EOS, `gre.strip` and `destinations.destination/source.strip` + (without defining protocols) are mutually exclusive. + primary_key: destination + items: + type: dict + keys: + destination: + description: Destination IP address of tunnel packets. + type: str + source: + description: Source IP address of tunnel packets. + Applied only when destination is defined. When not + defined; any GRE packet that matches the `destination` + is terminated. + type: str + strip: + type: bool + description: Strip GRE encapsulation header for specific + destination. + protocols: + type: list + primary_key: protocol + items: + type: dict + keys: + protocol: + type: str + description: 'Protocol type in GRE header. + + Valid range: 0x0-0xFFFF. The value must be + enclosed in quotes, e.g., "0x0".' + strip: + type: bool + description: This is a required key to strip + GRE encapsulation header for specific destination + with protocols. + feature_header_length: + description: 'Feature header length in bytes. + + Note: This setting does not appear in the + EOS running-config for protocol 0x0.' + type: int + convert_types: + - str + min: 1 + max: 16 + re_encapsulation_ethernet_header: + type: bool + description: 'Extra ethernet header to prepend + to the terminated packet. + + Note: This setting does not appear in the + EOS running-config for protocol 0x0.' + tool: + description: 'In tool mode, the interface operates as a tool port. + + Tool ports replicate traffic received by tap ports. + + This setting applies only to parent interfaces.' + type: dict + keys: + mpls_pop_all: + type: bool + description: Pop all MPLS labels. + encapsulation: + type: dict + keys: + dot1br_strip: + type: bool + description: Remove a 802.1 BR tag in packet header. 'mpls_pop_all' + takes precedence over 'dot1br_strip' in EOS. + vn_tag_strip: + type: bool + description: Remove a VN-tag in packet header. 'mpls_pop_all' + takes precedence over 'vn_tag_strip' in EOS. + allowed_vlan: + type: str + convert_types: + - int + description: VLAN ID or range of VLAN IDs within range 1-4094. + identity: + type: dict + keys: + tag: + type: str + valid_values: + - dot1q + - qinq + dot1q_dzgre_source: + type: str + valid_values: + - policy + - port + qinq_dzgre_source: + type: str + valid_values: + - policy inner port + - port inner policy + groups: + type: list + items: + type: str + description: Tool groups for the interface. + dot1q_remove_outer_vlan_tag: + type: str + convert_types: + - int + description: 'Indices of vlan tags to be removed. + + Range: 1-2' eos_cli: type: str description: Multiline EOS CLI rendered directly on the ethernet interface @@ -10954,6 +11205,12 @@ keys: type: int convert_types: - str + tap: + type: dict + $ref: eos_cli_config_gen#/keys/ethernet_interfaces/items/keys/switchport/keys/tap + tool: + type: dict + $ref: eos_cli_config_gen#/keys/ethernet_interfaces/items/keys/switchport/keys/tool validate_state: type: bool description: Set to false to disable interface state and LLDP topology validation diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml index 98aebab7287..2dca349d154 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml @@ -1958,6 +1958,231 @@ keys: required: true convert_types: - str + tap: + description: |- + In tap mode, the interface operates as a tap port. + Tap ports receive traffic for replication on one or more tool ports. + This setting applies only to parent interfaces. + type: dict + keys: + allowed_vlan: + type: str + convert_types: + - int + description: VLAN ID or range(s) of VLAN IDs within range 1-4094. + default: + description: Default tap destination config. + type: dict + keys: + groups: + description: Tap group names for the interface. + type: list + items: + type: str + interfaces: + description: Interfaces like - Ethernet1, InternalRecirc1, Port-Channel1, Recirc-Channel1. + type: list + items: + type: str + nexthop_groups: + type: list + description: Default nexthop-group names. + items: + type: str + identity: + type: dict + keys: + id: + type: int + convert_types: + - str + min: 1 + max: 65535 + description: Tap port VLAN ID (1-4094) or DzGRE extended ID (1-65535). + inner_vlan: + description: Tap port inner VLAN ID. Only applicable if `id` is a VLAN ID (1-4094). + type: int + convert_types: + - str + min: 1 + max: 4094 + mpls_pop_all: + type: bool + description: Pop all MPLS labels. + native_vlan: + type: int + convert_types: + - str + min: 1 + max: 4094 + description: Native VLAN ID when interface is in tap mode. + truncation: + type: dict + keys: + enabled: + type: bool + size: + description: Ingress packet truncation size in bytes. + type: int + convert_types: + - str + min: 100 + max: 9236 + mac_address: + type: dict + keys: + source: + type: str + pattern: "^([0-9a-f]{2}:){5}[0-9a-f]{2}$" + description: MAC address for the source. + destination: + type: str + pattern: "^([0-9a-f]{2}:){5}[0-9a-f]{2}$" + description: MAC address for the destination. + encapsulation: + type: dict + keys: + vxlan_strip: + type: bool + description: |- + Strip VXLAN encapsulation header. + `encapsulation.vxlan_strip` and `mpls_pop_all` are mutually exclusive. + `mpls_pop_all` takes precedence. + gre: + type: dict + keys: + strip: + type: bool + description: Strip GRE encapsulation header for all GRE tunnels. + protocols: + type: list + primary_key: protocol + description: Protocols for all destinations; destination-specific protocols should be set under the `destinations[].protocols` key. + items: + type: dict + keys: + protocol: + type: str + description: |- + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". + strip: + type: bool + description: |- + This is a required key to strip GRE encapsulation header with protocols. + feature_header_length: + description: |- + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for protocol 0x0. + type: int + convert_types: + - str + min: 1 + max: 16 + re_encapsulation_ethernet_header: + type: bool + description: |- + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the EOS running-config for protocol 0x0. + destinations: + type: list + description: |- + In EOS, `gre.strip` and `destinations.destination/source.strip` (without defining protocols) are mutually exclusive. + primary_key: destination + items: + type: dict + keys: + destination: + description: Destination IP address of tunnel packets. + type: str + source: + description: Source IP address of tunnel packets. Applied only when destination is defined. When not defined; any GRE packet that matches the `destination` is terminated. + type: str + strip: + type: bool + description: Strip GRE encapsulation header for specific destination. + protocols: + type: list + primary_key: protocol + items: + type: dict + keys: + protocol: + type: str + description: |- + Protocol type in GRE header. + Valid range: 0x0-0xFFFF. The value must be enclosed in quotes, e.g., "0x0". + strip: + type: bool + description: This is a required key to strip GRE encapsulation header for specific destination with protocols. + feature_header_length: + description: |- + Feature header length in bytes. + Note: This setting does not appear in the EOS running-config for protocol 0x0. + type: int + convert_types: + - str + min: 1 + max: 16 + re_encapsulation_ethernet_header: + type: bool + description: |- + Extra ethernet header to prepend to the terminated packet. + Note: This setting does not appear in the EOS running-config for protocol 0x0. + tool: + description: |- + In tool mode, the interface operates as a tool port. + Tool ports replicate traffic received by tap ports. + This setting applies only to parent interfaces. + type: dict + keys: + mpls_pop_all: + type: bool + description: Pop all MPLS labels. + encapsulation: + type: dict + keys: + dot1br_strip: + type: bool + description: Remove a 802.1 BR tag in packet header. 'mpls_pop_all' takes precedence over 'dot1br_strip' in EOS. + vn_tag_strip: + type: bool + description: Remove a VN-tag in packet header. 'mpls_pop_all' takes precedence over 'vn_tag_strip' in EOS. + allowed_vlan: + type: str + convert_types: + - int + description: VLAN ID or range of VLAN IDs within range 1-4094. + identity: + type: dict + keys: + tag: + type: str + valid_values: + - dot1q + - qinq + dot1q_dzgre_source: + type: str + valid_values: + - policy + - port + qinq_dzgre_source: + type: str + valid_values: + - policy inner port + - port inner policy + groups: + type: list + items: + type: str + description: Tool groups for the interface. + dot1q_remove_outer_vlan_tag: + type: str + convert_types: + - int + description: |- + Indices of vlan tags to be removed. + Range: 1-2 eos_cli: type: str description: Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml index 368d819d5f1..d2d9c89738f 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml @@ -1379,6 +1379,12 @@ keys: type: int convert_types: - str + tap: + type: dict + $ref: "eos_cli_config_gen#/keys/ethernet_interfaces/items/keys/switchport/keys/tap" + tool: + type: dict + $ref: "eos_cli_config_gen#/keys/ethernet_interfaces/items/keys/switchport/keys/tool" validate_state: type: bool description: Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. From 4e963f2561369995f4511eb7fa45cdc1078715af Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:57:50 +0100 Subject: [PATCH 3/7] CI: pre-commit autoupdate (#4817) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83ffb6d6378..80bf9cc2f78 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -75,7 +75,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.8.2 + rev: v0.8.3 hooks: # Run the linter. - id: ruff From 4fb884c5a868dad2dc7ac337b84d65ef27cbf9a1 Mon Sep 17 00:00:00 2001 From: emilarista <73955263+emilarista@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:04:22 +0100 Subject: [PATCH 4/7] Feat(eos_cli_config_gen): Add interface traffic engineering and TE admin group for ethernet/port-channel (#4754) --- .../documentation/devices/host1.md | 44 ++++++++ .../intended/configs/host1.cfg | 28 +++++ .../host_vars/host1/ethernet-interfaces.yml | 26 +++++ .../host1/port-channel-interfaces.yml | 22 ++++ .../docs/tables/ethernet-interfaces.md | 12 +++ .../docs/tables/port-channel-interfaces.md | 12 +++ .../documentation/ethernet-interfaces.j2 | 18 ++++ .../documentation/port-channel-interfaces.j2 | 18 ++++ .../j2templates/eos/ethernet-interfaces.j2 | 6 ++ .../eos/port-channel-interfaces.j2 | 6 ++ .../_eos_cli_config_gen/schema/__init__.py | 102 ++++++++++++++++++ .../schema/eos_cli_config_gen.schema.yml | 28 +++++ .../ethernet_interfaces.schema.yml | 13 +++ .../port_channel_interfaces.schema.yml | 13 +++ 14 files changed, 348 insertions(+) diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md index 728b9e0be8f..6692004d16f 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md @@ -3629,6 +3629,8 @@ interface Dps1 | Ethernet66 | Multiple VRIDs and tracking | - | 192.0.2.2/25 | default | - | False | - | - | | Ethernet80 | LAG Member | 17 | *192.0.2.3/31 | **default | **- | **- | **- | **- | | Ethernet81/2 | LAG Member LACP fallback LLDP ZTP VLAN | 112 | *dhcp | **default | **- | **- | **- | **- | +| Ethernet81/3 | Traffic Engineering Interface | - | 100.64.127.0/31 | default | - | False | - | - | +| Ethernet81/4 | Traffic Engineering Interface | - | 100.64.127.0/31 | default | - | False | - | - | *Inherited from Port-Channel Interface @@ -3785,6 +3787,12 @@ interface Dps1 | Ethernet5 | 127 | | Ethernet6 | disabled | +#### Traffic Engineering + +| Interface | Enabled | Administrative Groups | +| --------- | ------- | --------------------- | +| Ethernet81/3 | True | 3,15-29,testgrp | + #### Ethernet Interfaces Device Configuration ```eos @@ -4755,6 +4763,21 @@ interface Ethernet81/2 lldp tlv transmit ztp vlan 112 spanning-tree portfast ! +interface Ethernet81/3 + description Traffic Engineering Interface + no shutdown + no switchport + ip address 100.64.127.0/31 + traffic-engineering + traffic-engineering administrative-group 3,15-29,testgrp +! +interface Ethernet81/4 + description Traffic Engineering Interface + no shutdown + no switchport + ip address 100.64.127.0/31 + traffic-engineering administrative-group 4,7-100,testgrp +! interface Ethernet81/10 description isis_port_channel_member channel-group 110 mode active @@ -4941,6 +4964,8 @@ interface Ethernet84 | Port-Channel112 | LACP fallback individual | - | dhcp | default | - | - | - | - | | Port-Channel113 | interface_with_mpls_enabled | - | 172.31.128.9/31 | default | - | - | - | - | | Port-Channel114 | interface_with_mpls_disabled | - | 172.31.128.10/31 | default | - | - | - | - | +| Port-Channel136 | Test_te_admin_groups | - | 100.64.127.2/31 | default | - | - | - | - | +| Port-Channel137 | Traffic Engineering Interface | - | 100.64.127.4/31 | default | - | - | - | - | ##### IP NAT: Source Static @@ -4984,6 +5009,12 @@ interface Ethernet84 | Port-Channel100 | EVPN_UNDERLAY | - | - | - | - | - | Level-1: md5
Level-2: text | | Port-Channel110 | ISIS_TEST | True | 99 | point-to-point | level-2 | True | - | +#### Traffic Engineering + +| Interface | Enabled | Administrative Groups | +| --------- | ------- | --------------------- | +| Port-Channel136 | True | 7 | + #### Port-Channel Interfaces Device Configuration ```eos @@ -5586,6 +5617,19 @@ interface Port-Channel135 switchport tap encapsulation gre protocol 0x2 feature header length 3 strip switchport tap encapsulation gre protocol 0x3 feature header length 2 strip re-encapsulation ethernet switchport tap encapsulation gre protocol 0x10 strip +! +interface Port-Channel136 + description Test_te_admin_groups + no switchport + ip address 100.64.127.2/31 + traffic-engineering + traffic-engineering administrative-group 7 +! +interface Port-Channel137 + description Traffic Engineering Interface + no switchport + ip address 100.64.127.4/31 + traffic-engineering administrative-group 4,7-100,testgrp ``` ### Loopback Interfaces diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg index 60c6848eda4..76bc4a40826 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg @@ -2216,6 +2216,19 @@ interface Port-Channel135 switchport tap encapsulation gre protocol 0x3 feature header length 2 strip re-encapsulation ethernet switchport tap encapsulation gre protocol 0x10 strip ! +interface Port-Channel136 + description Test_te_admin_groups + no switchport + ip address 100.64.127.2/31 + traffic-engineering + traffic-engineering administrative-group 7 +! +interface Port-Channel137 + description Traffic Engineering Interface + no switchport + ip address 100.64.127.4/31 + traffic-engineering administrative-group 4,7-100,testgrp +! interface Dps1 description Test DPS Interface shutdown @@ -3192,6 +3205,21 @@ interface Ethernet81/2 lldp tlv transmit ztp vlan 112 spanning-tree portfast ! +interface Ethernet81/3 + description Traffic Engineering Interface + no shutdown + no switchport + ip address 100.64.127.0/31 + traffic-engineering + traffic-engineering administrative-group 3,15-29,testgrp +! +interface Ethernet81/4 + description Traffic Engineering Interface + no shutdown + no switchport + ip address 100.64.127.0/31 + traffic-engineering administrative-group 4,7-100,testgrp +! interface Ethernet81/10 description isis_port_channel_member channel-group 110 mode active diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml index b6c5faea864..900073e6f8e 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml @@ -1849,6 +1849,32 @@ ethernet_interfaces: lldp: ztp_vlan: 112 + - name: Ethernet81/3 + description: Traffic Engineering Interface + shutdown: false + switchport: + enabled: false + ip_address: 100.64.127.0/31 + traffic_engineering: + enabled: true + administrative_groups: + - 3 + - 15-29 + - testgrp + + - name: Ethernet81/4 + description: Traffic Engineering Interface + shutdown: false + switchport: + enabled: false + ip_address: 100.64.127.0/31 + traffic_engineering: + enabled: false + administrative_groups: + - 4 + - 7-100 + - testgrp + - name: Ethernet82 description: Switchport_tap_tool switchport: diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml index 16709ce88ce..c03f8e31ee2 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/port-channel-interfaces.yml @@ -1046,3 +1046,25 @@ port_channel_interfaces: feature_header_length: 2 re_encapsulation_ethernet_header: true strip: true + + - name: Port-Channel136 + description: Test_te_admin_groups + switchport: + enabled: false + ip_address: 100.64.127.2/31 + traffic_engineering: + enabled: true + administrative_groups: + - 7 + + - name: Port-Channel137 + description: Traffic Engineering Interface + switchport: + enabled: false + ip_address: 100.64.127.4/31 + traffic_engineering: + enabled: false + administrative_groups: + - 4 + - 7-100 + - testgrp diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md index 2222322c542..e1c7ce9db76 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md @@ -599,6 +599,10 @@ | [        groups](## "ethernet_interfaces.[].switchport.tool.groups") | List, items: String | | | | Tool groups for the interface. | | [          - <str>](## "ethernet_interfaces.[].switchport.tool.groups.[]") | String | | | | | | [        dot1q_remove_outer_vlan_tag](## "ethernet_interfaces.[].switchport.tool.dot1q_remove_outer_vlan_tag") | String | | | | Indices of vlan tags to be removed.
Range: 1-2 | + | [    traffic_engineering](## "ethernet_interfaces.[].traffic_engineering") | Dictionary | | | | | + | [      enabled](## "ethernet_interfaces.[].traffic_engineering.enabled") | Boolean | | | | Whether to enable traffic-engineering on this interface. | + | [      administrative_groups](## "ethernet_interfaces.[].traffic_engineering.administrative_groups") | List, items: String | | | | List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single integers 0-127. | + | [        - <str>](## "ethernet_interfaces.[].traffic_engineering.administrative_groups.[]") | String | | | | | | [    eos_cli](## "ethernet_interfaces.[].eos_cli") | String | | | | Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. | === "YAML" @@ -1913,6 +1917,14 @@ # Indices of vlan tags to be removed. # Range: 1-2 dot1q_remove_outer_vlan_tag: + traffic_engineering: + + # Whether to enable traffic-engineering on this interface. + enabled: + + # List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single integers 0-127. + administrative_groups: + - # Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. eos_cli: diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md index 91739aff4a7..fc514aa5e3a 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/port-channel-interfaces.md @@ -426,6 +426,10 @@ | [        groups](## "port_channel_interfaces.[].switchport.tool.groups") | List, items: String | | | | Tool groups for the interface. | | [          - <str>](## "port_channel_interfaces.[].switchport.tool.groups.[]") | String | | | | | | [        dot1q_remove_outer_vlan_tag](## "port_channel_interfaces.[].switchport.tool.dot1q_remove_outer_vlan_tag") | String | | | | Indices of vlan tags to be removed.
Range: 1-2 | + | [    traffic_engineering](## "port_channel_interfaces.[].traffic_engineering") | Dictionary | | | | | + | [      enabled](## "port_channel_interfaces.[].traffic_engineering.enabled") | Boolean | | | | Whether to enable traffic-engineering on this interface. | + | [      administrative_groups](## "port_channel_interfaces.[].traffic_engineering.administrative_groups") | List, items: String | | | | List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single integers 0-127. | + | [        - <str>](## "port_channel_interfaces.[].traffic_engineering.administrative_groups.[]") | String | | | | | | [    validate_state](## "port_channel_interfaces.[].validate_state") | Boolean | | | | Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. | | [    validate_lldp](## "port_channel_interfaces.[].validate_lldp") | Boolean | | | | Set to false to disable the LLDP topology validation performed by the `eos_validate_state` role. | | [    eos_cli](## "port_channel_interfaces.[].eos_cli") | String | | | | Multiline EOS CLI rendered directly on the port-channel interface in the final EOS configuration. | @@ -1407,6 +1411,14 @@ # Indices of vlan tags to be removed. # Range: 1-2 dot1q_remove_outer_vlan_tag: + traffic_engineering: + + # Whether to enable traffic-engineering on this interface. + enabled: + + # List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single integers 0-127. + administrative_groups: + - # Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. validate_state: diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/ethernet-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/ethernet-interfaces.j2 index 3d358413914..d220b8b5c6e 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/ethernet-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/ethernet-interfaces.j2 @@ -795,6 +795,24 @@ | {{ sync_e_interface.name }} | {{ sync_e_interface.sync_e.priority | arista.avd.default('127') }} | {% endfor %} {% endif %} +{% set te_interfaces = [] %} +{% for ethernet_interface in ethernet_interfaces | arista.avd.natural_sort('name') %} +{% if ethernet_interface.traffic_engineering.enabled is arista.avd.defined(true) %} +{% do te_interfaces.append(ethernet_interface) %} +{% endif %} +{% endfor %} +{% if te_interfaces | length > 0 %} + +#### Traffic Engineering + +| Interface | Enabled | Administrative Groups | +| --------- | ------- | --------------------- | +{% for te_interface in te_interfaces %} +{% set admin_groups = te_interface.traffic_engineering.administrative_groups | arista.avd.default (["-"]) | join(",") %} +{% set te_enabled = te_interface.traffic_engineering.enabled | arista.avd.default ("-") %} +| {{ te_interface.name }} | {{ te_enabled }} | {{ admin_groups }} | +{% endfor %} +{% endif %} #### Ethernet Interfaces Device Configuration diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/port-channel-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/port-channel-interfaces.j2 index b916790f1eb..ada154dc2c1 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/port-channel-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/documentation/port-channel-interfaces.j2 @@ -411,6 +411,24 @@ | {{ port_channel_interface.name }} | {{ isis_instance }} | {{ isis_bfd }} | {{ isis_metric }} | {{ mode }} | {{ isis_circuit_type }} | {{ isis_hello_padding }} | {{ isis_authentication_mode }} | {% endfor %} {% endif %} +{% set port_channel_te_interfaces = [] %} +{% for port_channel_interface in port_channel_interfaces | arista.avd.natural_sort('name') %} +{% if port_channel_interface.traffic_engineering.enabled is arista.avd.defined(true) %} +{% do port_channel_te_interfaces.append(port_channel_interface) %} +{% endif %} +{% endfor %} +{% if port_channel_te_interfaces | length > 0 %} + +#### Traffic Engineering + +| Interface | Enabled | Administrative Groups | +| --------- | ------- | --------------------- | +{% for po_te_interface in port_channel_te_interfaces %} +{% set admin_groups = po_te_interface.traffic_engineering.administrative_groups | arista.avd.default(["-"]) | join(",") %} +{% set te_enabled = po_te_interface.traffic_engineering.enabled | arista.avd.default("-") %} +| {{ po_te_interface.name }} | {{ te_enabled }} | {{ admin_groups }} | +{% endfor %} +{% endif %} #### Port-Channel Interfaces Device Configuration diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 index 9467cd18e05..74b00b5fe65 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 @@ -1103,6 +1103,12 @@ interface {{ ethernet_interface.name }} switchport tool dzgre preserve {% endif %} {% endif %} +{% if ethernet_interface.traffic_engineering.enabled is arista.avd.defined(true) %} + traffic-engineering +{% endif %} +{% if ethernet_interface.traffic_engineering.administrative_groups is arista.avd.defined %} + traffic-engineering administrative-group {{ ethernet_interface.traffic_engineering.administrative_groups | join(",") }} +{% endif %} {% for link_tracking_group in ethernet_interface.link_tracking_groups | arista.avd.natural_sort %} {% if link_tracking_group.name is arista.avd.defined and link_tracking_group.direction is arista.avd.defined %} link tracking group {{ link_tracking_group.name }} {{ link_tracking_group.direction }} diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 index 25cdb756206..ad5262c5f49 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/port-channel-interfaces.j2 @@ -923,6 +923,12 @@ interface {{ port_channel_interface.name }} switchport tool dot1q remove outer {{ port_channel_interface.switchport.tool.dot1q_remove_outer_vlan_tag }} {% endif %} {% endif %} +{% if port_channel_interface.traffic_engineering.enabled is arista.avd.defined(true) %} + traffic-engineering +{% endif %} +{% if port_channel_interface.traffic_engineering.administrative_groups is arista.avd.defined %} + traffic-engineering administrative-group {{ port_channel_interface.traffic_engineering.administrative_groups | join(",") }} +{% endif %} {% for link_tracking_group in port_channel_interface.link_tracking_groups | arista.avd.natural_sort('name') %} {% if link_tracking_group.name is arista.avd.defined and link_tracking_group.direction is arista.avd.defined %} link tracking group {{ link_tracking_group.name }} {{ link_tracking_group.direction }} diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py index a40a32fa721..adb4bba7394 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py @@ -12186,6 +12186,52 @@ def __init__( """ + class TrafficEngineering(AvdModel): + """Subclass of AvdModel.""" + + class AdministrativeGroups(AvdList[str]): + """Subclass of AvdList with `str` items.""" + + AdministrativeGroups._item_type = str + + _fields: ClassVar[dict] = {"enabled": {"type": bool}, "administrative_groups": {"type": AdministrativeGroups}, "_custom_data": {"type": dict}} + enabled: bool | None + """Whether to enable traffic-engineering on this interface.""" + administrative_groups: AdministrativeGroups + """ + List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single + integers 0-127. + + Subclass of AvdList with `str` items. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + enabled: bool | None | UndefinedType = Undefined, + administrative_groups: AdministrativeGroups | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + TrafficEngineering. + + + Subclass of AvdModel. + + Args: + enabled: Whether to enable traffic-engineering on this interface. + administrative_groups: + List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single + integers 0-127. + + Subclass of AvdList with `str` items. + _custom_data: _custom_data + + """ + _fields: ClassVar[dict] = { "name": {"type": str}, "description": {"type": str}, @@ -12300,6 +12346,7 @@ def __init__( "validate_state": {"type": bool}, "validate_lldp": {"type": bool}, "switchport": {"type": Switchport}, + "traffic_engineering": {"type": TrafficEngineering}, "eos_cli": {"type": str}, "_custom_data": {"type": dict}, } @@ -12565,6 +12612,8 @@ def __init__( Subclass of AvdModel. """ + traffic_engineering: TrafficEngineering + """Subclass of AvdModel.""" eos_cli: str | None """Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration.""" _custom_data: dict[str, Any] @@ -12687,6 +12736,7 @@ def __init__( validate_state: bool | None | UndefinedType = Undefined, validate_lldp: bool | None | UndefinedType = Undefined, switchport: Switchport | UndefinedType = Undefined, + traffic_engineering: TrafficEngineering | UndefinedType = Undefined, eos_cli: str | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: @@ -12861,6 +12911,7 @@ def __init__( Subclass of AvdModel. + traffic_engineering: Subclass of AvdModel. eos_cli: Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. _custom_data: _custom_data @@ -32639,6 +32690,52 @@ def __init__( """ + class TrafficEngineering(AvdModel): + """Subclass of AvdModel.""" + + class AdministrativeGroups(AvdList[str]): + """Subclass of AvdList with `str` items.""" + + AdministrativeGroups._item_type = str + + _fields: ClassVar[dict] = {"enabled": {"type": bool}, "administrative_groups": {"type": AdministrativeGroups}, "_custom_data": {"type": dict}} + enabled: bool | None + """Whether to enable traffic-engineering on this interface.""" + administrative_groups: AdministrativeGroups + """ + List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single + integers 0-127. + + Subclass of AvdList with `str` items. + """ + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + enabled: bool | None | UndefinedType = Undefined, + administrative_groups: AdministrativeGroups | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + TrafficEngineering. + + + Subclass of AvdModel. + + Args: + enabled: Whether to enable traffic-engineering on this interface. + administrative_groups: + List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single + integers 0-127. + + Subclass of AvdList with `str` items. + _custom_data: _custom_data + + """ + _fields: ClassVar[dict] = { "name": {"type": str}, "description": {"type": str}, @@ -32729,6 +32826,7 @@ def __init__( "peer_type": {"type": str}, "sflow": {"type": Sflow}, "switchport": {"type": Switchport}, + "traffic_engineering": {"type": TrafficEngineering}, "validate_state": {"type": bool}, "validate_lldp": {"type": bool}, "eos_cli": {"type": str}, @@ -32915,6 +33013,8 @@ def __init__( """Subclass of AvdModel.""" switchport: Switchport """Subclass of AvdModel.""" + traffic_engineering: TrafficEngineering + """Subclass of AvdModel.""" validate_state: bool | None """ Set to false to disable interface state and LLDP topology validation performed by the @@ -33020,6 +33120,7 @@ def __init__( peer_type: str | None | UndefinedType = Undefined, sflow: Sflow | UndefinedType = Undefined, switchport: Switchport | UndefinedType = Undefined, + traffic_engineering: TrafficEngineering | UndefinedType = Undefined, validate_state: bool | None | UndefinedType = Undefined, validate_lldp: bool | None | UndefinedType = Undefined, eos_cli: str | None | UndefinedType = Undefined, @@ -33145,6 +33246,7 @@ def __init__( peer_type: Key only used for documentation or validation purposes. sflow: Subclass of AvdModel. switchport: Subclass of AvdModel. + traffic_engineering: Subclass of AvdModel. validate_state: Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml index 4b0702d8bba..5d10316999b 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml @@ -4391,6 +4391,20 @@ keys: description: 'Indices of vlan tags to be removed. Range: 1-2' + traffic_engineering: + type: dict + keys: + enabled: + type: bool + description: Whether to enable traffic-engineering on this interface. + administrative_groups: + type: list + description: List of traffic-engineering administrative groups, valid + values are names, ranges 0-127, or single integers 0-127. + items: + type: str + convert_types: + - int eos_cli: type: str description: Multiline EOS CLI rendered directly on the ethernet interface @@ -11211,6 +11225,20 @@ keys: tool: type: dict $ref: eos_cli_config_gen#/keys/ethernet_interfaces/items/keys/switchport/keys/tool + traffic_engineering: + type: dict + keys: + enabled: + type: bool + description: Whether to enable traffic-engineering on this interface. + administrative_groups: + type: list + description: List of traffic-engineering administrative groups, valid + values are names, ranges 0-127, or single integers 0-127. + items: + type: str + convert_types: + - int validate_state: type: bool description: Set to false to disable interface state and LLDP topology validation diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml index 2dca349d154..e9460279a5a 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml @@ -2183,6 +2183,19 @@ keys: description: |- Indices of vlan tags to be removed. Range: 1-2 + traffic_engineering: + type: dict + keys: + enabled: + type: bool + description: Whether to enable traffic-engineering on this interface. + administrative_groups: + type: list + description: List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single integers 0-127. + items: + type: str + convert_types: + - int eos_cli: type: str description: Multiline EOS CLI rendered directly on the ethernet interface in the final EOS configuration. diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml index d2d9c89738f..b1d8ea708fa 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/port_channel_interfaces.schema.yml @@ -1385,6 +1385,19 @@ keys: tool: type: dict $ref: "eos_cli_config_gen#/keys/ethernet_interfaces/items/keys/switchport/keys/tool" + traffic_engineering: + type: dict + keys: + enabled: + type: bool + description: Whether to enable traffic-engineering on this interface. + administrative_groups: + type: list + description: List of traffic-engineering administrative groups, valid values are names, ranges 0-127, or single integers 0-127. + items: + type: str + convert_types: + - int validate_state: type: bool description: Set to false to disable interface state and LLDP topology validation performed by the `eos_validate_state` role. From 3647e850e8136718dbbe0228e01f00ce0e4a091c Mon Sep 17 00:00:00 2001 From: Ernesto <44853461+ernestoherrerab@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:19:54 +0100 Subject: [PATCH 5/7] Feat(eos_designs): sflow_polling_interval (#4820) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Guillaume Mulocher --- .../intended/configs/sflow-tests-l2-leaf1.cfg | 1 + .../intended/configs/sflow-tests-l2-leaf2.cfg | 1 + .../structured_configs/sflow-tests-l2-leaf1.yml | 1 + .../structured_configs/sflow-tests-l2-leaf2.yml | 1 + .../inventory/group_vars/SFLOW_TESTS_L2_LEAFS.yml | 1 + .../docs/tables/management-sflow-settings.md | 4 ++++ python-avd/pyavd/_eos_designs/schema/__init__.py | 12 +++++++++++- .../pyavd/_eos_designs/schema/eos_designs.schema.yml | 5 +++++ .../schema_fragments/sflow_settings.schema.yml | 5 +++++ .../_eos_designs/structured_config/flows/__init__.py | 2 +- 10 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf1.cfg b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf1.cfg index b5c51b22df9..41c3a66b90d 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf1.cfg +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf1.cfg @@ -11,6 +11,7 @@ service routing protocols model multi-agent hostname sflow-tests-l2-leaf1 ! sflow sample 10 +sflow polling-interval 1 sflow vrf MGMT destination 10.10.10.12 sflow vrf MGMT source-interface Management1 sflow destination 10.10.10.10 diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf2.cfg b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf2.cfg index 82db2c4fb18..83e37042a3a 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf2.cfg +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/configs/sflow-tests-l2-leaf2.cfg @@ -11,6 +11,7 @@ service routing protocols model multi-agent hostname sflow-tests-l2-leaf2 ! sflow sample 10 +sflow polling-interval 1 sflow vrf MGMT destination 10.10.10.12 sflow vrf MGMT source-interface Management1 sflow destination 10.10.10.10 diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf1.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf1.yml index 530df0b4500..6ba78f81f48 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf1.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf1.yml @@ -81,6 +81,7 @@ vlan_interfaces: type: inband_mgmt sflow: run: true + polling_interval: 1 sample: 10 destinations: - destination: 10.10.10.10 diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf2.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf2.yml index 74e300cd577..1008235b840 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf2.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/intended/structured_configs/sflow-tests-l2-leaf2.yml @@ -81,6 +81,7 @@ vlan_interfaces: type: inband_mgmt sflow: run: true + polling_interval: 1 sample: 10 destinations: - destination: 10.10.10.10 diff --git a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/group_vars/SFLOW_TESTS_L2_LEAFS.yml b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/group_vars/SFLOW_TESTS_L2_LEAFS.yml index 0c6312cbccf..20e71de2130 100644 --- a/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/group_vars/SFLOW_TESTS_L2_LEAFS.yml +++ b/ansible_collections/arista/avd/molecule/eos_designs_unit_tests/inventory/group_vars/SFLOW_TESTS_L2_LEAFS.yml @@ -4,6 +4,7 @@ type: l2leaf default_mgmt_method: inband sflow_settings: + polling_interval: 1 sample: rate: 10 destinations: diff --git a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/management-sflow-settings.md b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/management-sflow-settings.md index 05f7fbd98e2..6f16301e2ee 100644 --- a/ansible_collections/arista/avd/roles/eos_designs/docs/tables/management-sflow-settings.md +++ b/ansible_collections/arista/avd/roles/eos_designs/docs/tables/management-sflow-settings.md @@ -16,6 +16,7 @@ | [  mlag_interfaces](## "fabric_sflow.mlag_interfaces") | Boolean | | | | Enable sFlow on all MLAG peer interfaces. | | [  l3_interfaces](## "fabric_sflow.l3_interfaces") | Boolean | | | | Enable sFlow on all l3 interfaces. | | [sflow_settings](## "sflow_settings") | Dictionary | | | | sFlow settings.
The sFlow process will only be configured if any interface is enabled for sFlow.
For default enabling of sFlow for various interface types across the fabric see `fabric_sflow`. | + | [  polling_interval](## "sflow_settings.polling_interval") | Integer | | | | Interval in seconds for sending counter data to the sFlow collector. | | [  sample](## "sflow_settings.sample") | Dictionary | | | | | | [    rate](## "sflow_settings.sample.rate") | Integer | | | Min: 1
Max: 4294967295 | Packet sampling rate that defines the average number of ingress packets that pass through an interface for every packet that is sampled.
A rate of 16384 corresponds to an average sample of one per 16384 packets. | | [  destinations](## "sflow_settings.destinations") | List, items: Dictionary | Required | | Min Length: 1 | | @@ -59,6 +60,9 @@ # The sFlow process will only be configured if any interface is enabled for sFlow. # For default enabling of sFlow for various interface types across the fabric see `fabric_sflow`. sflow_settings: + + # Interval in seconds for sending counter data to the sFlow collector. + polling_interval: sample: # Packet sampling rate that defines the average number of ingress packets that pass through an interface for every packet that is sampled. diff --git a/python-avd/pyavd/_eos_designs/schema/__init__.py b/python-avd/pyavd/_eos_designs/schema/__init__.py index d0c0d567286..d043c343d53 100644 --- a/python-avd/pyavd/_eos_designs/schema/__init__.py +++ b/python-avd/pyavd/_eos_designs/schema/__init__.py @@ -12868,7 +12868,15 @@ class Vrfs(AvdIndexedList[str, VrfsItem]): Vrfs._item_type = VrfsItem - _fields: ClassVar[dict] = {"sample": {"type": Sample}, "destinations": {"type": Destinations}, "vrfs": {"type": Vrfs}, "_custom_data": {"type": dict}} + _fields: ClassVar[dict] = { + "polling_interval": {"type": int}, + "sample": {"type": Sample}, + "destinations": {"type": Destinations}, + "vrfs": {"type": Vrfs}, + "_custom_data": {"type": dict}, + } + polling_interval: int | None + """Interval in seconds for sending counter data to the sFlow collector.""" sample: Sample """Subclass of AvdModel.""" destinations: Destinations @@ -12882,6 +12890,7 @@ class Vrfs(AvdIndexedList[str, VrfsItem]): def __init__( self, *, + polling_interval: int | None | UndefinedType = Undefined, sample: Sample | UndefinedType = Undefined, destinations: Destinations | UndefinedType = Undefined, vrfs: Vrfs | UndefinedType = Undefined, @@ -12894,6 +12903,7 @@ def __init__( Subclass of AvdModel. Args: + polling_interval: Interval in seconds for sending counter data to the sFlow collector. sample: Subclass of AvdModel. destinations: Subclass of AvdList with `DestinationsItem` items. vrfs: Subclass of AvdIndexedList with `VrfsItem` items. Primary key is `name` (`str`). 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 e3c1288dc12..6c466485b9d 100644 --- a/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/eos_designs.schema.yml @@ -3691,6 +3691,11 @@ keys: For default enabling of sFlow for various interface types across the fabric see `fabric_sflow`.' keys: + polling_interval: + type: int + convert_types: + - str + description: Interval in seconds for sending counter data to the sFlow collector. sample: type: dict keys: diff --git a/python-avd/pyavd/_eos_designs/schema/schema_fragments/sflow_settings.schema.yml b/python-avd/pyavd/_eos_designs/schema/schema_fragments/sflow_settings.schema.yml index 6718f7dedce..632eb4b0fec 100644 --- a/python-avd/pyavd/_eos_designs/schema/schema_fragments/sflow_settings.schema.yml +++ b/python-avd/pyavd/_eos_designs/schema/schema_fragments/sflow_settings.schema.yml @@ -15,6 +15,11 @@ keys: The sFlow process will only be configured if any interface is enabled for sFlow. For default enabling of sFlow for various interface types across the fabric see `fabric_sflow`. keys: + polling_interval: + type: int + convert_types: + - str + description: Interval in seconds for sending counter data to the sFlow collector. sample: type: dict keys: diff --git a/python-avd/pyavd/_eos_designs/structured_config/flows/__init__.py b/python-avd/pyavd/_eos_designs/structured_config/flows/__init__.py index fe4ae14546c..94e6661e710 100644 --- a/python-avd/pyavd/_eos_designs/structured_config/flows/__init__.py +++ b/python-avd/pyavd/_eos_designs/structured_config/flows/__init__.py @@ -44,7 +44,7 @@ def sflow(self) -> dict | None: # At this point we have at least one interface with sFlow enabled # and at least one destination. - sflow = {"run": True, "sample": self.inputs.sflow_settings.sample.rate} + sflow = {"run": True, "polling_interval": self.inputs.sflow_settings.polling_interval, "sample": self.inputs.sflow_settings.sample.rate} # Using a temporary dict for VRFs sflow_vrfs = {} From 84e4e40fe79e985dbfc298aca87eac70c0b0867a Mon Sep 17 00:00:00 2001 From: Mahesh Kumar <122076792+MaheshGSLAB@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:58:47 +0530 Subject: [PATCH 6/7] Feat(eos_cli_config_gen): Added support for IP locking enforcement disabled and address family IPv4/IPv6 (#4803) Co-authored-by: Mahesh Kumar Co-authored-by: Guillaume Mulocher --- .../documentation/devices/host1.md | 20 +++ .../intended/configs/host1.cfg | 20 +++ .../host_vars/host1/ethernet-interfaces.yml | 13 ++ .../inventory/host_vars/host1/vlans.yml | 6 + .../docs/tables/ethernet-interfaces.md | 24 ++- .../eos_cli_config_gen/docs/tables/vlans.md | 16 ++ .../j2templates/eos/ethernet-interfaces.j2 | 20 ++- .../j2templates/eos/vlans.j2 | 13 ++ .../_eos_cli_config_gen/schema/__init__.py | 156 +++++++++++++++++- .../schema/eos_cli_config_gen.schema.yml | 44 ++++- .../ethernet_interfaces.schema.yml | 24 ++- .../schema/schema_fragments/vlans.schema.yml | 15 ++ 12 files changed, 359 insertions(+), 12 deletions(-) diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md index 6692004d16f..d84487ab92c 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host1.md @@ -3184,10 +3184,17 @@ vlan internal order ascending range 10 40 ! vlan 110 name PR01-DMZ + ! + address locking + address-family ipv4 + address-family ipv6 ! vlan 111 name PRIVATE_VLAN_COMMUNITY private-vlan community primary vlan 110 + ! + address locking + locked-address ipv4 enforcement disabled ! vlan 112 name PRIVATE_VLAN_ISOLATED @@ -3948,6 +3955,10 @@ interface Ethernet4 mtu 9100 no switchport snmp trap link-change + ! + address locking + address-family ipv4 + address-family ipv6 ipv6 enable ipv6 address 2020::2020/64 ipv6 address FE80:FEA::AB65/64 link-local @@ -3971,6 +3982,10 @@ interface Ethernet5 mtu 9100 switchport access vlan 220 no switchport + ! + address locking + address-family ipv4 disabled + address-family ipv6 disabled ip ospf cost 99 ip ospf network point-to-point ip ospf authentication message-digest @@ -4001,6 +4016,11 @@ interface Ethernet6 switchport trunk allowed vlan 110-111,210-211 switchport mode trunk switchport + ! + address locking + address-family ipv6 + address-family ipv4 disabled + locked-address ipv4 enforcement disabled no lldp transmit ptp enable ptp announce interval 3 diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg index 76bc4a40826..e213b30b800 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host1.cfg @@ -1059,10 +1059,17 @@ clock timezone GMT ! vlan 110 name PR01-DMZ + ! + address locking + address-family ipv4 + address-family ipv6 ! vlan 111 name PRIVATE_VLAN_COMMUNITY private-vlan community primary vlan 110 + ! + address locking + locked-address ipv4 enforcement disabled ! vlan 112 name PRIVATE_VLAN_ISOLATED @@ -2390,6 +2397,10 @@ interface Ethernet4 mtu 9100 no switchport snmp trap link-change + ! + address locking + address-family ipv4 + address-family ipv6 ipv6 enable ipv6 address 2020::2020/64 ipv6 address FE80:FEA::AB65/64 link-local @@ -2413,6 +2424,10 @@ interface Ethernet5 mtu 9100 switchport access vlan 220 no switchport + ! + address locking + address-family ipv4 disabled + address-family ipv6 disabled ip ospf cost 99 ip ospf network point-to-point ip ospf authentication message-digest @@ -2443,6 +2458,11 @@ interface Ethernet6 switchport trunk allowed vlan 110-111,210-211 switchport mode trunk switchport + ! + address locking + address-family ipv6 + address-family ipv4 disabled + locked-address ipv4 enforcement disabled no lldp transmit ptp enable ptp announce interval 3 diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml index 900073e6f8e..ea22cb6f482 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/ethernet-interfaces.yml @@ -317,6 +317,10 @@ ethernet_interfaces: port_security: violation: mode: protect + address_locking: + address_family: + ipv4: true + ipv6: true - name: Ethernet5 description: Molecule Routing @@ -355,6 +359,10 @@ ethernet_interfaces: access_vlan: 220 sync_e: enable: true + address_locking: + address_family: + ipv4: false + ipv6: false - name: Ethernet6 logging: @@ -404,6 +412,11 @@ ethernet_interfaces: sync_e: enable: true priority: disabled + address_locking: + ipv4_enforcement_disabled: true + address_family: + ipv4: false + ipv6: true - name: Ethernet7 description: Molecule L2 diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/vlans.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/vlans.yml index eea2df9524d..c837a30ec64 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/vlans.yml +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host1/vlans.yml @@ -4,12 +4,18 @@ vlans: - id: 110 tenant: Tenant_A name: PR01-DMZ + address_locking: + address_family: + ipv4: true + ipv6: true - id: 111 tenant: Tenant_A name: PRIVATE_VLAN_COMMUNITY private_vlan: type: community primary_vlan: 110 + address_locking: + ipv4_enforcement_disabled: true - id: 112 tenant: Tenant_A name: PRIVATE_VLAN_ISOLATED diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md index e1c7ce9db76..d96bbd3941c 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/ethernet-interfaces.md @@ -32,8 +32,12 @@ | [    type](## "ethernet_interfaces.[].type") deprecated | String | | | Valid Values:
- routed
- switched
- l3dot1q
- l2dot1q
- port-channel-member | l3dot1q and l2dot1q are used for sub-interfaces. The parent interface should be defined as routed.
The `type = switched/routed` should not be combined with `switchport`.
This key is deprecated. Support will be removed in AVD version 6.0.0. See [here](https://avd.arista.com/5.x/docs/porting-guides/5.x.x.html#removal-of-type-key-dependency-for-rendering-ethernetport-channel-interfaces-configuration-and-documentation) for details. | | [    snmp_trap_link_change](## "ethernet_interfaces.[].snmp_trap_link_change") | Boolean | | | | | | [    address_locking](## "ethernet_interfaces.[].address_locking") | Dictionary | | | | | - | [      ipv4](## "ethernet_interfaces.[].address_locking.ipv4") | Boolean | | | | Enable address locking for IPv4. | - | [      ipv6](## "ethernet_interfaces.[].address_locking.ipv6") | Boolean | | | | Enable address locking for IPv6. | + | [      ipv4](## "ethernet_interfaces.[].address_locking.ipv4") | Boolean | | | | Enable address locking for IPv4.
For EOS version 4.31 and above, the `address_family.ipv4` parameter should be used instead. | + | [      ipv6](## "ethernet_interfaces.[].address_locking.ipv6") | Boolean | | | | Enable address locking for IPv6.
For EOS version 4.31 and above, the `address_family.ipv6` parameter should be used instead. | + | [      address_family](## "ethernet_interfaces.[].address_locking.address_family") | Dictionary | | | | Configure address locking per address family.
The `address_locking.ipv4/ipv6` and `address_locking.address_family.ipv4/ipv6` are mutually exclusive and `address_locking.address_family.ipv4/ipv6` take precedence.
Introduced in EOS 4.31.0F. | + | [        ipv4](## "ethernet_interfaces.[].address_locking.address_family.ipv4") | Boolean | | | | Enable/disable address locking for IPv4. | + | [        ipv6](## "ethernet_interfaces.[].address_locking.address_family.ipv6") | Boolean | | | | Enable/disable address locking for IPv6. | + | [      ipv4_enforcement_disabled](## "ethernet_interfaces.[].address_locking.ipv4_enforcement_disabled") | Boolean | | | | Disable enforcement for IPv4 locked addresses. | | [    flowcontrol](## "ethernet_interfaces.[].flowcontrol") | Dictionary | | | | | | [      received](## "ethernet_interfaces.[].flowcontrol.received") | String | | | Valid Values:
- desired
- on
- off | | | [    vrf](## "ethernet_interfaces.[].vrf") | String | | | | VRF name. | @@ -681,10 +685,26 @@ address_locking: # Enable address locking for IPv4. + # For EOS version 4.31 and above, the `address_family.ipv4` parameter should be used instead. ipv4: # Enable address locking for IPv6. + # For EOS version 4.31 and above, the `address_family.ipv6` parameter should be used instead. ipv6: + + # Configure address locking per address family. + # The `address_locking.ipv4/ipv6` and `address_locking.address_family.ipv4/ipv6` are mutually exclusive and `address_locking.address_family.ipv4/ipv6` take precedence. + # Introduced in EOS 4.31.0F. + address_family: + + # Enable/disable address locking for IPv4. + ipv4: + + # Enable/disable address locking for IPv6. + ipv6: + + # Disable enforcement for IPv4 locked addresses. + ipv4_enforcement_disabled: flowcontrol: received: diff --git a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/vlans.md b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/vlans.md index 75eecd73513..30bd239499a 100644 --- a/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/vlans.md +++ b/ansible_collections/arista/avd/roles/eos_cli_config_gen/docs/tables/vlans.md @@ -11,6 +11,11 @@ | [  - id](## "vlans.[].id") | Integer | Required, Unique | | | VLAN ID. | | [    name](## "vlans.[].name") | String | | | | VLAN Name. | | [    state](## "vlans.[].state") | String | | | Valid Values:
- active
- suspend | | + | [    address_locking](## "vlans.[].address_locking") | Dictionary | | | | | + | [      address_family](## "vlans.[].address_locking.address_family") | Dictionary | | | | | + | [        ipv4](## "vlans.[].address_locking.address_family.ipv4") | Boolean | | | | Enable address locking for IPv4. | + | [        ipv6](## "vlans.[].address_locking.address_family.ipv6") | Boolean | | | | Enable address locking for IPv6. | + | [      ipv4_enforcement_disabled](## "vlans.[].address_locking.ipv4_enforcement_disabled") | Boolean | | | | Disable enforcement for IPv4 locked addresses. | | [    trunk_groups](## "vlans.[].trunk_groups") | List, items: String | | | | | | [      - <str>](## "vlans.[].trunk_groups.[]") | String | | | | Trunk Group Name. | | [    private_vlan](## "vlans.[].private_vlan") | Dictionary | | | | | @@ -29,6 +34,17 @@ # VLAN Name. name: state: + address_locking: + address_family: + + # Enable address locking for IPv4. + ipv4: + + # Enable address locking for IPv6. + ipv6: + + # Disable enforcement for IPv4 locked addresses. + ipv4_enforcement_disabled: trunk_groups: # Trunk Group Name. diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 index 74b00b5fe65..ff9f96cd608 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/ethernet-interfaces.j2 @@ -358,7 +358,25 @@ interface {{ ethernet_interface.name }} {% elif ethernet_interface.snmp_trap_link_change is arista.avd.defined(true) %} snmp trap link-change {% endif %} -{% if ethernet_interface.address_locking.ipv4 is arista.avd.defined(true) or ethernet_interface.address_locking.ipv6 is arista.avd.defined(true) %} +{% if ethernet_interface.address_locking.address_family.ipv4 is arista.avd.defined or ethernet_interface.address_locking.address_family.ipv6 is arista.avd.defined or ethernet_interface.address_locking.ipv4_enforcement_disabled is arista.avd.defined(true) %} + ! + address locking +{% if ethernet_interface.address_locking.address_family.ipv4 is arista.avd.defined(true) %} + address-family ipv4 +{% endif %} +{% if ethernet_interface.address_locking.address_family.ipv6 is arista.avd.defined(true) %} + address-family ipv6 +{% endif %} +{% if ethernet_interface.address_locking.address_family.ipv4 is arista.avd.defined(false) %} + address-family ipv4 disabled +{% endif %} +{% if ethernet_interface.address_locking.address_family.ipv6 is arista.avd.defined(false) %} + address-family ipv6 disabled +{% endif %} +{% if ethernet_interface.address_locking.ipv4_enforcement_disabled is arista.avd.defined(true) %} + locked-address ipv4 enforcement disabled +{% endif %} +{% elif ethernet_interface.address_locking.ipv4 is arista.avd.defined(true) or ethernet_interface.address_locking.ipv6 is arista.avd.defined(true) %} {% set address_locking_cli = "address locking" %} {% if ethernet_interface.address_locking.ipv4 is arista.avd.defined(true) %} {% set address_locking_cli = address_locking_cli + " ipv4" %} diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/vlans.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/vlans.j2 index 53263987f1f..23f97939690 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/vlans.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/vlans.j2 @@ -20,4 +20,17 @@ vlan {{ vlan.id }} vlan.private_vlan.primary_vlan is arista.avd.defined %} private-vlan {{ vlan.private_vlan.type }} primary vlan {{ vlan.private_vlan.primary_vlan }} {% endif %} +{% if vlan.address_locking.address_family.ipv4 is arista.avd.defined or vlan.address_locking.address_family.ipv6 is arista.avd.defined or vlan.address_locking.ipv4_enforcement_disabled is arista.avd.defined(true) %} + ! + address locking +{% if vlan.address_locking.address_family.ipv4 is arista.avd.defined(true) %} + address-family ipv4 +{% endif %} +{% if vlan.address_locking.address_family.ipv6 is arista.avd.defined(true) %} + address-family ipv6 +{% endif %} +{% if vlan.address_locking.ipv4_enforcement_disabled is arista.avd.defined(true) %} + locked-address ipv4 enforcement disabled +{% endif %} +{% endif %} {% endfor %} diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py index adb4bba7394..f35545f25db 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/__init__.py @@ -5707,11 +5707,70 @@ class TrunkGroups(AvdList[str]): class AddressLocking(AvdModel): """Subclass of AvdModel.""" - _fields: ClassVar[dict] = {"ipv4": {"type": bool}, "ipv6": {"type": bool}, "_custom_data": {"type": dict}} + class AddressFamily(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"ipv4": {"type": bool}, "ipv6": {"type": bool}, "_custom_data": {"type": dict}} + ipv4: bool | None + """Enable/disable address locking for IPv4.""" + ipv6: bool | None + """Enable/disable address locking for IPv6.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + ipv4: bool | None | UndefinedType = Undefined, + ipv6: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + AddressFamily. + + + Subclass of AvdModel. + + Args: + ipv4: Enable/disable address locking for IPv4. + ipv6: Enable/disable address locking for IPv6. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = { + "ipv4": {"type": bool}, + "ipv6": {"type": bool}, + "address_family": {"type": AddressFamily}, + "ipv4_enforcement_disabled": {"type": bool}, + "_custom_data": {"type": dict}, + } ipv4: bool | None - """Enable address locking for IPv4.""" + """ + Enable address locking for IPv4. + For EOS version 4.31 and above, the `address_family.ipv4` parameter + should be used instead. + """ ipv6: bool | None - """Enable address locking for IPv6.""" + """ + Enable address locking for IPv6. + For EOS version 4.31 and above, the `address_family.ipv6` parameter + should be used instead. + """ + address_family: AddressFamily + """ + Configure address locking per address family. + The `address_locking.ipv4/ipv6` and + `address_locking.address_family.ipv4/ipv6` are mutually exclusive and + `address_locking.address_family.ipv4/ipv6` take precedence. + Introduced in EOS 4.31.0F. + + Subclass of + AvdModel. + """ + ipv4_enforcement_disabled: bool | None + """Disable enforcement for IPv4 locked addresses.""" _custom_data: dict[str, Any] if TYPE_CHECKING: @@ -5721,6 +5780,8 @@ def __init__( *, ipv4: bool | None | UndefinedType = Undefined, ipv6: bool | None | UndefinedType = Undefined, + address_family: AddressFamily | UndefinedType = Undefined, + ipv4_enforcement_disabled: bool | None | UndefinedType = Undefined, _custom_data: dict[str, Any] | UndefinedType = Undefined, ) -> None: """ @@ -5730,8 +5791,24 @@ def __init__( Subclass of AvdModel. Args: - ipv4: Enable address locking for IPv4. - ipv6: Enable address locking for IPv6. + ipv4: + Enable address locking for IPv4. + For EOS version 4.31 and above, the `address_family.ipv4` parameter + should be used instead. + ipv6: + Enable address locking for IPv6. + For EOS version 4.31 and above, the `address_family.ipv6` parameter + should be used instead. + address_family: + Configure address locking per address family. + The `address_locking.ipv4/ipv6` and + `address_locking.address_family.ipv4/ipv6` are mutually exclusive and + `address_locking.address_family.ipv4/ipv6` take precedence. + Introduced in EOS 4.31.0F. + + Subclass of + AvdModel. + ipv4_enforcement_disabled: Disable enforcement for IPv4 locked addresses. _custom_data: _custom_data """ @@ -69037,6 +69114,70 @@ def __init__( class VlansItem(AvdModel): """Subclass of AvdModel.""" + class AddressLocking(AvdModel): + """Subclass of AvdModel.""" + + class AddressFamily(AvdModel): + """Subclass of AvdModel.""" + + _fields: ClassVar[dict] = {"ipv4": {"type": bool}, "ipv6": {"type": bool}, "_custom_data": {"type": dict}} + ipv4: bool | None + """Enable address locking for IPv4.""" + ipv6: bool | None + """Enable address locking for IPv6.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + ipv4: bool | None | UndefinedType = Undefined, + ipv6: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + AddressFamily. + + + Subclass of AvdModel. + + Args: + ipv4: Enable address locking for IPv4. + ipv6: Enable address locking for IPv6. + _custom_data: _custom_data + + """ + + _fields: ClassVar[dict] = {"address_family": {"type": AddressFamily}, "ipv4_enforcement_disabled": {"type": bool}, "_custom_data": {"type": dict}} + address_family: AddressFamily + """Subclass of AvdModel.""" + ipv4_enforcement_disabled: bool | None + """Disable enforcement for IPv4 locked addresses.""" + _custom_data: dict[str, Any] + + if TYPE_CHECKING: + + def __init__( + self, + *, + address_family: AddressFamily | UndefinedType = Undefined, + ipv4_enforcement_disabled: bool | None | UndefinedType = Undefined, + _custom_data: dict[str, Any] | UndefinedType = Undefined, + ) -> None: + """ + AddressLocking. + + + Subclass of AvdModel. + + Args: + address_family: Subclass of AvdModel. + ipv4_enforcement_disabled: Disable enforcement for IPv4 locked addresses. + _custom_data: _custom_data + + """ + class TrunkGroups(AvdList[str]): """Subclass of AvdList with `str` items.""" @@ -69077,6 +69218,7 @@ def __init__( "id": {"type": int}, "name": {"type": str}, "state": {"type": str}, + "address_locking": {"type": AddressLocking}, "trunk_groups": {"type": TrunkGroups}, "private_vlan": {"type": PrivateVlan}, "tenant": {"type": str}, @@ -69087,6 +69229,8 @@ def __init__( name: str | None """VLAN Name.""" state: Literal["active", "suspend"] | None + address_locking: AddressLocking + """Subclass of AvdModel.""" trunk_groups: TrunkGroups """Subclass of AvdList with `str` items.""" private_vlan: PrivateVlan @@ -69103,6 +69247,7 @@ def __init__( id: int | UndefinedType = Undefined, name: str | None | UndefinedType = Undefined, state: Literal["active", "suspend"] | None | UndefinedType = Undefined, + address_locking: AddressLocking | UndefinedType = Undefined, trunk_groups: TrunkGroups | UndefinedType = Undefined, private_vlan: PrivateVlan | UndefinedType = Undefined, tenant: str | None | UndefinedType = Undefined, @@ -69118,6 +69263,7 @@ def __init__( id: VLAN ID. name: VLAN Name. state: state + address_locking: Subclass of AvdModel. trunk_groups: Subclass of AvdList with `str` items. private_vlan: Subclass of AvdModel. tenant: Key only used for documentation or validation purposes. diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml index 5d10316999b..90a7ac05177 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/eos_cli_config_gen.schema.yml @@ -2191,10 +2191,35 @@ keys: keys: ipv4: type: bool - description: Enable address locking for IPv4. + description: 'Enable address locking for IPv4. + + For EOS version 4.31 and above, the `address_family.ipv4` parameter + should be used instead.' ipv6: type: bool - description: Enable address locking for IPv6. + description: 'Enable address locking for IPv6. + + For EOS version 4.31 and above, the `address_family.ipv6` parameter + should be used instead.' + address_family: + description: 'Configure address locking per address family. + + The `address_locking.ipv4/ipv6` and `address_locking.address_family.ipv4/ipv6` + are mutually exclusive and `address_locking.address_family.ipv4/ipv6` + take precedence. + + Introduced in EOS 4.31.0F.' + type: dict + keys: + ipv4: + type: bool + description: Enable/disable address locking for IPv4. + ipv6: + type: bool + description: Enable/disable address locking for IPv6. + ipv4_enforcement_disabled: + type: bool + description: Disable enforcement for IPv4 locked addresses. flowcontrol: type: dict keys: @@ -22492,6 +22517,21 @@ keys: valid_values: - active - suspend + address_locking: + type: dict + keys: + address_family: + type: dict + keys: + ipv4: + type: bool + description: Enable address locking for IPv4. + ipv6: + type: bool + description: Enable address locking for IPv6. + ipv4_enforcement_disabled: + type: bool + description: Disable enforcement for IPv4 locked addresses. trunk_groups: type: list items: diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml index e9460279a5a..6eda1e0b8b5 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/ethernet_interfaces.schema.yml @@ -149,10 +149,30 @@ keys: keys: ipv4: type: bool - description: Enable address locking for IPv4. + description: |- + Enable address locking for IPv4. + For EOS version 4.31 and above, the `address_family.ipv4` parameter should be used instead. ipv6: type: bool - description: Enable address locking for IPv6. + description: |- + Enable address locking for IPv6. + For EOS version 4.31 and above, the `address_family.ipv6` parameter should be used instead. + address_family: + description: |- + Configure address locking per address family. + The `address_locking.ipv4/ipv6` and `address_locking.address_family.ipv4/ipv6` are mutually exclusive and `address_locking.address_family.ipv4/ipv6` take precedence. + Introduced in EOS 4.31.0F. + type: dict + keys: + ipv4: + type: bool + description: Enable/disable address locking for IPv4. + ipv6: + type: bool + description: Enable/disable address locking for IPv6. + ipv4_enforcement_disabled: + type: bool + description: Disable enforcement for IPv4 locked addresses. flowcontrol: type: dict keys: diff --git a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/vlans.schema.yml b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/vlans.schema.yml index b68b063ce7c..e6f805a574b 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/vlans.schema.yml +++ b/python-avd/pyavd/_eos_cli_config_gen/schema/schema_fragments/vlans.schema.yml @@ -23,6 +23,21 @@ keys: state: type: str valid_values: ["active", "suspend"] + address_locking: + type: dict + keys: + address_family: + type: dict + keys: + ipv4: + type: bool + description: Enable address locking for IPv4. + ipv6: + type: bool + description: Enable address locking for IPv6. + ipv4_enforcement_disabled: + type: bool + description: Disable enforcement for IPv4 locked addresses. trunk_groups: type: list items: From eba713c67e839c066040d127a88bdad3433f9cd8 Mon Sep 17 00:00:00 2001 From: laxmikantchintakindi <159624484+laxmikantchintakindi@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:11:50 +0530 Subject: [PATCH 7/7] Fix(eos_cli_config_gen): Fix wrong variable used in `eos\stun.j2` (#4814) Co-authored-by: Laxmikant Chintakindi Co-authored-by: Guillaume Mulocher --- .../documentation/devices/host2.md | 21 +++++++++++++++++++ .../intended/configs/host2.cfg | 5 +++++ .../inventory/host_vars/host2/stun.yml | 8 +++++++ .../j2templates/eos/stun.j2 | 2 +- 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host2/stun.yml diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host2.md b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host2.md index ca6b9942883..cfdfc2cb655 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host2.md +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/documentation/devices/host2.md @@ -81,6 +81,9 @@ - [IP NAT](#ip-nat) - [IP NAT Device Configuration](#ip-nat-device-configuration) - [Traffic Policies information](#traffic-policies-information) +- [STUN](#stun) + - [STUN Server](#stun-server) + - [STUN Device Configuration](#stun-device-configuration) ## Management @@ -1177,3 +1180,21 @@ traffic-policies ! field-set ipv6 prefix IPv6-DEMO-2 ``` + +## STUN + +### STUN Server + +| Server Local Interfaces | Bindings Timeout (s) | SSL Profile | SSL Connection Lifetime | Port | +| ----------------------- | -------------------- | ----------- | ----------------------- | ---- | +| Ethernet1 | - | - | 3 hours | 3478 | + +### STUN Device Configuration + +```eos +! +stun + server + local-interface Ethernet1 + ssl connection lifetime 3 hours +``` diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host2.cfg b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host2.cfg index 9d32990dd62..525b3f956eb 100644 --- a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host2.cfg +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/intended/configs/host2.cfg @@ -305,6 +305,11 @@ router pim sparse-mode ipv4 make-before-break ! +stun + server + local-interface Ethernet1 + ssl connection lifetime 3 hours +! traffic-policies field-set ipv6 prefix IPv6-DEMO-1 11:22:33:44:55:66:77:88 diff --git a/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host2/stun.yml b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host2/stun.yml new file mode 100644 index 00000000000..76d5d8aa75d --- /dev/null +++ b/ansible_collections/arista/avd/molecule/eos_cli_config_gen/inventory/host_vars/host2/stun.yml @@ -0,0 +1,8 @@ +--- +### stun +stun: + server: + local_interfaces: + - Ethernet1 + ssl_connection_lifetime: + hours: 3 diff --git a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/stun.j2 b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/stun.j2 index 4303568acef..20427f391bd 100644 --- a/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/stun.j2 +++ b/python-avd/pyavd/_eos_cli_config_gen/j2templates/eos/stun.j2 @@ -39,7 +39,7 @@ stun {% if stun.server.ssl_connection_lifetime.minutes is arista.avd.defined %} ssl connection lifetime {{ stun.server.ssl_connection_lifetime.minutes }} minutes {% elif stun.server.ssl_connection_lifetime.hours is arista.avd.defined %} - ssl connection lifetime {{ stun.segitrver.ssl_connection_lifetime.hours }} hours + ssl connection lifetime {{ stun.server.ssl_connection_lifetime.hours }} hours {% endif %} {% endif %} {% endif %}