diff --git a/catalystwan/api/template_api.py b/catalystwan/api/template_api.py index 4fa20e0ea..bfc499517 100644 --- a/catalystwan/api/template_api.py +++ b/catalystwan/api/template_api.py @@ -382,7 +382,9 @@ def _edit_feature_template(self, template: FeatureTemplate, data: FeatureTemplat if self.is_created_by_generator(template): debug = False schema = self.get_feature_template_schema(template, debug) - payload = self.generate_feature_template_payload(template, schema, debug).model_dump(by_alias=True) + payload = self.generate_feature_template_payload(template, schema, debug).model_dump( + by_alias=True, mode="json" + ) else: payload = json.loads(template.generate_payload(self.session)) @@ -541,7 +543,7 @@ def create_by_generator(self, template: FeatureTemplate, debug: bool) -> str: payload = self.generate_feature_template_payload(template, schema, debug) endpoint = "/dataservice/template/feature" - response = self.session.post(endpoint, json=payload.model_dump(by_alias=True, exclude_none=True)) + response = self.session.post(endpoint, json=payload.model_dump(by_alias=True, exclude_none=True, mode="json")) return response.json()["templateId"] @@ -557,9 +559,9 @@ def generate_feature_template_payload( ) # type: ignore fr_template_fields = [FeatureTemplateField(**field) for field in schema["fields"]] # TODO - + json_dumped_template = template.model_dump(mode="json") # "name" - for i, field in enumerate(fr_template_fields): + for field in fr_template_fields: value = None priority_order = None # TODO How to discover Device specific variable @@ -575,16 +577,19 @@ def generate_feature_template_payload( ): priority_order = get_extra_field(field_value, "priority_order") # type: ignore value = getattr(template, field_name) + json_dumped_value = json_dumped_template.get(field_name) break if value is None: continue - # print(field.payload_scheme(value)) - payload.definition = merge(payload.definition, field.payload_scheme(value, priority_order=priority_order)) + payload.definition = merge( + payload.definition, + field.payload_scheme(value, json_dumped_value=json_dumped_value, priority_order=priority_order), + ) if debug: with open(f"payload_{template.type}.json", "w") as f: - f.write(json.dumps(payload.model_dump(by_alias=True), indent=4)) + f.write(json.dumps(payload.model_dump(by_alias=True, mode="json"), indent=4)) return payload diff --git a/catalystwan/api/templates/bool_str.py b/catalystwan/api/templates/bool_str.py new file mode 100644 index 000000000..da1c5092b --- /dev/null +++ b/catalystwan/api/templates/bool_str.py @@ -0,0 +1,4 @@ +from pydantic import PlainSerializer +from typing_extensions import Annotated + +BoolStr = Annotated[bool, PlainSerializer(lambda x: str(x).lower(), return_type=str, when_used="json-unless-none")] diff --git a/catalystwan/api/templates/feature_template.py b/catalystwan/api/templates/feature_template.py index 357f919eb..db990117c 100644 --- a/catalystwan/api/templates/feature_template.py +++ b/catalystwan/api/templates/feature_template.py @@ -29,7 +29,7 @@ def generate_payload(self, session: ManagerSession) -> str: undefined=DebugUndefined, ) template = env.get_template(self.payload_path.name) - output = template.render(self.model_dump()) + output = template.render(self.model_dump(mode="json")) ast = env.parse(output) if meta.find_undeclared_variables(ast): diff --git a/catalystwan/api/templates/feature_template_field.py b/catalystwan/api/templates/feature_template_field.py index f7f813b71..235db2f63 100644 --- a/catalystwan/api/templates/feature_template_field.py +++ b/catalystwan/api/templates/feature_template_field.py @@ -89,7 +89,7 @@ def data_path(self, output): # value must be JSON serializable, return JSON serializable dict def payload_scheme( - self, value: Any = None, help=None, current_path=None, priority_order=None, vip_type=None + self, value: Any = None, json_dumped_value: Any = None, priority_order=None, vip_type=None ) -> dict: output: dict = {} rel_output: dict = {} @@ -119,10 +119,9 @@ def nest_value_in_output(value: Any) -> dict: if self.children: children_output = [] for obj in value: # obj is User, atomic value. Loop every child + obj_json_dump = obj.model_dump(mode="json") child_payload: dict = {} for child in self.children: # Child in schema - if current_path is None: - current_path = [] obj: FeatureTemplate # type: ignore model_tuple = next( filter( @@ -137,14 +136,14 @@ def nest_value_in_output(value: Any) -> dict: ) model_field = model_tuple[1] obj_value = getattr(obj, model_tuple[0]) + obj_json_value = obj_json_dump.get(model_tuple[0]) po = get_extra_field(model_field, "priority_order") vip_type = get_extra_field(model_field, "vip_type") merge( child_payload, child.payload_scheme( obj_value, - help=output, - current_path=self.dataPath + [self.key], + json_dumped_value=obj_json_value, priority_order=po, vip_type=vip_type, ), @@ -154,7 +153,7 @@ def nest_value_in_output(value: Any) -> dict: children_output.append(child_payload) output["vipValue"] = children_output else: - output["vipValue"] = value + output["vipValue"] = json_dumped_value else: if value is None: return {} diff --git a/catalystwan/api/templates/models/cisco_bfd_model.py b/catalystwan/api/templates/models/cisco_bfd_model.py index 837871240..c79cf6425 100644 --- a/catalystwan/api/templates/models/cisco_bfd_model.py +++ b/catalystwan/api/templates/models/cisco_bfd_model.py @@ -2,10 +2,10 @@ from pathlib import Path from typing import ClassVar, List, Optional -from pydantic import ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel DEFAULT_BFD_COLOR_MULTIPLIER = 7 DEFAULT_BFD_DSCP = 48 @@ -39,18 +39,18 @@ class ColorType(str, Enum): PRIVATE6 = "private6" -class Color(ConvertBoolToStringModel): +class Color(BaseModel): color: ColorType hello_interval: Optional[int] = Field( DEFAULT_BFD_HELLO_INTERVAL, json_schema_extra={"vmanage_key": "hello-interval"} ) multiplier: Optional[int] = DEFAULT_BFD_COLOR_MULTIPLIER - pmtu_discovery: Optional[bool] = Field(True, json_schema_extra={"vmanage_key": "pmtu-discovery"}) + pmtu_discovery: Optional[BoolStr] = Field(default=True, json_schema_extra={"vmanage_key": "pmtu-discovery"}) dscp: Optional[int] = DEFAULT_BFD_DSCP model_config = ConfigDict(populate_by_name=True) -class CiscoBFDModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoBFDModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) multiplier: Optional[int] = Field(DEFAULT_BFD_MULTIPLIER, json_schema_extra={"data_path": ["app-route"]}) diff --git a/catalystwan/api/templates/models/cisco_bgp_model.py b/catalystwan/api/templates/models/cisco_bgp_model.py index 9f40b651a..9d3014a24 100644 --- a/catalystwan/api/templates/models/cisco_bgp_model.py +++ b/catalystwan/api/templates/models/cisco_bgp_model.py @@ -2,8 +2,9 @@ from pathlib import Path from typing import ClassVar, List, Optional -from pydantic import BaseModel, ConfigDict, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate @@ -42,16 +43,10 @@ class AddressFamilyType(str, Enum): class AggregateAddress(BaseModel): prefix: str - as_set: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "as-set"}) - summary_only: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "summary-only"}) + as_set: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "as-set"}) + summary_only: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "summary-only"}) model_config = ConfigDict(populate_by_name=True) - @field_validator("as_set", "summary_only") - @classmethod - def cast_to_str(cls, value): - if value is not None: - return str(value).lower() - class Ipv6AggregateAddress(BaseModel): prefix: str @@ -95,20 +90,14 @@ class AddressFamily(BaseModel): network: Optional[List[Network]] = None ipv6_network: Optional[List[Ipv6Network]] = Field(default=None, json_schema_extra={"vmanage_key": "ipv6-network"}) paths: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["maximum-paths"]}) - originate: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["default-information"]}) + originate: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["default-information"]}) policy_name: Optional[str] = Field( default=None, json_schema_extra={"data_path": ["table-map"], "vmanage_key": "name"} ) - filter: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["table-map"]}) + filter: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["table-map"]}) redistribute: Optional[List[Redistribute]] = None model_config = ConfigDict(populate_by_name=True) - @field_validator("originate", "filter") - @classmethod - def cast_to_str(cls, value): - if value is not None: - return str(value).lower() - class NeighborFamilyType(str, Enum): IPV4_UNICAST = "ipv4-unicast" @@ -144,21 +133,23 @@ class NeighborAddressFamily(BaseModel): class Neighbor(BaseModel): address: str description: Optional[str] = None - shutdown: Optional[bool] = None + shutdown: Optional[BoolStr] = None remote_as: int = Field(json_schema_extra={"vmanage_key": "remote-as"}) keepalive: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["timers"]}) holdtime: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["timers"]}) if_name: Optional[str] = Field( default=None, json_schema_extra={"data_path": ["update-source"], "vmanage_key": "if-name"} ) - next_hop_self: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "next-hop-self"}) - send_community: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "send-community"}) - send_ext_community: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "send-ext-community"}) + next_hop_self: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "next-hop-self"}) + send_community: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "send-community"}) + send_ext_community: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "send-ext-community"}) ebgp_multihop: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "ebgp-multihop"}) password: Optional[str] = None - send_label: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "send-label"}) - send_label_explicit: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "send-label-explicit"}) - as_override: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "as-override"}) + send_label: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "send-label"}) + send_label_explicit: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "send-label-explicit"} + ) + as_override: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "as-override"}) as_number: Optional[int] = Field( default=None, json_schema_extra={"data_path": ["allowas-in"], "vmanage_key": "as-number"} ) @@ -167,20 +158,6 @@ class Neighbor(BaseModel): ) model_config = ConfigDict(populate_by_name=True) - @field_validator( - "shutdown", - "next_hop_self", - "send_community", - "send_ext_community", - "send_label", - "send_label_explicit", - "as_override", - ) - @classmethod - def cast_to_str(cls, value): - if value is not None: - return str(value).lower() - class IPv6NeighborFamilyType(str, Enum): IPV6_UNICAST = "ipv6-unicast" @@ -203,21 +180,23 @@ class IPv6NeighborAddressFamily(BaseModel): class Ipv6Neighbor(BaseModel): address: str description: Optional[str] = None - shutdown: Optional[bool] = None + shutdown: Optional[BoolStr] = None remote_as: int = Field(default=None, json_schema_extra={"vmanage_key": "remote-as"}) keepalive: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["timers"]}) holdtime: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["timers"]}) if_name: Optional[str] = Field( default=None, json_schema_extra={"data_path": ["update-source"], "vmanage_key": "if-name"} ) - next_hop_self: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "next-hop-self"}) - send_community: Optional[bool] = Field(True, json_schema_extra={"vmanage_key": "send-community"}) - send_ext_community: Optional[bool] = Field(True, json_schema_extra={"vmanage_key": "send-ext-community"}) + next_hop_self: Optional[BoolStr] = Field(default=False, json_schema_extra={"vmanage_key": "next-hop-self"}) + send_community: Optional[BoolStr] = Field(default=True, json_schema_extra={"vmanage_key": "send-community"}) + send_ext_community: Optional[BoolStr] = Field(default=True, json_schema_extra={"vmanage_key": "send-ext-community"}) ebgp_multihop: Optional[int] = Field(1, json_schema_extra={"vmanage_key": "ebgp-multihop"}) password: Optional[str] = None - send_label: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "send-label"}) - send_label_explicit: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "send-label-explicit"}) - as_override: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "as-override"}) + send_label: Optional[BoolStr] = Field(default=False, json_schema_extra={"vmanage_key": "send-label"}) + send_label_explicit: Optional[BoolStr] = Field( + default=False, json_schema_extra={"vmanage_key": "send-label-explicit"} + ) + as_override: Optional[BoolStr] = Field(default=False, json_schema_extra={"vmanage_key": "as-override"}) as_number: Optional[int] = Field( default=None, json_schema_extra={"data_path": ["allowas-in"], "vmanage_key": "as-number"} ) @@ -226,26 +205,12 @@ class Ipv6Neighbor(BaseModel): ) model_config = ConfigDict(populate_by_name=True) - @field_validator( - "shutdown", - "next_hop_self", - "send_community", - "send_ext_community", - "send_label", - "send_label_explicit", - "as_override", - ) - @classmethod - def cast_to_str(cls, value): - if value is not None: - return str(value).lower() - class CiscoBGPModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) as_num: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["bgp"], "vmanage_key": "as-num"}) - shutdown: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["bgp"]}) + shutdown: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["bgp"]}) router_id: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["bgp"], "vmanage_key": "router-id"}) propagate_aspath: Optional[bool] = Field( default=None, json_schema_extra={"data_path": ["bgp"], "vmanage_key": "propagate-aspath"} @@ -270,14 +235,14 @@ class CiscoBGPModel(FeatureTemplate): always_compare: Optional[bool] = Field( default=None, json_schema_extra={"data_path": ["bgp", "best-path", "med"], "vmanage_key": "always-compare"} ) - deterministic: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["bgp", "best-path", "med"]}) - missing_as_worst: Optional[bool] = Field( + deterministic: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["bgp", "best-path", "med"]}) + missing_as_worst: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["bgp", "best-path", "med"], "vmanage_key": "missing-as-worst"} ) - compare_router_id: Optional[bool] = Field( + compare_router_id: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["bgp", "best-path"], "vmanage_key": "compare-router-id"} ) - multipath_relax: Optional[bool] = Field( + multipath_relax: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["bgp", "best-path", "as-path"], "vmanage_key": "multipath-relax"} ) address_family: Optional[List[AddressFamily]] = Field( @@ -290,9 +255,3 @@ class CiscoBGPModel(FeatureTemplate): payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_bgp" - - @field_validator("shutdown", "deterministic", "missing_as_worst", "compare_router_id", "multipath_relax") - @classmethod - def cast_to_str(cls, value): - if value is not None: - return str(value).lower() diff --git a/catalystwan/api/templates/models/cisco_logging_model.py b/catalystwan/api/templates/models/cisco_logging_model.py index 2fc95acde..65612e4e4 100644 --- a/catalystwan/api/templates/models/cisco_logging_model.py +++ b/catalystwan/api/templates/models/cisco_logging_model.py @@ -2,10 +2,10 @@ from pathlib import Path from typing import ClassVar, List, Optional -from pydantic import ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel class Version(str, Enum): @@ -18,7 +18,7 @@ class AuthType(str, Enum): MUTUAL = "Mutual" -class TlsProfile(ConvertBoolToStringModel): +class TlsProfile(BaseModel): profile: str version: Optional[Version] = Field(Version.TLSV11, json_schema_extra={"data_path": ["tls-version"]}) auth_type: AuthType = Field(json_schema_extra={"vmanage_key": "auth-type"}) @@ -39,36 +39,40 @@ class Priority(str, Enum): EMERGENCY = "emergency" -class Server(ConvertBoolToStringModel): +class Server(BaseModel): name: str vpn: Optional[int] = None source_interface: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "source-interface"}) priority: Optional[Priority] = Priority.INFORMATION - enable_tls: Optional[bool] = Field(False, json_schema_extra={"data_path": ["tls"], "vmanage_key": "enable-tls"}) - custom_profile: Optional[bool] = Field( - False, json_schema_extra={"data_path": ["tls", "tls-properties"], "vmanage_key": "custom-profile"} + enable_tls: Optional[BoolStr] = Field( + default=False, json_schema_extra={"data_path": ["tls"], "vmanage_key": "enable-tls"} + ) + custom_profile: Optional[BoolStr] = Field( + default=False, json_schema_extra={"data_path": ["tls", "tls-properties"], "vmanage_key": "custom-profile"} ) profile: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["tls", "tls-properties"]}) model_config = ConfigDict(populate_by_name=True) -class Ipv6Server(ConvertBoolToStringModel): +class Ipv6Server(BaseModel): name: str vpn: Optional[int] = None source_interface: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "source-interface"}) priority: Optional[Priority] = Priority.INFORMATION - enable_tls: Optional[bool] = Field(False, json_schema_extra={"data_path": ["tls"], "vmanage_key": "enable-tls"}) - custom_profile: Optional[bool] = Field( - False, json_schema_extra={"data_path": ["tls", "tls-properties"], "vmanage_key": "custom-profile"} + enable_tls: Optional[BoolStr] = Field( + default=False, json_schema_extra={"data_path": ["tls"], "vmanage_key": "enable-tls"} + ) + custom_profile: Optional[BoolStr] = Field( + default=False, json_schema_extra={"data_path": ["tls", "tls-properties"], "vmanage_key": "custom-profile"} ) profile: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["tls", "tls-properties"]}) model_config = ConfigDict(populate_by_name=True) -class CiscoLoggingModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoLoggingModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) - enable: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["disk"]}) + enable: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["disk"]}) size: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["disk", "file"]}) rotate: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["disk", "file"]}) tls_profile: Optional[List[TlsProfile]] = Field(default=None, json_schema_extra={"vmanage_key": "tls-profile"}) diff --git a/catalystwan/api/templates/models/cisco_ntp_model.py b/catalystwan/api/templates/models/cisco_ntp_model.py index d26605691..f98cae3ec 100644 --- a/catalystwan/api/templates/models/cisco_ntp_model.py +++ b/catalystwan/api/templates/models/cisco_ntp_model.py @@ -3,11 +3,11 @@ from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel -class Server(ConvertBoolToStringModel): +class Server(BaseModel): model_config = ConfigDict(populate_by_name=True) name: str @@ -15,7 +15,7 @@ class Server(ConvertBoolToStringModel): vpn: Optional[int] = None version: Optional[int] = None source_interface: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "source-interface"}) - prefer: Optional[bool] = None + prefer: Optional[BoolStr] = None class Authentication(BaseModel): @@ -25,13 +25,13 @@ class Authentication(BaseModel): md5: str -class CiscoNTPModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoNTPModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) server: List[Server] = Field(default=[]) authentication: Optional[List[Authentication]] = Field(default=None, json_schema_extra={"data_path": ["keys"]}) trusted: Optional[List[int]] = Field(default=None, json_schema_extra={"data_path": ["keys"]}) - enable: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["master"]}) + enable: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["master"]}) stratum: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["master"]}) source: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["master"]}) diff --git a/catalystwan/api/templates/models/cisco_omp_model.py b/catalystwan/api/templates/models/cisco_omp_model.py index 5f1cbb719..458661716 100644 --- a/catalystwan/api/templates/models/cisco_omp_model.py +++ b/catalystwan/api/templates/models/cisco_omp_model.py @@ -4,8 +4,8 @@ from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel DEFAULT_OMP_HOLDTIME = 60 DEFAULT_OMP_EOR_TIMER = 300 @@ -64,16 +64,16 @@ class SiteTypes(str, Enum): SPOKE = "spoke" -class CiscoOMPModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoOMPModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) - graceful_restart: Optional[bool] = Field(True, json_schema_extra={"vmanage_key": "graceful-restart"}) + graceful_restart: Optional[BoolStr] = Field(default=True, json_schema_extra={"vmanage_key": "graceful-restart"}) overlay_as: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "overlay-as"}) send_path_limit: Optional[int] = Field( DEFAULT_OMP_SENDPATH_LIMIT, json_schema_extra={"vmanage_key": "send-path-limit"} ) ecmp_limit: Optional[int] = Field(DEFAULT_OMP_ECMP_LIMIT, json_schema_extra={"vmanage_key": "ecmp-limit"}) - shutdown: Optional[bool] + shutdown: Optional[BoolStr] = None omp_admin_distance_ipv4: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "omp-admin-distance-ipv4"} ) @@ -96,14 +96,14 @@ class CiscoOMPModel(FeatureTemplate, ConvertBoolToStringModel): ipv6_advertise: Optional[List[IPv6Advertise]] = Field( default=None, json_schema_extra={"vmanage_key": "ipv6-advertise"} ) - ignore_region_path_length: Optional[bool] = Field( - False, json_schema_extra={"vmanage_key": "ignore-region-path-length"} + ignore_region_path_length: Optional[BoolStr] = Field( + default=False, json_schema_extra={"vmanage_key": "ignore-region-path-length"} ) transport_gateway: Optional[TransportGateway] = Field( default=None, json_schema_extra={"vmanage_key": "transport-gateway"} ) site_types: Optional[List[SiteTypes]] = Field(default=None, json_schema_extra={"vmanage_key": "site-types"}) - auto_translate: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "auto-translate"}) + auto_translate: Optional[BoolStr] = Field(default=False, json_schema_extra={"vmanage_key": "auto-translate"}) payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_omp" diff --git a/catalystwan/api/templates/models/cisco_ospf.py b/catalystwan/api/templates/models/cisco_ospf.py index d20f3ad47..ac4494781 100644 --- a/catalystwan/api/templates/models/cisco_ospf.py +++ b/catalystwan/api/templates/models/cisco_ospf.py @@ -3,10 +3,10 @@ from pathlib import Path from typing import ClassVar, List, Optional -from pydantic import ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel, ConvertIPToStringModel DEFAULT_OSPF_HELLO_INTERVAL = 10 DEFAULT_OSPF_DEAD_INTERVAL = 40 @@ -35,10 +35,10 @@ class Protocol(str, Enum): EIGRP = "eigrp" -class Redistribute(ConvertBoolToStringModel): +class Redistribute(BaseModel): protocol: Protocol route_policy: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "route-policy"}) - dia: Optional[bool] = True + dia: Optional[BoolStr] = True model_config = ConfigDict(populate_by_name=True) @@ -47,7 +47,7 @@ class AdType(str, Enum): ON_STARTUP = "on-startup" -class RouterLsa(ConvertBoolToStringModel): +class RouterLsa(BaseModel): ad_type: AdType = Field(json_schema_extra={"vmanage_key": "ad-type"}) time: int model_config = ConfigDict(populate_by_name=True) @@ -57,7 +57,7 @@ class Direction(str, Enum): IN = "in" -class RoutePolicy(ConvertBoolToStringModel): +class RoutePolicy(BaseModel): direction: Direction pol_name: str = Field(json_schema_extra={"vmanage_key": "pol-name"}) model_config = ConfigDict(populate_by_name=True) @@ -76,7 +76,7 @@ class Type(str, Enum): NULL = "null" -class Interface(ConvertBoolToStringModel): +class Interface(BaseModel): name: str hello_interval: Optional[int] = Field( DEFAULT_OSPF_DEAD_INTERVAL, json_schema_extra={"vmanage_key": "hello-interval"} @@ -88,7 +88,7 @@ class Interface(ConvertBoolToStringModel): cost: Optional[int] = None priority: Optional[int] = DEFAULT_OSPF_INTERFACE_PRIORITY network: Optional[Network] = Network.BROADCAST - passive_interface: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "passive-interface"}) + passive_interface: Optional[BoolStr] = Field(default=False, json_schema_extra={"vmanage_key": "passive-interface"}) type: Optional[Type] = Field(default=None, json_schema_extra={"data_path": ["authentication"]}) message_digest_key: Optional[int] = Field( default=None, @@ -98,23 +98,27 @@ class Interface(ConvertBoolToStringModel): model_config = ConfigDict(populate_by_name=True) -class Range(ConvertBoolToStringModel, ConvertIPToStringModel): +class Range(BaseModel): address: ipaddress.IPv4Interface cost: Optional[int] = None - no_advertise: Optional[bool] = Field(False, json_schema_extra={"vmanage_key": "no-advertise"}) + no_advertise: Optional[BoolStr] = Field(default=False, json_schema_extra={"vmanage_key": "no-advertise"}) model_config = ConfigDict(populate_by_name=True) -class Area(ConvertBoolToStringModel): +class Area(BaseModel): a_num: int = Field(json_schema_extra={"vmanage_key": "a-num"}) - stub: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["stub"]}) - nssa: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["nssa"]}) + stub: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["stub"]} + ) + nssa: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["nssa"]} + ) interface: Optional[List[Interface]] = None range: Optional[List[Range]] = None model_config = ConfigDict(populate_by_name=True) -class CiscoOSPFModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoOSPFModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) router_id: Optional[str] = Field( @@ -124,9 +128,9 @@ class CiscoOSPFModel(FeatureTemplate, ConvertBoolToStringModel): DEFAULT_OSPF_REFERENCE_BANDWIDTH, json_schema_extra={"data_path": ["ospf", "auto-cost"], "vmanage_key": "reference-bandwidth"}, ) - rfc1583: Optional[bool] = Field(True, json_schema_extra={"data_path": ["ospf", "compatible"]}) - originate: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["ospf", "default-information"]}) - always: Optional[bool] = Field( + rfc1583: Optional[BoolStr] = Field(default=True, json_schema_extra={"data_path": ["ospf", "compatible"]}) + originate: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["ospf", "default-information"]}) + always: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["ospf", "default-information", "originate"]} ) metric: Optional[int] = Field( diff --git a/catalystwan/api/templates/models/cisco_ospfv3.py b/catalystwan/api/templates/models/cisco_ospfv3.py index 14b515dbc..b3671231c 100644 --- a/catalystwan/api/templates/models/cisco_ospfv3.py +++ b/catalystwan/api/templates/models/cisco_ospfv3.py @@ -3,11 +3,10 @@ from pathlib import Path from typing import ClassVar, List, Optional -from pydantic import ConfigDict -from pydantic.v1 import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel, ConvertIPToStringModel class MetricType(str, Enum): @@ -26,10 +25,10 @@ class Protocol(str, Enum): STATIC = "static" -class Redistribute(ConvertBoolToStringModel): +class Redistribute(BaseModel): protocol: Protocol route_policy: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "route-policy"}) - dia: Optional[bool] = True + dia: Optional[BoolStr] = True model_config = ConfigDict(populate_by_name=True) @@ -82,12 +81,16 @@ class Range(BaseModel): model_config = ConfigDict(populate_by_name=True) -class Area(ConvertBoolToStringModel): +class Area(BaseModel): a_num: int = Field(json_schema_extra={"vmanage_key": "a-num"}) - stub: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["stub"]}) - nssa: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["nssa"]}) + stub: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["stub"]} + ) + nssa: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["nssa"]} + ) translate: Optional[Translate] = Field(default=None, json_schema_extra={"data_path": ["nssa"]}) - normal: Optional[bool] = None + normal: Optional[BoolStr] = None interface: Optional[List[Interface]] = None range: Optional[List[Range]] = None model_config = ConfigDict(populate_by_name=True) @@ -122,18 +125,22 @@ class RangeV6(BaseModel): model_config = ConfigDict(populate_by_name=True) -class AreaV6(ConvertBoolToStringModel): +class AreaV6(BaseModel): a_num: int = Field(json_schema_extra={"vmanage_key": "a-num"}) - stub: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["stub"]}) - nssa: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["nssa"]}) + stub: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["stub"]} + ) + nssa: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "no-summary", "data_path": ["nssa"]} + ) translate: Optional[Translate] = Field(default=None, json_schema_extra={"data_path": ["nssa"]}) - normal: Optional[bool] = None + normal: Optional[BoolStr] = None interface: Optional[List[InterfaceV6]] = None range: Optional[List[RangeV6]] = None model_config = ConfigDict(populate_by_name=True) -class CiscoOspfv3Model(FeatureTemplate, ConvertIPToStringModel, ConvertBoolToStringModel): +class CiscoOspfv3Model(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) router_id_v4: Optional[ipaddress.IPv4Address] = Field( @@ -146,18 +153,18 @@ class CiscoOspfv3Model(FeatureTemplate, ConvertIPToStringModel, ConvertBoolToStr "data_path": ["ospfv3", "address-family", "ipv4", "auto-cost"], }, ) - rfc1583_v4: Optional[bool] = Field( - True, + rfc1583_v4: Optional[BoolStr] = Field( + default=True, json_schema_extra={"vmanage_key": "rfc1583", "data_path": ["ospfv3", "address-family", "ipv4", "compatible"]}, ) - originate_v4: Optional[bool] = Field( + originate_v4: Optional[BoolStr] = Field( default=None, json_schema_extra={ "vmanage_key": "originate", "data_path": ["ospfv3", "address-family", "ipv4", "default-information"], }, ) - always_v4: Optional[bool] = Field( + always_v4: Optional[BoolStr] = Field( default=None, json_schema_extra={ "vmanage_key": "always", @@ -231,7 +238,7 @@ class CiscoOspfv3Model(FeatureTemplate, ConvertIPToStringModel, ConvertBoolToStr default=None, json_schema_extra={"vmanage_key": "name", "data_path": ["ospfv3", "address-family", "ipv4", "table-map"]}, ) - filter_v4: Optional[bool] = Field( + filter_v4: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "filter", "data_path": ["ospfv3", "address-family", "ipv4", "table-map"]}, ) @@ -259,18 +266,18 @@ class CiscoOspfv3Model(FeatureTemplate, ConvertIPToStringModel, ConvertBoolToStr "data_path": ["ospfv3", "address-family", "ipv6", "auto-cost"], }, ) - rfc1583_v6: Optional[bool] = Field( - True, + rfc1583_v6: Optional[BoolStr] = Field( + default=True, json_schema_extra={"vmanage_key": "rfc1583", "data_path": ["ospfv3", "address-family", "ipv6", "compatible"]}, ) - originate_v6: Optional[bool] = Field( + originate_v6: Optional[BoolStr] = Field( default=None, json_schema_extra={ "vmanage_key": "originate", "data_path": ["ospfv3", "address-family", "ipv6", "default-information"], }, ) - always_v6: Optional[bool] = Field( + always_v6: Optional[BoolStr] = Field( default=None, json_schema_extra={ "vmanage_key": "always", @@ -344,7 +351,7 @@ class CiscoOspfv3Model(FeatureTemplate, ConvertIPToStringModel, ConvertBoolToStr default=None, json_schema_extra={"vmanage_key": "name", "data_path": ["ospfv3", "address-family", "ipv6", "table-map"]}, ) - filter_v6: Optional[bool] = Field( + filter_v6: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "filter", "data_path": ["ospfv3", "address-family", "ipv6", "table-map"]}, ) diff --git a/catalystwan/api/templates/models/cisco_secure_internet_gateway.py b/catalystwan/api/templates/models/cisco_secure_internet_gateway.py index 6eb7ac0c1..859f28a97 100644 --- a/catalystwan/api/templates/models/cisco_secure_internet_gateway.py +++ b/catalystwan/api/templates/models/cisco_secure_internet_gateway.py @@ -6,7 +6,6 @@ from pydantic import BaseModel, ConfigDict, Field from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertIPToStringModel DEFAULT_TRACKER_THRESHOLD = 300 DEFAULT_TRACKER_INTERVAL = 60 @@ -73,7 +72,7 @@ class PerfectForwardSecrecy(str, Enum): NONE = "none" -class Interface(ConvertIPToStringModel): +class Interface(BaseModel): if_name: str = Field(json_schema_extra={"vmanage_key": "if-name"}) auto: bool shutdown: bool @@ -201,7 +200,7 @@ class Tracker(BaseModel): model_config = ConfigDict(populate_by_name=True) -class CiscoSecureInternetGatewayModel(FeatureTemplate, ConvertIPToStringModel): +class CiscoSecureInternetGatewayModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) vpn_id: int = Field(DEFAULT_SIG_VPN_ID, json_schema_extra={"vmanage_key": "vpn-id"}) diff --git a/catalystwan/api/templates/models/cisco_snmp_model.py b/catalystwan/api/templates/models/cisco_snmp_model.py index 743c07a50..663f04acc 100644 --- a/catalystwan/api/templates/models/cisco_snmp_model.py +++ b/catalystwan/api/templates/models/cisco_snmp_model.py @@ -4,13 +4,13 @@ from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel -class Oid(ConvertBoolToStringModel): +class Oid(BaseModel): id: str - exclude: Optional[bool] = None + exclude: Optional[BoolStr] = None class View(BaseModel): @@ -70,10 +70,10 @@ class Target(BaseModel): model_config = ConfigDict(populate_by_name=True) -class CiscoSNMPModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoSNMPModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) - shutdown: Optional[bool] = True + shutdown: Optional[BoolStr] = True contact: Optional[str] = None location: Optional[str] = None view: Optional[List[View]] = None diff --git a/catalystwan/api/templates/models/cisco_system.py b/catalystwan/api/templates/models/cisco_system.py index eaebe5f25..8c0d36fc6 100644 --- a/catalystwan/api/templates/models/cisco_system.py +++ b/catalystwan/api/templates/models/cisco_system.py @@ -4,9 +4,9 @@ from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.device_variable import DeviceVariable from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel from catalystwan.utils.timezone import Timezone @@ -118,7 +118,7 @@ class Epfr(str, Enum): CONSERVATIVE = "conservative" -class CiscoSystemModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoSystemModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) timezone: Optional[Timezone] = Field(default=None, json_schema_extra={"data_path": ["clock"]}) @@ -131,15 +131,15 @@ class CiscoSystemModel(FeatureTemplate, ConvertBoolToStringModel): latitude: Optional[float] = Field(default=None, json_schema_extra={"data_path": ["gps-location"]}) longitude: Optional[float] = Field(default=None, json_schema_extra={"data_path": ["gps-location"]}) range: Optional[int] = Field(100, json_schema_extra={"data_path": ["gps-location", "geo-fencing"]}) - enable_fencing: Optional[bool] = Field( + enable_fencing: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["gps-location", "geo-fencing"], "vmanage_key": "enable"} ) mobile_number: Optional[List[MobileNumber]] = Field( default=None, json_schema_extra={"vmanage_key": "mobile-number", "data_path": ["gps-location", "geo-fencing", "sms"]}, ) - enable_sms: Optional[bool] = Field( - False, json_schema_extra={"data_path": ["gps-location", "geo-fencing", "sms"], "vmanage_key": "enable"} + enable_sms: Optional[BoolStr] = Field( + default=False, json_schema_extra={"data_path": ["gps-location", "geo-fencing", "sms"], "vmanage_key": "enable"} ) device_groups: Optional[List[str]] = Field(default=None, json_schema_extra={"vmanage_key": "device-groups"}) controller_group_list: Optional[List[int]] = Field( @@ -152,22 +152,22 @@ class CiscoSystemModel(FeatureTemplate, ConvertBoolToStringModel): site_id: int = Field(default=DeviceVariable(name="system_site_id"), json_schema_extra={"vmanage_key": "site-id"}) site_type: Optional[List[SiteType]] = Field(default=None, json_schema_extra={"vmanage_key": "site-type"}) port_offset: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "port-offset"}) - port_hop: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "port-hop"}) + port_hop: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "port-hop"}) control_session_pps: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "control-session-pps"}) - track_transport: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "track-transport"}) + track_transport: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "track-transport"}) track_interface_tag: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "track-interface-tag"}) console_baud_rate: Optional[ConsoleBaudRate] = Field( default=None, json_schema_extra={"vmanage_key": "console-baud-rate"} ) max_omp_sessions: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "max-omp-sessions"}) - multi_tenant: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "multi-tenant"}) - track_default_gateway: Optional[bool] = Field( + multi_tenant: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "multi-tenant"}) + track_default_gateway: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "track-default-gateway"} ) - admin_tech_on_failure: Optional[bool] = Field( + admin_tech_on_failure: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "admin-tech-on-failure"} ) - enable_tunnel: Optional[bool] = Field( + enable_tunnel: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "enable", "data_path": ["on-demand"]} ) idle_timeout: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "idle-timeout"}) @@ -183,22 +183,22 @@ class CiscoSystemModel(FeatureTemplate, ConvertBoolToStringModel): default=None, json_schema_extra={"vmanage_key": "affinity-group-number", "data_path": ["affinity-group"]} ) preference: Optional[List[int]] = Field(default=None, json_schema_extra={"data_path": ["affinity-group"]}) - preference_auto: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "preference-auto"}) + preference_auto: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "preference-auto"}) affinity_per_vrf: Optional[List[AffinityPerVrf]] = Field( default=None, json_schema_extra={"vmanage_key": "affinity-per-vrf"} ) - transport_gateway: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "transport-gateway"}) + transport_gateway: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "transport-gateway"}) enable_mrf_migration: Optional[EnableMrfMigration] = Field( default=None, json_schema_extra={"vmanage_key": "enable-mrf-migration"} ) migration_bgp_community: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "migration-bgp-community"} ) - enable_management_region: Optional[bool] = Field( + enable_management_region: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "enable-management-region"} ) vrf: Optional[List[Vrf]] = None - management_gateway: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "management-gateway"}) + management_gateway: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "management-gateway"}) epfr: Optional[Epfr] = None payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" diff --git a/catalystwan/api/templates/models/cisco_vpn_interface_model.py b/catalystwan/api/templates/models/cisco_vpn_interface_model.py index 93dc208c4..80719dd82 100644 --- a/catalystwan/api/templates/models/cisco_vpn_interface_model.py +++ b/catalystwan/api/templates/models/cisco_vpn_interface_model.py @@ -3,10 +3,10 @@ from pathlib import Path from typing import ClassVar, List, Optional -from pydantic import ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel, ConvertIPToStringModel DEFAULT_STATIC_NAT64_SOURCE_VPN_ID = 0 DEFAULT_STATIC_NAT_SOURCE_VPN_ID = 0 @@ -20,11 +20,11 @@ DEFAULT_IPV6_VRRP_TIMER = 1000 -class SecondaryIPv4Address(ConvertBoolToStringModel, ConvertIPToStringModel): +class SecondaryIPv4Address(BaseModel): address: Optional[ipaddress.IPv4Interface] = None -class SecondaryIPv6Address(ConvertBoolToStringModel, ConvertIPToStringModel): +class SecondaryIPv6Address(BaseModel): address: Optional[ipaddress.IPv6Interface] = None @@ -33,13 +33,13 @@ class Direction(str, Enum): OUT = "out" -class AccessList(ConvertBoolToStringModel): +class AccessList(BaseModel): direction: Direction acl_name: str = Field(json_schema_extra={"vmanage_key": "acl-name"}) model_config = ConfigDict(populate_by_name=True) -class DhcpHelperV6(ConvertBoolToStringModel, ConvertIPToStringModel): +class DhcpHelperV6(BaseModel): address: ipaddress.IPv6Address vpn: Optional[int] = None @@ -50,7 +50,7 @@ class NatChoice(str, Enum): LOOPBACK = "Loopback" -class StaticNat66(ConvertBoolToStringModel, ConvertIPToStringModel): +class StaticNat66(BaseModel): source_prefix: ipaddress.IPv6Interface = Field(json_schema_extra={"vmanage_key": "source-prefix"}) translated_source_prefix: str = Field(json_schema_extra={"vmanage_key": "translated-source-prefix"}) source_vpn_id: int = Field(DEFAULT_STATIC_NAT64_SOURCE_VPN_ID, json_schema_extra={"vmanage_key": "source-vpn-id"}) @@ -62,7 +62,7 @@ class StaticNatDirection(str, Enum): OUTSIDE = "outside" -class Static(ConvertBoolToStringModel, ConvertIPToStringModel): +class Static(BaseModel): source_ip: ipaddress.IPv4Address = Field(json_schema_extra={"vmanage_key": "source-ip"}) translate_ip: ipaddress.IPv4Address = Field(json_schema_extra={"vmanage_key": "translate-ip"}) static_nat_direction: StaticNatDirection = Field( @@ -77,7 +77,7 @@ class Proto(str, Enum): UDP = "udp" -class StaticPortForward(ConvertBoolToStringModel, ConvertIPToStringModel): +class StaticPortForward(BaseModel): source_ip: ipaddress.IPv4Address = Field(json_schema_extra={"vmanage_key": "source-ip"}) translate_ip: ipaddress.IPv4Address = Field(json_schema_extra={"vmanage_key": "translate-ip"}) static_nat_direction: StaticNatDirection = Field( @@ -108,7 +108,7 @@ class Encap(str, Enum): IPSEC = "ipsec" -class Encapsulation(ConvertBoolToStringModel): +class Encapsulation(BaseModel): encap: Encap preference: Optional[int] = None weight: int = DEFAULT_ENCAPSULATION_WEIGHT @@ -176,12 +176,12 @@ class Duplex(str, Enum): AUTO = "auto" -class Ip(ConvertBoolToStringModel, ConvertIPToStringModel): +class Ip(BaseModel): addr: ipaddress.IPv4Address mac: str -class Ipv4Secondary(ConvertBoolToStringModel, ConvertIPToStringModel): +class Ipv4Secondary(BaseModel): address: ipaddress.IPv4Address @@ -190,18 +190,18 @@ class TrackAction(str, Enum): SHUTDOWN = "Shutdown" -class TrackingObject(ConvertBoolToStringModel): +class TrackingObject(BaseModel): name: int track_action: TrackAction = Field(TrackAction.DECREMENT, json_schema_extra={"vmanage_key": "track-action"}) decrement: int model_config = ConfigDict(populate_by_name=True) -class Vrrp(ConvertBoolToStringModel, ConvertIPToStringModel): +class Vrrp(BaseModel): grp_id: int = Field(json_schema_extra={"vmanage_key": "grp-id"}) priority: int = DEFAULT_VRRP_PRIORITY timer: int = DEFAULT_VRRP_TIMER - track_omp: bool = Field(False, json_schema_extra={"vmanage_key": "track-omp"}) + track_omp: BoolStr = Field(default=False, json_schema_extra={"vmanage_key": "track-omp"}) track_prefix_list: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "track-prefix-list"}) address: Optional[ipaddress.IPv4Address] = Field( default=None, json_schema_extra={"data_path": ["ipv4"], "vmanage_key": "address"} @@ -209,7 +209,7 @@ class Vrrp(ConvertBoolToStringModel, ConvertIPToStringModel): ipv4_secondary: Optional[List[Ipv4Secondary]] = Field( default=None, json_schema_extra={"vmanage_key": "ipv4-secondary"} ) - tloc_change_pref: bool = Field(False, json_schema_extra={"vmanage_key": "tloc-change-pref"}) + tloc_change_pref: BoolStr = Field(default=False, json_schema_extra={"vmanage_key": "tloc-change-pref"}) value: int tracking_object: Optional[List[TrackingObject]] = Field( default=None, json_schema_extra={"vmanage_key": "tracking-object"} @@ -217,38 +217,38 @@ class Vrrp(ConvertBoolToStringModel, ConvertIPToStringModel): model_config = ConfigDict(populate_by_name=True) -class Ipv6(ConvertBoolToStringModel, ConvertIPToStringModel): +class Ipv6(BaseModel): ipv6_link_local: ipaddress.IPv6Address = Field(json_schema_extra={"vmanage_key": "ipv6-link-local"}) prefix: Optional[ipaddress.IPv6Interface] = None model_config = ConfigDict(populate_by_name=True) -class Ipv6Vrrp(ConvertBoolToStringModel): +class Ipv6Vrrp(BaseModel): grp_id: int = Field(json_schema_extra={"vmanage_key": "grp-id"}) priority: int = DEFAULT_IPV6_VRRP_PRIORITY timer: int = DEFAULT_IPV6_VRRP_TIMER - track_omp: bool = Field(False, json_schema_extra={"vmanage_key": "track-omp"}) + track_omp: BoolStr = Field(default=False, json_schema_extra={"vmanage_key": "track-omp"}) track_prefix_list: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "track-prefix-list"}) ipv6: Optional[List[Ipv6]] = None model_config = ConfigDict(populate_by_name=True) -class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertIPToStringModel): +class CiscoVpnInterfaceModel(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) if_name: str = Field(json_schema_extra={"vmanage_key": "if-name"}) interface_description: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "description"}) - poe: Optional[bool] = None + poe: Optional[BoolStr] = None ipv4_address: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["ip"], "vmanage_key": "address"}) secondary_ipv4_address: Optional[List[SecondaryIPv4Address]] = Field( default=None, json_schema_extra={"data_path": ["ip"], "vmanage_key": "secondary-address"} ) - dhcp_ipv4_client: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "dhcp-client"}) + dhcp_ipv4_client: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "dhcp-client"}) dhcp_distance: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "dhcp-distance"}) ipv6_address: Optional[ipaddress.IPv6Interface] = Field( default=None, json_schema_extra={"data_path": ["ipv6"], "vmanage_key": "address"} ) - dhcp_ipv6_client: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "dhcp-client"}) + dhcp_ipv6_client: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "dhcp-client"}) secondary_ipv6_address: Optional[List[SecondaryIPv6Address]] = Field( default=None, json_schema_extra={"data_path": ["ipv6"], "vmanage_key": "secondary-address"} ) @@ -260,13 +260,13 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI default=None, json_schema_extra={"vmanage_key": "dhcp-helper-v6"} ) tracker: Optional[List[str]] = None - auto_bandwidth_detect: Optional[bool] = Field( + auto_bandwidth_detect: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "auto-bandwidth-detect"} ) iperf_server: Optional[ipaddress.IPv4Address] = Field( default=None, json_schema_extra={"vmanage_key": "iperf-server"} ) - nat: Optional[bool] = None + nat: Optional[BoolStr] = None nat_choice: Optional[NatChoice] = Field(default=None, json_schema_extra={"vmanage_key": "nat-choice"}) udp_timeout: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "udp-timeout"}) tcp_timeout: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "tcp-timeout"}) @@ -274,12 +274,12 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI default=None, json_schema_extra={"vmanage_key": "range-start"} ) nat_range_end: Optional[ipaddress.IPv4Address] = Field(default=None, json_schema_extra={"vmanage_key": "range-end"}) - overload: Optional[bool] = None + overload: Optional[BoolStr] = None loopback_interface: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "loopback-interface"}) prefix_length: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "prefix-length"}) - enable: Optional[bool] = None - nat64: Optional[bool] = None - nat66: Optional[bool] = None + enable: Optional[BoolStr] = None + nat64: Optional[BoolStr] = None + nat66: Optional[BoolStr] = None static_nat66: Optional[List[StaticNat66]] = Field(default=None, json_schema_extra={"vmanage_key": "static-nat66"}) static: Optional[List[Static]] = Field( default=None, json_schema_extra={"data_path": ["nat"], "vmanage_key": "static"} @@ -287,7 +287,7 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI static_port_forward: Optional[List[StaticPortForward]] = Field( default=None, json_schema_extra={"vmanage_key": "static-port-forward"} ) - enable_core_region: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "enable-core-region"}) + enable_core_region: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "enable-core-region"}) core_region: Optional[CoreRegion] = Field(default=None, json_schema_extra={"vmanage_key": "core-region"}) secondary_region: Optional[SecondaryRegion] = Field( default=None, json_schema_extra={"vmanage_key": "secondary-region"} @@ -295,9 +295,9 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI tloc_encapsulation: Optional[List[Encapsulation]] = Field( default=None, json_schema_extra={"vmanage_key": "encapsulation", "data_path": ["tunnel-interface"]} ) - border: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface"]}) - per_tunnel_qos: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "per-tunnel-qos"}) - per_tunnel_qos_aggregator: Optional[bool] = Field( + border: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface"]}) + per_tunnel_qos: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "per-tunnel-qos"}) + per_tunnel_qos_aggregator: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "per-tunnel-qos-aggregator"} ) mode: Optional[Mode] = None @@ -307,10 +307,10 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI max_control_connections: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "max-control-connections", "data_path": ["tunnel-interface"]} ) - control_connections: Optional[bool] = Field( + control_connections: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "control-connections", "data_path": ["tunnel-interface"]} ) - vbond_as_stun_server: Optional[bool] = Field( + vbond_as_stun_server: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "vbond-as-stun-server", "data_path": ["tunnel-interface"]} ) exclude_controller_group_list: Optional[List[int]] = Field( @@ -321,10 +321,10 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI default=None, json_schema_extra={"vmanage_key": "vmanage-connection-preference", "data_path": ["tunnel-interface"]}, ) - port_hop: Optional[bool] = Field( + port_hop: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "port-hop", "data_path": ["tunnel-interface"]} ) - restrict: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "color"]}) + restrict: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "color"]}) dst_ip: Optional[ipaddress.IPv4Address] = Field( default=None, json_schema_extra={"vmanage_key": "dst-ip", "data_path": ["tunnel-interface", "tloc-extension-gre-to"]}, @@ -340,38 +340,52 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI default=None, json_schema_extra={"vmanage_key": "hello-tolerance", "data_path": ["tunnel-interface"]} ) bind: Optional[str] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface"]}) - last_resort_circuit: Optional[bool] = Field( + last_resort_circuit: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "last-resort-circuit", "data_path": ["tunnel-interface"]} ) - low_bandwidth_link: Optional[bool] = Field( + low_bandwidth_link: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "low-bandwidth-link", "data_path": ["tunnel-interface"]} ) tunnel_tcp_mss_adjust: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "tunnel-tcp-mss-adjust", "data_path": ["tunnel-interface"]} ) - clear_dont_fragment: Optional[bool] = Field( + clear_dont_fragment: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "clear-dont-fragment", "data_path": ["tunnel-interface"]} ) - propagate_sgt: Optional[bool] = Field( + propagate_sgt: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["tunnel-interface"], "vmanage_key": "propagate-sgt"} ) - network_broadcast: Optional[bool] = Field( + network_broadcast: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "network-broadcast", "data_path": ["tunnel-interface"]} ) - all: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - bgp: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - dhcp: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - dns: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - icmp: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - sshd: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - netconf: Optional[bool] = Field( + all: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) + bgp: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) + dhcp: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + dns: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) + icmp: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + sshd: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + netconf: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + ntp: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) + ospf: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + stun: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + snmp: Optional[BoolStr] = Field( + default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} + ) + https: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]} ) - ntp: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - ospf: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - stun: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - snmp: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) - https: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["tunnel-interface", "allow-service"]}) media_type: Optional[MediaType] = Field(default=None, json_schema_extra={"vmanage_key": "media-type"}) intrf_mtu: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "intrf-mtu"}) mtu: Optional[int] = None @@ -385,16 +399,16 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI mac_address: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "mac-address"}) speed: Optional[Speed] = None duplex: Optional[Duplex] = None - shutdown: Optional[bool] = False + shutdown: Optional[BoolStr] = False arp_timeout: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "arp-timeout"}) - autonegotiate: Optional[bool] = None - ip_directed_broadcast: Optional[bool] = Field( + autonegotiate: Optional[BoolStr] = None + ip_directed_broadcast: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "ip-directed-broadcast"} ) - icmp_redirect_disable: Optional[bool] = Field( + icmp_redirect_disable: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "icmp-redirect-disable"} ) - qos_adaptive: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "qos-adaptive"}) + qos_adaptive: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "qos-adaptive"}) period: Optional[int] = Field(default=None, json_schema_extra={"data_path": ["qos-adaptive"]}) bandwidth_down: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "bandwidth-down", "data_path": ["qos-adaptive", "downstream"]} @@ -412,7 +426,9 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI service_provider: Optional[str] = Field(default=None, json_schema_extra={"vmanage_key": "service-provider"}) bandwidth_upstream: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "bandwidth-upstream"}) bandwidth_downstream: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "bandwidth-downstream"}) - block_non_source_ip: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "block-non-source-ip"}) + block_non_source_ip: Optional[BoolStr] = Field( + default=None, json_schema_extra={"vmanage_key": "block-non-source-ip"} + ) rule_name: Optional[str] = Field( default=None, json_schema_extra={"vmanage_key": "rule-name", "data_path": ["rewrite-rule"]} ) @@ -422,17 +438,17 @@ class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertI ip: Optional[List[Ip]] = Field(default=None, json_schema_extra={"data_path": ["arp"]}) vrrp: Optional[List[Vrrp]] = Field(default=None, json_schema_extra={"vmanage_key": "vrrp"}) ipv6_vrrp: Optional[List[Ipv6Vrrp]] = Field(default=None, json_schema_extra={"vmanage_key": "ipv6-vrrp"}) - enable_sgt_propagation: Optional[bool] = Field( + enable_sgt_propagation: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["trustsec", "propagate"], "vmanage_key": "sgt"} ) security_group_tag: Optional[int] = Field( default=None, json_schema_extra={"data_path": ["trustsec", "static"], "vmanage_key": "sgt"} ) - trusted: Optional[bool] = Field(default=None, json_schema_extra={"data_path": ["trustsec", "static"]}) - enable_sgt_authorization_and_forwarding: Optional[bool] = Field( + trusted: Optional[BoolStr] = Field(default=None, json_schema_extra={"data_path": ["trustsec", "static"]}) + enable_sgt_authorization_and_forwarding: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["trustsec"], "vmanage_key": "enable"} ) - enable_sgt_enforcement: Optional[bool] = Field( + enable_sgt_enforcement: Optional[BoolStr] = Field( default=None, json_schema_extra={"data_path": ["trustsec", "enforcement"], "vmanage_key": "enable"} ) enforcement_sgt: Optional[int] = Field( diff --git a/catalystwan/api/templates/models/omp_vsmart_model.py b/catalystwan/api/templates/models/omp_vsmart_model.py index 5daa2edcc..88e31e214 100644 --- a/catalystwan/api/templates/models/omp_vsmart_model.py +++ b/catalystwan/api/templates/models/omp_vsmart_model.py @@ -3,18 +3,18 @@ from pydantic import ConfigDict, Field +from catalystwan.api.templates.bool_str import BoolStr from catalystwan.api.templates.feature_template import FeatureTemplate -from catalystwan.utils.pydantic_validators import ConvertBoolToStringModel -class OMPvSmart(FeatureTemplate, ConvertBoolToStringModel): +class OMPvSmart(FeatureTemplate): model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) - graceful_restart: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "graceful-restart"}) + graceful_restart: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "graceful-restart"}) send_path_limit: Optional[int] = Field(default=None, json_schema_extra={"vmanage_key": "send-path-limit"}) - send_backup_paths: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "send-backup-paths"}) - discard_rejected: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "discard-rejected"}) - shutdown: Optional[bool] = Field(default=None, json_schema_extra={"vmanage_key": "shutdown"}) + send_backup_paths: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "send-backup-paths"}) + discard_rejected: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "discard-rejected"}) + shutdown: Optional[BoolStr] = Field(default=None, json_schema_extra={"vmanage_key": "shutdown"}) graceful_restart_timer: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "graceful-restart-timer", "data_path": ["timers"]} ) @@ -24,7 +24,7 @@ class OMPvSmart(FeatureTemplate, ConvertBoolToStringModel): holdtime: Optional[int] = Field( default=None, json_schema_extra={"vmanage_key": "holdtime", "data_path": ["timers"]} ) - affinity_group_preference: Optional[bool] = Field( + affinity_group_preference: Optional[BoolStr] = Field( default=None, json_schema_extra={"vmanage_key": "affinity-group-preference"} ) advertisement_interval: Optional[int] = Field( diff --git a/catalystwan/tests/templates/test_generate_payload.py b/catalystwan/tests/templates/test_generate_payload.py index 81172af7e..d4f6b3cbf 100644 --- a/catalystwan/tests/templates/test_generate_payload.py +++ b/catalystwan/tests/templates/test_generate_payload.py @@ -73,14 +73,16 @@ class DataPathFeatureTemplate(FeatureTemplate): as_num: str = Field(alias="as-num", json_schema_extra={"data_path": ["authentication", "dot1x", "default"]}) +password = "pass" # pragma: allowlist secret + mocked_feature_template_children_1 = MockedFeatureTemplateChildren( - user=[User(name="user1", password="pass"), User(name="user2", password="pass")] + user=[User(name="user1", password=password), User(name="user2", password=password)] ) mocked_feature_template_children_2 = MockedFeatureTemplateChildren( user=[ - User(name="user1", password="pass", pubkey_chain=[RSA(key="*****", key_type="RSA")]), - User(name="user2", password="pass"), + User(name="user1", password=password, pubkey_chain=[RSA(key="*****", key_type="RSA")]), + User(name="user2", password=password), ] ) @@ -122,7 +124,7 @@ def test_get( # Act a = templates_api.generate_feature_template_payload(mocked_template, schema).model_dump( - by_alias=True, exclude_none=True + by_alias=True, exclude_none=True, mode="json" )["templateDefinition"] print(json.dumps(a)) # Assert diff --git a/catalystwan/tests/templates/test_serialize_model.py b/catalystwan/tests/templates/test_serialize_model.py index e8822af39..817331359 100644 --- a/catalystwan/tests/templates/test_serialize_model.py +++ b/catalystwan/tests/templates/test_serialize_model.py @@ -43,7 +43,7 @@ def test_generate_feature_template_payload_definition( self.maxDiff = 10000 self.assertDictEqual( definition["templateDefinition"], - feature_template_payload.model_dump(by_alias=True)["templateDefinition"], + feature_template_payload.model_dump(by_alias=True, mode="json")["templateDefinition"], ) diff --git a/catalystwan/utils/pydantic_validators.py b/catalystwan/utils/pydantic_validators.py deleted file mode 100644 index c8f5accaf..000000000 --- a/catalystwan/utils/pydantic_validators.py +++ /dev/null @@ -1,32 +0,0 @@ -import ipaddress -from typing import Any - -from pydantic import BaseModel, model_validator - - -class ConvertBoolToStringModel(BaseModel): - @model_validator(mode="after") - def convert_bool_to_string_validator(self): - for key in self.model_fields.keys(): - value = getattr(self, key) - if isinstance(value, bool): - setattr(self, key, str(value).lower()) - return self - - -class ConvertIPToStringModel(BaseModel): - @model_validator(mode="after") - def convert_ip_to_string_validator(self): - for key in self.model_fields.keys(): - value = getattr(self, key) - setattr(self, key, convert_ip_to_string(value)) - return self - - -def convert_ip_to_string(values: Any): - if isinstance(values, list): - for index, ip in enumerate(values): - values[index] = convert_ip_to_string(ip) - if isinstance(values, ipaddress._BaseAddress): - values = str(values) - return values diff --git a/pyproject.toml b/pyproject.toml index 648c4b23d..8062fbc30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,8 @@ flake8-quotes = "^3.3.1" clint = "^0.5.1" requests-toolbelt = "^1.0.0" packaging = "^23.0" -pydantic = "2.5" +pydantic = "^2.5" +typing-extensions = "^4.6.1" [tool.poetry.dev-dependencies] parameterized = "^0.8.1"