From 4057b3e84e82b542a5b4ccbe0b5d1e48198ffe0b Mon Sep 17 00:00:00 2001 From: Jakub Krajewski <95274389+jpkrajewski@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:48:05 +0200 Subject: [PATCH] dev: fix bfd, lan-vpn, acl, traffic-data (#830) * Fix bfd, lan-vpn * fix acl: actions, base_action fields are mutually exclusive, update builder methods * traffic-policy: allow encap to be a list for 20.12 compatibility * bump dev version --------- Co-authored-by: Szymon Basan --- ENDPOINTS.md | 2 +- .../feature_profile/sdwan/acl/ipv4acl.py | 41 ++++++++++++------- .../feature_profile/sdwan/acl/ipv6acl.py | 39 +++++++++++------- .../application_priority/traffic_policy.py | 18 ++++++-- .../feature_profile/sdwan/service/lan/vpn.py | 2 +- .../feature_profile/sdwan/system/bfd.py | 2 +- pyproject.toml | 2 +- 7 files changed, 70 insertions(+), 36 deletions(-) diff --git a/ENDPOINTS.md b/ENDPOINTS.md index 7dc043d1..8f1819e6 100644 --- a/ENDPOINTS.md +++ b/ENDPOINTS.md @@ -1,6 +1,6 @@ **THIS FILE WAS AUTO-GENERATED DO NOT EDIT** -Generated for: catalystwan-0.33.4.dev0 +Generated for: catalystwan-0.35.4.dev4 All URIs are relative to */dataservice* HTTP request | Supported Versions | Method | Payload Type | Return Type | Tenancy Mode diff --git a/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv4acl.py b/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv4acl.py index bbdd49bc..701ca294 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv4acl.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv4acl.py @@ -3,10 +3,10 @@ from typing import List, Literal, Optional, Tuple, Union from uuid import UUID -from pydantic import AliasPath, BaseModel, ConfigDict, Field +from pydantic import AliasPath, BaseModel, ConfigDict, Field, model_validator from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_global, as_variable -from catalystwan.models.common import AcceptDropActionType +from catalystwan.models.common import AcceptDropActionType, check_fields_exclusive from catalystwan.models.configuration.feature_profile.common import RefIdItem IcmpIPv4Messages = Literal[ @@ -162,7 +162,7 @@ class DropAction(BaseModel): class Sequence(BaseModel): - model_config = ConfigDict(populate_by_name=True, validate_assignment=True) + model_config = ConfigDict(populate_by_name=True) actions: Optional[List[Union[AcceptAction, DropAction]]] = Field( default=None, description="Define list of actions", max_length=1, min_length=1 ) @@ -184,26 +184,33 @@ class Sequence(BaseModel): default=None, validation_alias="sequenceName", serialization_alias="sequenceName" ) - @property - def _action(self) -> Union[AcceptAction, DropAction]: + @model_validator(mode="after") + def check_base_action_xor_actions(self): + check_fields_exclusive(self.__dict__, {"base_action", "actions"}, True) + return self + + def _action(self, mode: Optional[AcceptDropActionType] = None) -> Union[AcceptAction, DropAction]: + if mode is None and self.base_action is not None: + _mode = self.base_action.value + else: + _mode = mode + self.base_action = None if self.actions is None: - if self.base_action is None: - self.base_action = Global[AcceptDropActionType](value="accept") - if self.base_action.value == "accept": - self.actions = [(AcceptAction(accept=Accept()))] - else: + if _mode == "drop": self.actions = [(DropAction(drop=Drop()))] + else: + self.actions = [(AcceptAction(accept=Accept()))] return self.actions[0] @property def _accept_action(self) -> Accept: - action = self._action + action = self._action("accept") assert isinstance(action, AcceptAction), "Sequence action must be set to accept" return action.accept @property def _drop_action(self) -> Drop: - action = self._action + action = self._action("drop") assert isinstance(action, DropAction), "Sequence action must be set to drop" return action.drop @@ -283,13 +290,13 @@ def match_tcp(self): self._entry.tcp = as_global("syn", Literal["syn"]) def associate_log_action(self): - if isinstance(self._action, DropAction): + if isinstance(self._action(), DropAction): self._drop_action.log = as_global(True) else: self._accept_action.log = as_global(True) def associate_counter_action(self, name: str): - if isinstance(self._action, DropAction): + if isinstance(self._action(), DropAction): self._drop_action.counter_name = as_global(name) else: self._accept_action.counter_name = as_global(name) @@ -321,8 +328,12 @@ def set_default_action(self, action: AcceptDropActionType): self.default_action = as_global(action, AcceptDropActionType) def add_sequence(self, name: str, id_: int, base_action: Optional[AcceptDropActionType] = None) -> Sequence: + if base_action is not None: + _base_action = as_global(base_action, AcceptDropActionType) + else: + _base_action = Default[Literal["accept"]](value="accept") seq = Sequence( - base_action=as_global(base_action, AcceptDropActionType) if base_action is not None else None, + base_action=_base_action, sequence_id=as_global(id_), sequence_name=as_global(name), ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv6acl.py b/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv6acl.py index c56da0ab..b674e24e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv6acl.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/acl/ipv6acl.py @@ -2,10 +2,10 @@ from typing import List, Literal, Optional, Tuple, Union from uuid import UUID -from pydantic import AliasPath, BaseModel, ConfigDict, Field +from pydantic import AliasPath, BaseModel, ConfigDict, Field, model_validator from catalystwan.api.configuration_groups.parcel import Default, Global, _ParcelBase, as_global -from catalystwan.models.common import AcceptDropActionType +from catalystwan.models.common import AcceptDropActionType, check_fields_exclusive from catalystwan.models.configuration.feature_profile.common import RefIdItem @@ -192,26 +192,33 @@ class Sequence(BaseModel): default=None, validation_alias="sequenceName", serialization_alias="sequenceName" ) - @property - def _action(self) -> Union[AcceptAction, DropAction]: + @model_validator(mode="after") + def check_base_action_xor_actions(self): + check_fields_exclusive(self.__dict__, {"base_action", "actions"}, True) + return self + + def _action(self, mode: Optional[AcceptDropActionType] = None) -> Union[AcceptAction, DropAction]: + if mode is None and self.base_action is not None: + _mode = self.base_action.value + else: + _mode = mode + self.base_action = None if self.actions is None: - if self.base_action is None: - self.base_action = Global[AcceptDropActionType](value="accept") - if self.base_action.value == "accept": - self.actions = [(AcceptAction(accept=Accept()))] - else: + if _mode == "drop": self.actions = [(DropAction(drop=Drop()))] + else: + self.actions = [(AcceptAction(accept=Accept()))] return self.actions[0] @property def _accept_action(self) -> Accept: - action = self._action + action = self._action("accept") assert isinstance(action, AcceptAction), "Sequence action must be set to accept" return action.accept @property def _drop_action(self) -> Drop: - action = self._action + action = self._action("drop") assert isinstance(action, DropAction), "Sequence action must be set to drop" return action.drop @@ -282,13 +289,13 @@ def match_traffic_class(self, classes: List[int]): self._entry.traffic_class = as_global(classes) def associate_log_action(self): - if isinstance(self._action, DropAction): + if isinstance(self._action(), DropAction): self._drop_action.log = as_global(True) else: self._accept_action.log = as_global(True) def associate_counter_action(self, name: str): - if isinstance(self._action, DropAction): + if isinstance(self._action(), DropAction): self._drop_action.counter_name = as_global(name) else: self._accept_action.counter_name = as_global(name) @@ -320,8 +327,12 @@ def set_default_action(self, action: AcceptDropActionType): self.default_action = as_global(action, AcceptDropActionType) def add_sequence(self, name: str, id_: int, base_action: Optional[AcceptDropActionType] = None) -> Sequence: + if base_action is not None: + _base_action = as_global(base_action, AcceptDropActionType) + else: + _base_action = Default[Literal["accept"]](value="accept") seq = Sequence( - base_action=as_global(base_action, AcceptDropActionType) if base_action is not None else None, + base_action=_base_action, sequence_id=as_global(id_), sequence_name=as_global(name), ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/application_priority/traffic_policy.py b/catalystwan/models/configuration/feature_profile/sdwan/application_priority/traffic_policy.py index 1057742c..fda009aa 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/application_priority/traffic_policy.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/application_priority/traffic_policy.py @@ -294,9 +294,15 @@ class SlaClass(BaseModel): class LocalTlocList(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="forbid") color: Global[List[TLOCColor]] = Field(default=None) - encap: Optional[Global[EncapType]] = Field(default=None) + encap: Optional[Union[Global[EncapType], Global[List[EncapType]]]] = Field( + default=None, description="encap is list <=20.12" + ) restrict: Optional[Global[bool]] = Field(default=None) + def make_encap_list(self): + if self.encap is not None and self.encap.value is not None and not isinstance(self.encap.value, List): + self.encap = Global[List[EncapType]](value=[self.encap.value]) + class PreferredRemoteColor(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="forbid") @@ -355,6 +361,10 @@ class SetLocalTlocList(BaseModel): default=None, validation_alias="localTlocList", serialization_alias="localTlocList" ) + def make_encap_list(self): + if self.local_tloc_list is not None: + self.local_tloc_list.make_encap_list() + class SetNextHop(BaseModel): model_config = ConfigDict(populate_by_name=True, extra="forbid") @@ -845,13 +855,15 @@ def associate_forwarding_class_action(self, fwclass_id: UUID) -> None: def associate_local_tloc_list_action( self, color: List[TLOCColor], encap: Optional[EncapType] = None, restrict: bool = False - ) -> None: + ) -> SetLocalTlocList: tloc_list = LocalTlocList( color=Global[List[TLOCColor]](value=color), encap=as_optional_global(encap, EncapType), restrict=as_global(restrict), ) - self._insert_action_in_set(SetLocalTlocList(local_tloc_list=tloc_list)) + set_local_tloc_list = SetLocalTlocList(local_tloc_list=tloc_list) + self._insert_action_in_set(set_local_tloc_list) + return set_local_tloc_list def associate_next_hop_action(self, ip: IPv4Address) -> None: self._insert_action_in_set(SetNextHop(next_hop=as_global(ip))) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/service/lan/vpn.py b/catalystwan/models/configuration/feature_profile/sdwan/service/lan/vpn.py index 95f970ca..b5bc144e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/service/lan/vpn.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/service/lan/vpn.py @@ -329,7 +329,7 @@ def validate_one_of_ip_route( class StaticRouteIPv6(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) - prefix: RoutePrefix + prefix: Union[Global[IPv6Interface], Variable] = Field() one_of_ip_route: Union[NextHopRouteIPv6Container, Null0IPv6, NAT, InterfaceRouteIPv6Container] = Field( serialization_alias="oneOfIpRoute", validation_alias="oneOfIpRoute" ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py b/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py index 7b1a532e..c51c8dd6 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py @@ -12,7 +12,7 @@ class Color(BaseModel): default=as_default(1000), validation_alias="helloInterval", serialization_alias="helloInterval" ) multiplier: Optional[Union[Global[int], Default[int]]] = as_default(7) - pmtu_discovery: Optional[Union[Global[bool], Default[int]]] = Field( + pmtu_discovery: Optional[Union[Global[bool], Default[bool]]] = Field( default=as_default(True), validation_alias="pmtuDiscovery", serialization_alias="pmtuDiscovery" ) dscp: Optional[Union[Global[int], Default[int]]] = as_default(48) diff --git a/pyproject.toml b/pyproject.toml index 47cd2dbb..ec6760af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "catalystwan" -version = "0.35.4dev3" +version = "0.35.4dev4" description = "Cisco Catalyst WAN SDK for Python" authors = ["kagorski "] readme = "README.md"