diff --git a/catalystwan/api/api_container.py b/catalystwan/api/api_container.py index d9ea675ff..290a0b26b 100644 --- a/catalystwan/api/api_container.py +++ b/catalystwan/api/api_container.py @@ -18,7 +18,7 @@ from catalystwan.api.config_device_inventory_api import ConfigurationDeviceInventoryAPI from catalystwan.api.config_group_api import ConfigGroupAPI from catalystwan.api.dashboard_api import DashboardAPI -from catalystwan.api.feature_profile_api import SDRoutingFeatureProfilesAPI, SDWANFeatureProfilesAPI +from catalystwan.api.feature_profile_api import SDRoutingFeatureProfilesAPI from catalystwan.api.logs_api import LogsAPI from catalystwan.api.omp_api import OmpAPI from catalystwan.api.packet_capture_api import PacketCaptureAPI @@ -66,4 +66,3 @@ def __init__(self, session: ManagerSession): self.sessions = SessionsAPI(session) self.policy = PolicyAPI(session) self.sd_routing_feature_profiles = SDRoutingFeatureProfilesAPI(session) - self.sdwan_feature_profiles = SDWANFeatureProfilesAPI(session) diff --git a/catalystwan/api/config_group_api.py b/catalystwan/api/config_group_api.py index f653ea225..9e3d9fbab 100644 --- a/catalystwan/api/config_group_api.py +++ b/catalystwan/api/config_group_api.py @@ -2,16 +2,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Union -from uuid import UUID - -from catalystwan.typed_list import DataSequence +from typing import TYPE_CHECKING if TYPE_CHECKING: from catalystwan.session import ManagerSession from catalystwan.endpoints.configuration_group import ( - ConfigGroup, ConfigGroupAssociatePayload, ConfigGroupCreationPayload, ConfigGroupCreationResponse, @@ -20,6 +16,7 @@ ConfigGroupDisassociateResponse, ConfigGroupEditPayload, ConfigGroupEditResponse, + ConfigGroupResponsePayload, ConfigGroupVariablesCreatePayload, ConfigGroupVariablesCreateResponse, ConfigGroupVariablesEditPayload, @@ -113,14 +110,11 @@ def edit( return self.endpoint.edit_config_group(config_group_id=cg_id, payload=payload) - def get(self, group_id: Optional[UUID] = None) -> Union[DataSequence[ConfigGroup], ConfigGroup, None]: + def get(self) -> ConfigGroupResponsePayload: """ - Gets list of existing config-groups or single config-group with given ID - If given ID is not correct return None + Gets list of existing config-groups """ - if group_id is None: - return self.endpoint.get() - return self.endpoint.get().filter(id=group_id).single_or_default() + return self.endpoint.get() def update_variables(self, cg_id: str, solution: Solution, device_variables: list) -> None: """ diff --git a/catalystwan/api/configuration_groups/parcel.py b/catalystwan/api/configuration_groups/parcel.py index 46bede28d..df4c20a59 100644 --- a/catalystwan/api/configuration_groups/parcel.py +++ b/catalystwan/api/configuration_groups/parcel.py @@ -3,25 +3,13 @@ from enum import Enum from typing import Any, Dict, Generic, Literal, Optional, TypeVar, get_origin -from pydantic import ( - AliasPath, - BaseModel, - ConfigDict, - Field, - PrivateAttr, - SerializerFunctionWrapHandler, - model_serializer, -) - -from catalystwan.exceptions import CatalystwanException +from pydantic import AliasPath, BaseModel, ConfigDict, Field, PrivateAttr, model_serializer T = TypeVar("T") class _ParcelBase(BaseModel): - model_config = ConfigDict( - extra="allow", arbitrary_types_allowed=True, populate_by_name=True, json_schema_mode_override="validation" - ) + model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, populate_by_name=True) parcel_name: str = Field( min_length=1, max_length=128, @@ -35,18 +23,11 @@ class _ParcelBase(BaseModel): validation_alias="description", description="Set the parcel description", ) + data: Optional[Any] = None _parcel_data_key: str = PrivateAttr(default="data") @model_serializer(mode="wrap") - def envelope_parcel_data(self, handler: SerializerFunctionWrapHandler) -> Dict[str, Any]: - """ - serializes model fields with respect to field validation_alias, - sub-classing parcel fields can be defined like following: - >>> entries: List[SecurityZoneListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) - - "data" is default _parcel_data_key which must match validation_alias prefix, - this attribute can be overriden in sub-class when needed - """ + def envelope_parcel_data(self, handler) -> Dict[str, Any]: model_dict = handler(self) model_dict[self._parcel_data_key] = {} remove_keys = [] @@ -62,13 +43,6 @@ def envelope_parcel_data(self, handler: SerializerFunctionWrapHandler) -> Dict[s del model_dict[key] return model_dict - @classmethod - def _get_parcel_type(cls) -> str: - field_info = cls.model_fields.get("type_") - if field_info is not None: - return str(field_info.default) - raise CatalystwanException("Field parcel type is not set.") - class OptionType(str, Enum): GLOBAL = "global" @@ -77,7 +51,7 @@ class OptionType(str, Enum): class ParcelAttribute(BaseModel): - model_config = ConfigDict(extra="forbid", populate_by_name=True) + model_config = ConfigDict(extra="forbid") option_type: OptionType = Field(serialization_alias="optionType", validation_alias="optionType") diff --git a/catalystwan/api/feature_profile_api.py b/catalystwan/api/feature_profile_api.py index 7eaf4acd9..bd14d3928 100644 --- a/catalystwan/api/feature_profile_api.py +++ b/catalystwan/api/feature_profile_api.py @@ -2,12 +2,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional, Protocol, Type, Union, overload +from typing import TYPE_CHECKING, Any, Protocol, Type, Union, overload from uuid import UUID -from pydantic import Json - -from catalystwan.endpoints.configuration.feature_profile.sdwan.system import SystemFeatureProfile from catalystwan.typed_list import DataSequence if TYPE_CHECKING: @@ -19,12 +16,11 @@ from catalystwan.models.configuration.feature_profile.common import ( FeatureProfileCreationPayload, FeatureProfileCreationResponse, - FeatureProfileInfo, - GetFeatureProfilesPayload, Parcel, ParcelCreationResponse, ) from catalystwan.models.configuration.feature_profile.sdwan.policy_object import ( + POLICY_OBJECT_PAYLOAD_ENDPOINT_MAPPING, AnyPolicyObjectParcel, ApplicationListParcel, AppProbeParcel, @@ -48,32 +44,15 @@ SecurityZoneListParcel, StandardCommunityParcel, TlocParcel, -) -from catalystwan.models.configuration.feature_profile.sdwan.system import ( - AAAParcel, - AnySystemParcel, - BannerParcel, - BasicParcel, - BFDParcel, - GlobalParcel, - LoggingParcel, - MRFParcel, - NTPParcel, - OMPParcel, - SecurityParcel, - SNMPParcel, + URLAllowParcel, + URLBlockParcel, ) class SDRoutingFeatureProfilesAPI: def __init__(self, session: ManagerSession): self.cli = SDRoutingCLIFeatureProfileAPI(session=session) - - -class SDWANFeatureProfilesAPI: - def __init__(self, session: ManagerSession): self.policy_object = PolicyObjectFeatureProfileAPI(session=session) - self.system = SystemFeatureProfileAPI(session=session) class FeatureProfileAPI(Protocol): @@ -126,372 +105,6 @@ def delete(self, fp_id: str) -> None: self.endpoint.delete_cli_feature_profile(cli_fp_id=fp_id) -class SystemFeatureProfileAPI: - """ - SDWAN Feature Profile System APIs - """ - - def __init__(self, session: ManagerSession): - self.session = session - self.endpoint = SystemFeatureProfile(session) - - def get_profiles( - self, limit: Optional[int] = None, offset: Optional[int] = None - ) -> DataSequence[FeatureProfileInfo]: - """ - Get all System Feature Profiles - """ - payload = GetFeatureProfilesPayload(limit=limit if limit else None, offset=offset if offset else None) - - return self.endpoint.get_sdwan_system_feature_profiles(payload) - - def create_profile(self, name: str, description: str) -> FeatureProfileCreationResponse: - """ - Create System Feature Profile - """ - payload = FeatureProfileCreationPayload(name=name, description=description) - return self.endpoint.create_sdwan_system_feature_profile(payload) - - def delete_profile(self, profile_id: UUID) -> None: - """ - Delete System Feature Profile - """ - self.endpoint.delete_sdwan_system_feature_profile(profile_id) - - def get_schema( - self, - profile_id: UUID, - parcel_type: Type[AnySystemParcel], - ) -> Json: - """ - Get all System Parcels for selected profile_id and selected type or get one Policy Object given parcel id - """ - - return self.endpoint.get_schema(profile_id, parcel_type._get_parcel_type()) - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[AAAParcel], - ) -> DataSequence[Parcel[AAAParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[BFDParcel], - ) -> DataSequence[Parcel[BFDParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[LoggingParcel], - ) -> DataSequence[Parcel[LoggingParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[BannerParcel], - ) -> DataSequence[Parcel[BannerParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[BasicParcel], - ) -> DataSequence[Parcel[BasicParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[GlobalParcel], - ) -> DataSequence[Parcel[GlobalParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[NTPParcel], - ) -> DataSequence[Parcel[NTPParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[MRFParcel], - ) -> DataSequence[Parcel[MRFParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[OMPParcel], - ) -> DataSequence[Parcel[OMPParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[SecurityParcel], - ) -> DataSequence[Parcel[SecurityParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[SNMPParcel], - ) -> DataSequence[Parcel[SNMPParcel]]: - ... - - # get by id - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[AAAParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[AAAParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[BFDParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[BFDParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[LoggingParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[LoggingParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[BannerParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[BannerParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[BasicParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[BasicParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[GlobalParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[GlobalParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[NTPParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[NTPParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[MRFParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[MRFParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[OMPParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[OMPParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[SecurityParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[SecurityParcel]]: - ... - - @overload - def get( - self, - profile_id: UUID, - parcel_type: Type[SNMPParcel], - parcel_id: UUID, - ) -> DataSequence[Parcel[SNMPParcel]]: - ... - - def get( - self, - profile_id: UUID, - parcel_type: Type[AnySystemParcel], - parcel_id: Union[UUID, None] = None, - ) -> DataSequence[Parcel[Any]]: - """ - Get all System Parcels for selected profile_id and selected type or get one System Parcel given parcel id - """ - - if not parcel_id: - return self.endpoint.get_all(profile_id, parcel_type._get_parcel_type()) - return self.endpoint.get_by_id(profile_id, parcel_type._get_parcel_type(), parcel_id) - - def create(self, profile_id: UUID, payload: AnySystemParcel) -> ParcelCreationResponse: - """ - Create System Parcel for selected profile_id based on payload type - """ - - return self.endpoint.create(profile_id, payload._get_parcel_type(), payload) - - def update(self, profile_id: UUID, payload: AnySystemParcel, parcel_id: UUID) -> ParcelCreationResponse: - """ - Update System Parcel for selected profile_id based on payload type - """ - - return self.endpoint.update(profile_id, payload._get_parcel_type(), parcel_id, payload) - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[AAAParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[BFDParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[LoggingParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[BannerParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[BasicParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[GlobalParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[NTPParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[MRFParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[OMPParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[SecurityParcel], - parcel_id: UUID, - ) -> None: - ... - - @overload - def delete( - self, - profile_id: UUID, - parcel_type: Type[SNMPParcel], - parcel_id: UUID, - ) -> None: - ... - - def delete(self, profile_id: UUID, parcel_type: Type[AnySystemParcel], parcel_id: UUID) -> None: - """ - Delete System Parcel for selected profile_id based on payload type - """ - return self.endpoint.delete(profile_id, parcel_type._get_parcel_type(), parcel_id) - - class PolicyObjectFeatureProfileAPI: """ SDWAN Feature Profile Policy Object APIs @@ -589,13 +202,13 @@ def get(self, profile_id: UUID, parcel_type: Type[StandardCommunityParcel]) -> D def get(self, profile_id: UUID, parcel_type: Type[TlocParcel]) -> DataSequence[Parcel[Any]]: ... - # @overload - # def get(self, profile_id: UUID, parcel_type: Type[URLAllowParcel]) -> DataSequence[Parcel[Any]]: - # ... + @overload + def get(self, profile_id: UUID, parcel_type: Type[URLAllowParcel]) -> DataSequence[Parcel[Any]]: + ... - # @overload - # def get(self, profile_id: UUID, parcel_type: Type[URLBlockParcel]) -> DataSequence[Parcel[Any]]: - # ... + @overload + def get(self, profile_id: UUID, parcel_type: Type[URLBlockParcel]) -> DataSequence[Parcel[Any]]: + ... # get by id @@ -715,13 +328,13 @@ def get( def get(self, profile_id: UUID, parcel_type: Type[TlocParcel], parcel_id: UUID) -> DataSequence[Parcel[Any]]: ... - # @overload - # def get(self, profile_id: UUID, parcel_type: Type[URLAllowParcel], parcel_id: UUID) -> DataSequence[Parcel[Any]]: - # ... + @overload + def get(self, profile_id: UUID, parcel_type: Type[URLAllowParcel], parcel_id: UUID) -> DataSequence[Parcel[Any]]: + ... - # @overload - # def get(self, profile_id: UUID, parcel_type: Type[URLBlockParcel], parcel_id: UUID) -> DataSequence[Parcel[Any]]: - # ... + @overload + def get(self, profile_id: UUID, parcel_type: Type[URLBlockParcel], parcel_id: UUID) -> DataSequence[Parcel[Any]]: + ... def get( self, @@ -733,7 +346,7 @@ def get( Get all Policy Objects for selected profile_id and selected type or get one Policy Object given parcel id """ - policy_object_list_type = parcel_type._get_parcel_type() + policy_object_list_type = POLICY_OBJECT_PAYLOAD_ENDPOINT_MAPPING[parcel_type] if not parcel_id: return self.endpoint.get_all(profile_id=profile_id, policy_object_list_type=policy_object_list_type) parcel = self.endpoint.get_by_id( @@ -746,7 +359,7 @@ def create(self, profile_id: UUID, payload: AnyPolicyObjectParcel) -> ParcelCrea Create Policy Object for selected profile_id based on payload type """ - policy_object_list_type = payload._get_parcel_type() + policy_object_list_type = POLICY_OBJECT_PAYLOAD_ENDPOINT_MAPPING[type(payload)] return self.endpoint.create( profile_id=profile_id, policy_object_list_type=policy_object_list_type, payload=payload ) @@ -756,7 +369,7 @@ def update(self, profile_id: UUID, payload: AnyPolicyObjectParcel, list_object_i Update Policy Object for selected profile_id based on payload type """ - policy_type = payload._get_parcel_type() + policy_type = POLICY_OBJECT_PAYLOAD_ENDPOINT_MAPPING[type(payload)] return self.endpoint.update( profile_id=profile_id, policy_object_list_type=policy_type, list_object_id=list_object_id, payload=payload ) @@ -849,20 +462,20 @@ def delete(self, profile_id: UUID, parcel_type: Type[StandardCommunityParcel], l def delete(self, profile_id: UUID, parcel_type: Type[TlocParcel], list_object_id: UUID) -> None: ... - # @overload - # def delete(self, profile_id: UUID, parcel_type: Type[URLAllowParcel], list_object_id: UUID) -> None: - # ... + @overload + def delete(self, profile_id: UUID, parcel_type: Type[URLAllowParcel], list_object_id: UUID) -> None: + ... - # @overload - # def delete(self, profile_id: UUID, parcel_type: Type[URLBlockParcel], list_object_id: UUID) -> None: - # ... + @overload + def delete(self, profile_id: UUID, parcel_type: Type[URLBlockParcel], list_object_id: UUID) -> None: + ... def delete(self, profile_id: UUID, parcel_type: Type[AnyPolicyObjectParcel], list_object_id: UUID) -> None: """ Delete Policy Object for selected profile_id based on payload type """ - policy_object_list_type = parcel_type._get_parcel_type() + policy_object_list_type = POLICY_OBJECT_PAYLOAD_ENDPOINT_MAPPING[parcel_type] return self.endpoint.delete( profile_id=profile_id, policy_object_list_type=policy_object_list_type, list_object_id=list_object_id ) diff --git a/catalystwan/api/policy_api.py b/catalystwan/api/policy_api.py index dd282bcbe..ff6d40cb0 100644 --- a/catalystwan/api/policy_api.py +++ b/catalystwan/api/policy_api.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple, Type, overload +from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Type, overload from uuid import UUID from catalystwan.api.task_status_api import Task @@ -639,12 +639,6 @@ def get(self, type: Type[AnyPolicyList], id: Optional[UUID] = None) -> Any: return endpoints.get_lists_by_id(id=id) return endpoints.get_policy_lists() - def get_all(self) -> List[AnyPolicyList]: - infos: List[AnyPolicyList] = [] - for list_type, _ in POLICY_LIST_ENDPOINTS_MAP.items(): - infos.extend(self.get(list_type)) - return infos - class PolicyDefinitionsAPI: def __init__(self, session: ManagerSession): @@ -788,12 +782,6 @@ def get(self, type: Type[AnyPolicyDefinition], id: Optional[UUID] = None) -> Any return endpoints.get_policy_definition(id=id) return endpoints.get_definitions() - def get_all(self) -> List[Tuple[type, PolicyDefinitionInfo]]: - all_items: List[Tuple[type, PolicyDefinitionInfo]] = [] - for definition_type, _ in POLICY_DEFINITION_ENDPOINTS_MAP.items(): - all_items.extend([(definition_type, info) for info in self.get(definition_type)]) - return all_items - class PolicyAPI: """This is exposing so called 'UX 1.0' API""" diff --git a/catalystwan/api/template_api.py b/catalystwan/api/template_api.py index b1b68b892..31b5050e4 100644 --- a/catalystwan/api/template_api.py +++ b/catalystwan/api/template_api.py @@ -2,14 +2,12 @@ from __future__ import annotations -import datetime as dt import json import logging from enum import Enum -from typing import TYPE_CHECKING, Any, List, Optional, Type, overload +from typing import TYPE_CHECKING, Any, Optional, Type, overload from ciscoconfparse import CiscoConfParse # type: ignore -from pydantic import BaseModel, ConfigDict, Field from catalystwan.api.task_status_api import Task from catalystwan.api.templates.cli_template import CLITemplate @@ -71,41 +69,6 @@ class DeviceTemplateFeature(Enum): ALL = "all" -class TemplateInformation(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - last_updated_by: str = Field(serialization_alias="lastUpdatedBy", validation_alias="lastUpdatedBy") - id: str = Field(serialization_alias="templateId", validation_alias="templateId") - factory_default: bool = Field(serialization_alias="factoryDefault", validation_alias="factoryDefault") - name: str = Field(serialization_alias="templateName", validation_alias="templateName") - devices_attached: int = Field(serialization_alias="devicesAttached", validation_alias="devicesAttached") - description: str = Field(serialization_alias="templateDescription", validation_alias="templateDescription") - last_updated_on: dt.datetime = Field(serialization_alias="lastUpdatedOn", validation_alias="lastUpdatedOn") - resource_group: Optional[str] = Field(None, serialization_alias="resourceGroup", validation_alias="resourceGroup") - - -class FeatureTemplateInformation(TemplateInformation): - model_config = ConfigDict(populate_by_name=True) - - template_type: str = Field(serialization_alias="templateType", validation_alias="templateType") - device_type: List[str] = Field(serialization_alias="deviceType", validation_alias="deviceType") - version: str = Field(serialization_alias="templateMinVersion", validation_alias="templateMinVersion") - template_definiton: Optional[str] = Field( - None, serialization_alias="templateDefinition", validation_alias="templateDefinition" - ) - - -class DeviceTemplateInformation(TemplateInformation): - model_config = ConfigDict(populate_by_name=True) - - device_type: str = Field(serialization_alias="deviceType", validation_alias="deviceType") - template_class: str = Field(serialization_alias="templateClass", validation_alias="templateClass") - config_type: str = Field(serialization_alias="configType", validation_alias="configType") - template_attached: int = Field(serialization_alias="templateAttached", validation_alias="templateAttached") - draft_mode: Optional[str] = Field(None, serialization_alias="draftMode", validation_alias="draftMode") - device_role: Optional[str] = Field(None, serialization_alias="deviceRole", validation_alias="deviceRole") - - class TemplatesAPI: def __init__(self, session: ManagerSession) -> None: self.session = session @@ -735,14 +698,3 @@ def load_running(self, device: Device) -> CiscoConfParse: config = CiscoConfParse(response["config"].splitlines()) logger.debug(f"Template loaded from {device.hostname}.") return config - - def get_feature_templates(self) -> DataSequence[FeatureTemplateInformation]: - endpoint = "/dataservice/template/feature" - fr_templates = self.session.get(endpoint) - return fr_templates.dataseq(FeatureTemplateInformation) - - def get_device_templates(self) -> DataSequence[DeviceTemplateInformation]: - endpoint = "/dataservice/template/device" - params = {"feature": "all"} - templates = self.session.get(url=endpoint, params=params) - return templates.dataseq(DeviceTemplateInformation) diff --git a/catalystwan/endpoints/__init__.py b/catalystwan/endpoints/__init__.py index f45341745..06e25fa9d 100644 --- a/catalystwan/endpoints/__init__.py +++ b/catalystwan/endpoints/__init__.py @@ -52,7 +52,6 @@ Sequence, Set, Tuple, - Type, TypeVar, Union, runtime_checkable, @@ -117,47 +116,6 @@ def json(cls) -> TypeSpecifier: def model_union(cls, models: Sequence[type]) -> TypeSpecifier: return TypeSpecifier(present=True, payload_union_model_types=models) - @classmethod - def resolve_nested_base_model_unions( - cls, annotation: Any, models_types: List[Union[Type[BaseModelV1], Type[BaseModelV2]]] - ) -> List[Union[Type[BaseModelV1], Type[BaseModelV2]]]: - type_origin = get_origin(annotation) - if isclass(annotation): - try: - if issubclass(annotation, (BaseModelV1, BaseModelV2)): - return [annotation] - raise APIEndpointError(f"Expected: {PayloadType}") - except TypeError: - raise APIEndpointError(f"Expected: {PayloadType}") - # Check if Annnotated[Union[PayloadModelType, ...]], only unions of pydantic models allowed - elif type_origin == Annotated: - if annotated_origin := get_args(annotation): - if (len(annotated_origin) >= 1) and get_origin(annotated_origin[0]) == Union: - type_args = get_args(annotated_origin[0]) - if all(isclass(t) for t in type_args) and all( - issubclass(t, (BaseModelV1, BaseModelV2)) for t in type_args - ): - models_types.extend(list(type_args)) - return models_types - else: - non_models = [t for t in type_args if not isclass(t)] - for non_model in non_models: - models_types.extend(cls.resolve_nested_base_model_unions(non_model, models_types)) - return models_types - - # Check if Union[PayloadModelType, ...], only unions of pydantic models allowed - elif type_origin == Union: - type_args = get_args(annotation) - if all(isclass(t) for t in type_args) and all(issubclass(t, (BaseModelV1, BaseModelV2)) for t in type_args): - models_types.extend(list(type_args)) - return models_types - else: - non_models = [t for t in type_args if not isclass(t)] - for non_model in non_models: - models_types.extend(cls.resolve_nested_base_model_unions(non_model, models_types)) - return models_types - raise APIEndpointError(f"Expected: {PayloadType}") - @dataclass class APIEndpointRequestMeta: @@ -495,10 +453,27 @@ def specify_payload_type(self) -> TypeSpecifier: and issubclass(type_args[0], (BaseModelV1, BaseModelV2)) ): return TypeSpecifier(True, type_origin, type_args[0], None, False, is_optional) - else: - models = TypeSpecifier.resolve_nested_base_model_unions(annotation, []) - return TypeSpecifier.model_union(models) - raise APIEndpointError(f"'payload' param must be annotated with supported type: {PayloadType}") + # Check if Annnotated[Union[PayloadModelType, ...]], only unions of pydantic models allowed + elif type_origin == Annotated: + if annotated_origin := get_args(annotation): + if (len(annotated_origin) >= 1) and get_origin(annotated_origin[0]) == Union: + if ( + (type_args := get_args(annotated_origin[0])) + and all(isclass(t) for t in type_args) + and all(issubclass(t, (BaseModelV1, BaseModelV2)) for t in type_args) + ): + return TypeSpecifier.model_union(models=list(type_args)) + # Check if Union[PayloadModelType, ...], only unions of pydantic models allowed + elif type_origin == Union: + if ( + (type_args := get_args(annotation)) + and all(isclass(t) for t in type_args) + and all(issubclass(t, (BaseModelV1, BaseModelV2)) for t in type_args) + ): + return TypeSpecifier.model_union(models=list(type_args)) + raise APIEndpointError(f"Expected: {PayloadType} but found payload {annotation}") + else: + raise APIEndpointError(f"Expected: {PayloadType} but found payload {annotation}") def check_params(self): """Checks params in decorated method definition diff --git a/catalystwan/endpoints/configuration/feature_profile/sdwan/system.py b/catalystwan/endpoints/configuration/feature_profile/sdwan/system.py index d9243200f..6c34b1fd3 100644 --- a/catalystwan/endpoints/configuration/feature_profile/sdwan/system.py +++ b/catalystwan/endpoints/configuration/feature_profile/sdwan/system.py @@ -2,7 +2,6 @@ # mypy: disable-error-code="empty-body" from typing import Optional -from uuid import UUID from catalystwan.api.configuration_groups.parcel import _ParcelBase from catalystwan.endpoints import JSON, APIEndpoints, delete, get, post, put, versions @@ -11,18 +10,16 @@ FeatureProfileCreationResponse, FeatureProfileInfo, GetFeatureProfilesPayload, - Parcel, ParcelId, SchemaTypeQuery, ) -from catalystwan.models.configuration.feature_profile.sdwan.system import AnySystemParcel from catalystwan.typed_list import DataSequence class SystemFeatureProfile(APIEndpoints): @versions(supported_versions=(">=20.9"), raises=False) - @get("/v1/feature-profile/sdwan/system/{parcel_type}/schema", resp_json_key="request") - def get_schema(self, parcel_type: str, params: SchemaTypeQuery) -> JSON: + @get("/v1/feature-profile/sdwan/system/aaa/schema", resp_json_key="request") + def get_sdwan_system_aaa_parcel_schema(self, params: SchemaTypeQuery) -> JSON: ... @versions(supported_versions=(">=20.9"), raises=False) @@ -40,46 +37,16 @@ def create_sdwan_system_feature_profile( ... @versions(supported_versions=(">=20.9"), raises=False) - @delete("/v1/feature-profile/sdwan/system/{profile_id}") - def delete_sdwan_system_feature_profile(self, profile_id: UUID) -> None: + @delete("/v1/feature-profile/sdwan/system/{system_id}") + def delete_sdwan_system_feature_profile(self, system_id: str) -> None: ... @versions(supported_versions=(">=20.9"), raises=False) - @post("/v1/feature-profile/sdwan/system/{profile_id}/aaa") - def create_aaa_profile_parcel_for_system(self, profile_id: UUID, payload: _ParcelBase) -> ParcelId: + @post("/v1/feature-profile/sdwan/system/{system_id}/aaa") + def create_aaa_profile_parcel_for_system(self, system_id: str, payload: _ParcelBase) -> ParcelId: ... @versions(supported_versions=(">=20.9"), raises=False) - @put("/v1/feature-profile/sdwan/system/{profile_id}/aaa/{parcel_id}") - def edit_aaa_profile_parcel_for_system(self, profile_id: UUID, parcel_id: UUID, payload: _ParcelBase) -> ParcelId: - ... - - @versions(supported_versions=(">=20.9"), raises=False) - @get("/v1/feature-profile/sdwan/system/{profile_id}/{parcel_type}") - def get_all(self, profile_id: UUID, parcel_type: UUID) -> DataSequence[Parcel]: - ... - - @versions(supported_versions=(">=20.9"), raises=False) - @get("/v1/feature-profile/sdwan/system/{profile_id}/{parcel_type}/{parcel_id}") - def get_by_id(self, profile_id: UUID, parcel_type: str, parcel_id: UUID) -> Parcel: - ... - - @versions(supported_versions=(">=20.9"), raises=False) - @put("/v1/feature-profile/sdwan/system/{profile_id}/{parcel_type}/{parcel_id}") - def update(self, profile_id: UUID, parcel_type: str, parcel_id: UUID, payload: AnySystemParcel) -> ParcelId: - ... - - @versions(supported_versions=(">=20.9"), raises=False) - @delete("/v1/feature-profile/sdwan/system/{profile_id}/{parcel_type}/{parcel_id}") - def delete(self, profile_id: UUID, parcel_type: str, parcel_id: UUID) -> None: - ... - - @versions(supported_versions=(">=20.9"), raises=False) - @post("/v1/feature-profile/sdwan/system/{system_id}/bfd") - def create_bfd_profile_parcel_for_system(self, system_id: str, payload: _ParcelBase) -> ParcelId: - ... - - @versions(supported_versions=(">=20.9"), raises=False) - @post("/v1/feature-profile/sdwan/system/{profile_id}/{parcel_type}") - def create(self, profile_id: UUID, parcel_type: str, payload: AnySystemParcel) -> ParcelId: + @put("/v1/feature-profile/sdwan/system/{system_id}/aaa/{parcel_id}") + def edit_aaa_profile_parcel_for_system(self, system_id: str, parcel_id: str, payload: _ParcelBase) -> ParcelId: ... diff --git a/catalystwan/endpoints/configuration_group.py b/catalystwan/endpoints/configuration_group.py index b84676396..ef4993042 100644 --- a/catalystwan/endpoints/configuration_group.py +++ b/catalystwan/endpoints/configuration_group.py @@ -3,9 +3,8 @@ # mypy: disable-error-code="empty-body" from datetime import datetime from typing import List, Optional -from uuid import UUID -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from catalystwan.endpoints import APIEndpoints, delete, get, post, put, versions from catalystwan.models.configuration.common import Solution @@ -14,7 +13,7 @@ class ProfileId(BaseModel): - id: UUID + id: str # TODO Get mode from schema @@ -38,24 +37,10 @@ class FeatureProfile(BaseModel): class ConfigGroup(BaseModel): - id: UUID name: str description: Optional[str] solution: Solution profiles: Optional[List[FeatureProfile]] - source: Optional[str] = None - state: Optional[str] = None - devices: List = Field(default=[]) - created_by: Optional[str] = Field(alias="createdBy") - last_updated_by: Optional[str] = Field(alias="lastUpdatedBy") - created_on: Optional[datetime] = Field(alias="createdOn") - last_updated_on: Optional[datetime] = Field(alias="lastUpdatedOn") - version: int - number_of_devices: int = Field(alias="numberOfDevices") - number_of_devices_up_to_date: int = Field(alias="numberOfDevicesUpToDate") - origin: Optional[str] - topology: Optional[str] = None - full_config_cli: bool = Field(alias="fullConfigCli") class ConfigGroupResponsePayload(BaseModel): @@ -121,7 +106,7 @@ class ConfigGroupDisassociateResponse(BaseModel): class ConfigGroupCreationResponse(BaseModel): - id: UUID + id: str class EditedProfileId(BaseModel): diff --git a/catalystwan/integration_tests/feature_profile/sdwan/system/test_models.py b/catalystwan/integration_tests/feature_profile/sdwan/system/test_models.py deleted file mode 100644 index 9bc5aad62..000000000 --- a/catalystwan/integration_tests/feature_profile/sdwan/system/test_models.py +++ /dev/null @@ -1,217 +0,0 @@ -import os -import unittest -from typing import cast - -from catalystwan.models.configuration.feature_profile.sdwan.system import ( - BannerParcel, - BasicParcel, - BFDParcel, - GlobalParcel, - LoggingParcel, - MRFParcel, - NTPParcel, - OMPParcel, - SecurityParcel, - SNMPParcel, -) -from catalystwan.session import create_manager_session - - -class TestSystemFeatureProfileModels(unittest.TestCase): - def setUp(self) -> None: - self.session = create_manager_session( - url=cast(str, os.environ.get("TEST_VMANAGE_URL")), - port=cast(int, int(os.environ.get("TEST_VMANAGE_PORT"))), # type: ignore - username=cast(str, os.environ.get("TEST_VMANAGE_USERNAME")), - password=cast(str, os.environ.get("TEST_VMANAGE_PASSWORD")), - ) - self.profile_id = self.session.api.sdwan_feature_profiles.system.create_profile("TestProfile", "Description").id - - def test_when_default_values_banner_parcel_expect_successful_post(self): - # Arrange - banner_parcel = BannerParcel( - parcel_name="BannerDefault", - parcel_description="Banner Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, banner_parcel).id - # Assert - assert parcel_id - - def test_when_fully_specified_banner_parcel_expect_successful_post(self): - # Arrange - banner_parcel = BannerParcel( - parcel_name="BannerFullySpecified", - parcel_description="Banner Parcel", - ) - banner_parcel.add_login("Login") - banner_parcel.add_motd("Hello! Welcome to the network!") - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, banner_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_logging_parcel_expect_successful_post(self): - # Arrange - logging_parcel = LoggingParcel( - parcel_name="LoggingDefault", - parcel_description="Logging Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, logging_parcel).id - # Assert - assert parcel_id - - def test_when_fully_specified_logging_parcel_expect_successful_post(self): - # Arrange - logging_parcel = LoggingParcel( - parcel_name="LoggingFullySpecified", - parcel_description="Logging Parcel", - ) - logging_parcel.set_disk( - enable=True, - disk_file_rotate=10, - disk_file_size=10, - ) - logging_parcel.add_tls_profile( - profile="TLSProfile", - version="TLSv1.2", - ciphersuite_list=[ - "aes-256-cbc-sha", - "aes-128-cbc-sha", - "ecdhe-ecdsa-aes-gcm-sha2", - "ecdhe-rsa-aes-cbc-sha2", - ], - ) - logging_parcel.add_ipv4_server( - name="Server1", - vpn=0, - source_interface="fastethernet1/0", - priority="debugging", - enable_tls=True, - custom_profile=True, - profile_properties="TLSProfile", - ) - logging_parcel.add_ipv6_server( - name="Server2", - vpn=0, - source_interface="fastethernet1/1", - priority="debugging", - enable_tls=True, - custom_profile=True, - profile_properties="TLSProfile", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, logging_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_bfd_parcel_expect_successful_post(self): - # Arrange - bfd_parcel = BFDParcel( - parcel_name="BFDDefault", - parcel_description="BFD Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, bfd_parcel).id - # Assert - assert parcel_id - - def test_when_fully_specified_bfd_parcel_expect_successful_post(self): - # Arrange - bfd_parcel = BFDParcel( - parcel_name="BFDFullySpecified", - parcel_description="BFD Parcel", - ) - bfd_parcel.set_muliplier(1) - bfd_parcel.set_poll_interval(700000) - bfd_parcel.set_default_dscp(51) - bfd_parcel.add_color(color="lte", hello_interval=300000, multiplier=7, pmtu_discovery=False) - bfd_parcel.add_color(color="mpls", pmtu_discovery=False) - bfd_parcel.add_color(color="biz-internet") - bfd_parcel.add_color(color="public-internet") - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, bfd_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_basic_parcel_expect_successful_post(self): - # Arrange - basic_parcel = BasicParcel( - parcel_name="BasicDefault", - parcel_description="Basic Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, basic_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_security_parcel_expect_successful_post(self): - # Arrange - security_parcel = SecurityParcel( - parcel_name="SecurityDefault", - parcel_description="Security Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, security_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_ntp_parcel_expect_successful_post(self): - # Arrange - ntp_parcel = NTPParcel( - parcel_name="NTPDefault", - parcel_description="NTP Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, ntp_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_global_parcel_expect_successful_post(self): - # Arrange - global_parcel = GlobalParcel( - parcel_name="GlobalDefault", - parcel_description="Global Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, global_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_mrf_parcel_expect_successful_post(self): - # Arrange - mrf_parcel = MRFParcel( - parcel_name="MRFDefault", - parcel_description="MRF Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, mrf_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_snmp_parcel_expect_successful_post(self): - # Arrange - snmp_parcel = SNMPParcel( - parcel_name="SNMPDefault", - parcel_description="SNMP Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, snmp_parcel).id - # Assert - assert parcel_id - - def test_when_default_values_omp_parcel_expect_successful_post(self): - # Arrange - omp_parcel = OMPParcel( - parcel_name="OMPDefault", - parcel_description="OMP Parcel", - ) - # Act - parcel_id = self.session.api.sdwan_feature_profiles.system.create(self.profile_id, omp_parcel).id - # Assert - assert parcel_id - - def tearDown(self) -> None: - self.session.api.sdwan_feature_profiles.system.delete_profile(self.profile_id) - self.session.close() diff --git a/catalystwan/integration_tests/test_config_migration.py b/catalystwan/integration_tests/test_config_migration.py deleted file mode 100644 index 4deac3636..000000000 --- a/catalystwan/integration_tests/test_config_migration.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import unittest -from typing import cast - -from catalystwan.session import create_manager_session -from catalystwan.workflows.config_migration import collect_ux1_config, transform - - -class TestConfigMigration(unittest.TestCase): - def setUp(self) -> None: - self.session = create_manager_session( - url=cast(str, os.environ.get("TEST_VMANAGE_URL")), - port=cast(int, int(os.environ.get("TEST_VMANAGE_PORT"))), # type: ignore - username=cast(str, os.environ.get("TEST_VMANAGE_USERNAME")), - password=cast(str, os.environ.get("TEST_VMANAGE_PASSWORD")), - ) - - def test_config_migration(self): - ux1_config = collect_ux1_config(self.session) - ux2_config = transform(ux1_config) - # push_ux2_config(self.session, ux2_config) - # This section will include the Feature profiles creation - # and pushing the configuration to the vManage - assert ux2_config - - def tearDown(self) -> None: - # This section will include the Feature profiles deletion - self.session.close() diff --git a/catalystwan/models/common.py b/catalystwan/models/common.py index cd97b740e..481886f4a 100644 --- a/catalystwan/models/common.py +++ b/catalystwan/models/common.py @@ -1,11 +1,6 @@ # Copyright 2023 Cisco Systems, Inc. and its affiliates -from typing import Dict, List, Literal, Sequence, Set, Tuple, Union -from uuid import UUID - -from pydantic import PlainSerializer -from pydantic.functional_validators import BeforeValidator -from typing_extensions import Annotated +from typing import Dict, List, Literal, Set, Tuple def check_fields_exclusive(values: Dict, field_names: Set[str], at_least_one: bool = False) -> bool: @@ -53,25 +48,6 @@ def check_any_of_exclusive_field_sets(values: Dict, field_sets: List[Tuple[Set[s raise ValueError(f"One of {all_sets_field_names} must be assigned") -IntStr = Annotated[ - int, - PlainSerializer(lambda x: str(x), return_type=str, when_used="json-unless-none"), - BeforeValidator(lambda x: int(x)), -] - - -def str_as_uuid_list(val: Union[str, Sequence[UUID]]) -> Sequence[UUID]: - if isinstance(val, str): - return [UUID(uuid_) for uuid_ in val.split()] - return val - - -def str_as_str_list(val: Union[str, Sequence[str]]) -> Sequence[str]: - if isinstance(val, str): - return [s for s in val.split()] - return val - - InterfaceType = Literal[ "Ethernet", "FastEthernet", @@ -140,7 +116,3 @@ def str_as_str_list(val: Union[str, Sequence[str]]) -> Sequence[str]: "SC15", "SC16", ] - -ICMPMessageType = Literal[ - "echo", "echo-reply", "unreachable", "net-unreachable", "host-unreachable", "protocol-unreachable" -] diff --git a/catalystwan/models/configuration/config_migration.py b/catalystwan/models/configuration/config_migration.py index f68558d98..631b4b697 100644 --- a/catalystwan/models/configuration/config_migration.py +++ b/catalystwan/models/configuration/config_migration.py @@ -1,16 +1,9 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal, Union +from typing import List -from pydantic import BaseModel, ConfigDict, Field -from typing_extensions import Annotated +from pydantic import BaseModel, Field -from catalystwan.api.template_api import DeviceTemplateInformation, FeatureTemplateInformation -from catalystwan.endpoints.configuration_group import ConfigGroup -from catalystwan.models.configuration.feature_profile.common import FeatureProfileCreationPayload -from catalystwan.models.configuration.feature_profile.sdwan.policy_object import AnyPolicyObjectParcel -from catalystwan.models.configuration.feature_profile.sdwan.system import AnySystemParcel -from catalystwan.models.configuration.topology_group import TopologyGroup from catalystwan.models.policy import ( AnyPolicyDefinition, AnyPolicyList, @@ -19,73 +12,25 @@ SecurityPolicy, ) -AnyParcel = Annotated[ - Union[ - AnySystemParcel, - AnyPolicyObjectParcel, - ], - Field(discriminator="type_"), -] - class UX1Policies(BaseModel): - model_config = ConfigDict(populate_by_name=True) - centralized_policies: List[CentralizedPolicy] = Field( - default=[], serialization_alias="centralizedPolicies", validation_alias="centralizedPolicies" - ) - localized_policies: List[LocalizedPolicy] = Field( - default=[], serialization_alias="localizedPolicies", validation_alias="localizedPolicies" - ) - security_policies: List[SecurityPolicy] = Field( - default=[], serialization_alias="securityPolicies", validation_alias="securityPolicies" - ) - policy_definitions: List[AnyPolicyDefinition] = Field( - default=[], serialization_alias="policyDefinitions", validation_alias="policyDefinitions" - ) - policy_lists: List[AnyPolicyList] = Field( - default=[], serialization_alias="policyLists", validation_alias="policyLists" - ) + centralized_policies: List[CentralizedPolicy] = Field(default=[], serialization_alias="centralizedPolicies") + localized_policies: List[LocalizedPolicy] = Field(default=[], serialization_alias="localizedPolicies") + security_policies: List[SecurityPolicy] = Field(default=[], serialization_alias="securityPolicies") + policy_definitions: List[AnyPolicyDefinition] = Field(default=[], serialization_alias="policyDefinitions") + policy_lists: List[AnyPolicyList] = Field(default=[], serialization_alias="policyLists") class UX1Templates(BaseModel): - feature_templates: List[FeatureTemplateInformation] = Field( - default=[], serialization_alias="featureTemplates", validation_alias="featureTemplates" - ) - device_templates: List[DeviceTemplateInformation] = Field( - default=[], serialization_alias="deviceTemplates", validation_alias="deviceTemplates" - ) - - -class ConfigGroupPreset(BaseModel): - config_group_name: str = Field(serialization_alias="name", validation_alias="name") - solution: Literal["sdwan"] = "sdwan" - profile_parcels: List[AnyParcel] = Field( - default=[], serialization_alias="profileParcels", validation_alias="profileParcels" - ) + pass class UX1Config(BaseModel): # All UX1 Configuration items - Mega Model - model_config = ConfigDict(populate_by_name=True) - policies: UX1Policies = UX1Policies() - templates: UX1Templates = UX1Templates() + policies: UX1Policies + templates: UX1Templates class UX2Config(BaseModel): # All UX2 Configuration items - Mega Model - model_config = ConfigDict(populate_by_name=True) - topology_groups: List[TopologyGroup] = Field( - default=[], serialization_alias="topologyGroups", validation_alias="topologyGroups" - ) - config_groups: List[ConfigGroup] = Field( - default=[], serialization_alias="configurationGroups", validation_alias="configurationGroups" - ) - policy_groups: List[ConfigGroup] = Field( - default=[], serialization_alias="policyGroups", validation_alias="policyGroups" - ) - feature_profiles: List[FeatureProfileCreationPayload] = Field( - default=[], serialization_alias="featureProfiles", validation_alias="featureProfiles" - ) - profile_parcels: List[AnyParcel] = Field( - default=[], serialization_alias="profileParcels", validation_alias="profileParcels" - ) + pass diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/__init__.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/__init__.py index bcf0d6bf2..38cdb0798 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/__init__.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/__init__.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Union +from typing import List, Mapping, Union from pydantic import Field from typing_extensions import Annotated @@ -34,44 +34,65 @@ from .security.url import BaseURLListEntry, URLAllowParcel, URLBlockParcel from .security.zone import SecurityZoneListEntry, SecurityZoneListParcel -AnyURLParcel = Annotated[ - Union[ - URLAllowParcel, - URLBlockParcel, - ], - Field(discriminator="parcel_type"), -] - AnyPolicyObjectParcel = Annotated[ Union[ - # AnyURLParcel, - ApplicationListParcel, AppProbeParcel, + ApplicationListParcel, ColorParcel, DataPrefixParcel, ExpandedCommunityParcel, FowardingClassParcel, - FQDNDomainParcel, - GeoLocationListParcel, - IPSSignatureParcel, IPv6DataPrefixParcel, IPv6PrefixListParcel, - LocalDomainParcel, + PrefixListParcel, PolicierParcel, PreferredColorGroupParcel, - PrefixListParcel, + SLAClassParcel, + TlocParcel, + StandardCommunityParcel, + LocalDomainParcel, + FQDNDomainParcel, + IPSSignatureParcel, + URLAllowParcel, + URLBlockParcel, + SecurityPortParcel, ProtocolListParcel, + GeoLocationListParcel, + SecurityZoneListParcel, SecurityApplicationListParcel, SecurityDataPrefixParcel, - SecurityPortParcel, - SecurityZoneListParcel, - SLAClassParcel, - StandardCommunityParcel, - TlocParcel, ], - Field(discriminator="type_"), + Field(discriminator="type"), ] +POLICY_OBJECT_PAYLOAD_ENDPOINT_MAPPING: Mapping[type, str] = { + AppProbeParcel: "app-probe", + ApplicationListParcel: "app-list", + ColorParcel: "color", + DataPrefixParcel: "data-prefix", + ExpandedCommunityParcel: "expanded-community", + FowardingClassParcel: "class", + IPv6DataPrefixParcel: "data-ipv6-prefix", + IPv6PrefixListParcel: "ipv6-prefix", + PrefixListParcel: "prefix", + PolicierParcel: "policer", + PreferredColorGroupParcel: "preferred-color-group", + SLAClassParcel: "sla-class", + TlocParcel: "tloc", + StandardCommunityParcel: "standard-community", + LocalDomainParcel: "security-localdomain", + FQDNDomainParcel: "security-fqdn", + IPSSignatureParcel: "security-ipssignature", + URLAllowParcel: "security-urllist", + URLBlockParcel: "security-urllist", + SecurityPortParcel: "security-port", + ProtocolListParcel: "security-protocolname", + GeoLocationListParcel: "security-geolocation", + SecurityZoneListParcel: "security-zone", + SecurityApplicationListParcel: "security-localapp", + SecurityDataPrefixParcel: "security-data-ip-prefix", +} + __all__ = ( "AnyPolicyObjectParcel", "ApplicationFamilyListEntry", diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/app_probe.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/app_probe.py index 1f358e9af..b889d648d 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/app_probe.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/app_probe.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator @@ -28,7 +28,6 @@ class AppProbeEntry(BaseModel): class AppProbeParcel(_ParcelBase): - type_: Literal["app-probe"] = Field(default="app-probe", exclude=True) entries: List[AppProbeEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_fowarding_class(self, forwarding_class_name: str): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/application_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/application_list.py index 355f53168..85f1c703e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/application_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/application_list.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal, Union +from typing import List, Union from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -18,7 +18,6 @@ class ApplicationFamilyListEntry(BaseModel): class ApplicationListParcel(_ParcelBase): - type_: Literal["app-list"] = Field(default="app-list", exclude=True) entries: List[Union[ApplicationListEntry, ApplicationFamilyListEntry]] = Field( default=[], validation_alias=AliasPath("data", "entries") ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/color_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/color_list.py index ae0e7bbdd..a988881de 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/color_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/color_list.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, Field @@ -13,7 +13,6 @@ class ColorEntry(BaseModel): class ColorParcel(_ParcelBase): - type_: Literal["color"] = Field(default="color", exclude=True) entries: List[ColorEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_color(self, color: TLOCColor): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/data_prefix.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/data_prefix.py index dadc1ce9b..dfe8ec6f0 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/data_prefix.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/data_prefix.py @@ -1,7 +1,7 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv4Address, IPv4Network -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -13,17 +13,14 @@ class DataPrefixEntry(BaseModel): ipv4_address: Global[IPv4Address] = Field(serialization_alias="ipv4Address", validation_alias="ipv4Address") ipv4_prefix_length: Global[int] = Field(serialization_alias="ipv4PrefixLength", validation_alias="ipv4PrefixLength") - @staticmethod - def from_ipv4_network(ipv4_network: IPv4Network) -> "DataPrefixEntry": - return DataPrefixEntry( - ipv4_address=as_global(ipv4_network.network_address), - ipv4_prefix_length=as_global(ipv4_network.prefixlen), - ) - class DataPrefixParcel(_ParcelBase): - type_: Literal["data-prefix"] = Field(default="data-prefix", exclude=True) entries: List[DataPrefixEntry] = Field(default_factory=list, validation_alias=AliasPath("data", "entries")) def add_data_prefix(self, ipv4_network: IPv4Network): - self.entries.append(DataPrefixEntry.from_ipv4_network(ipv4_network)) + self.entries.append( + DataPrefixEntry( + ipv4_address=as_global(ipv4_network.network_address), + ipv4_prefix_length=as_global(ipv4_network.prefixlen), + ) + ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/expanded_community_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/expanded_community_list.py index 01edff737..0fd79245e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/expanded_community_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/expanded_community_list.py @@ -1,14 +1,11 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import Literal - from pydantic import AliasPath, ConfigDict, Field, field_validator from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global class ExpandedCommunityParcel(_ParcelBase): - type_: Literal["expanded-community"] = Field(default="expanded-community", exclude=True) model_config = ConfigDict(populate_by_name=True) expandedCommunityList: Global[list] = Field( default=as_global([]), diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/fowarding_class.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/fowarding_class.py index 428a18063..58ef5040d 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/fowarding_class.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/fowarding_class.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, Field, field_validator @@ -18,7 +18,6 @@ def check_burst(cls, queue: Global): class FowardingClassParcel(_ParcelBase): - type_: Literal["class"] = Field(default="class", exclude=True) entries: List[FowardingClassQueueEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_queue(self, queue: int): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_data_prefix.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_data_prefix.py index 877572950..861edfe19 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_data_prefix.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_data_prefix.py @@ -1,7 +1,7 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv6Address, IPv6Network -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -15,7 +15,6 @@ class IPv6DataPrefixEntry(BaseModel): class IPv6DataPrefixParcel(_ParcelBase): - type_: Literal["data-ipv6-prefix"] = Field(default="data-ipv6-prefix", exclude=True) entries: List[IPv6DataPrefixEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_prefix(self, ipv6_network: IPv6Network): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_prefix_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_prefix_list.py index 7608ef2be..d01dd457e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_prefix_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/ipv6_prefix_list.py @@ -1,7 +1,7 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv6Address, IPv6Network -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -15,7 +15,6 @@ class IPv6PrefixListEntry(BaseModel): class IPv6PrefixListParcel(_ParcelBase): - type_: Literal["ipv6-prefix"] = Field(default="ipv6-prefix", exclude=True) entries: List[IPv6PrefixListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_prefix(self, ipv6_network: IPv6Network): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/policier.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/policier.py index d375446f5..0eac93e92 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/policier.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/policier.py @@ -1,15 +1,11 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global - -PolicerExceedAction = Literal[ - "drop", - "remark", -] +from catalystwan.models.policy.lists_entries import PolicerExceedAction class PolicierEntry(BaseModel): @@ -32,7 +28,6 @@ def check_rate(cls, rate_str: Global): class PolicierParcel(_ParcelBase): - type_: Literal["policer"] = Field(default="policer", exclude=True) entries: List[PolicierEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_entry(self, burst: int, exceed: PolicerExceedAction, rate: int): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefered_group_color.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefered_group_color.py index ad725de65..8ea933c05 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefered_group_color.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefered_group_color.py @@ -1,17 +1,12 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal, Optional +from typing import List, Optional from pydantic import AliasPath, BaseModel, ConfigDict, Field, model_validator from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global from catalystwan.models.common import TLOCColor - -PathPreference = Literal[ - "direct-path", - "multi-hop-path", - "all-paths", -] +from catalystwan.models.policy.lists_entries import PathPreference class Preference(BaseModel): @@ -42,7 +37,6 @@ def check_passwords_match(self) -> "PreferredColorGroupEntry": class PreferredColorGroupParcel(_ParcelBase): - type_: Literal["preferred-color-group"] = Field(default="preferred-color-group", exclude=True) entries: List[PreferredColorGroupEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_primary(self, color_preference: List[TLOCColor], path_preference: PathPreference): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefix_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefix_list.py index 14fd01dfd..ba05f99ce 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefix_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/prefix_list.py @@ -1,7 +1,7 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv4Address, IPv4Network -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -15,7 +15,6 @@ class PrefixListEntry(BaseModel): class PrefixListParcel(_ParcelBase): - type_: Literal["prefix"] = Field(default="prefix", exclude=True) entries: List[PrefixListEntry] = Field(default_factory=list, validation_alias=AliasPath("data", "entries")) def add_prefix(self, ipv4_network: IPv4Network): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/sla_class.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/sla_class.py index d4056f851..e40f79abf 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/sla_class.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/sla_class.py @@ -115,7 +115,6 @@ class SLAClassListEntry(BaseModel): class SLAClassParcel(_ParcelBase): - type_: Literal["sla-class"] = Field(default="sla-class", exclude=True) entries: List[SLAClassListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_entry( diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/standard_community.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/standard_community.py index ae7d1f72e..f42eefa66 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/standard_community.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/standard_community.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -16,7 +16,6 @@ class StandardCommunityEntry(BaseModel): class StandardCommunityParcel(_ParcelBase): - type_: Literal["standard-community"] = Field(default="standard-community", exclude=True) entries: List[StandardCommunityEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_community(self, standard_community: WellKnownBGPCommunities): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/tloc_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/tloc_list.py index b608e8151..8ba90518d 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/tloc_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/policy/tloc_list.py @@ -1,17 +1,13 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv4Address -from typing import List, Literal, Optional +from typing import List, Optional from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global from catalystwan.models.common import TLOCColor - -EncapType = Literal[ - "ipsec", - "gre", -] +from catalystwan.models.policy.lists_entries import EncapType class TlocEntry(BaseModel): @@ -32,7 +28,6 @@ def ensure_correct_preference_value(cls, v: Global): class TlocParcel(_ParcelBase): - type_: Literal["tloc"] = Field(default="tloc", exclude=True) entries: List[TlocEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_entry( diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/application_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/application_list.py index 49eee2a03..ad242e1c8 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/application_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/application_list.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal, Union +from typing import List, Union from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -18,7 +18,6 @@ class SecurityApplicationFamilyListEntry(BaseModel): class SecurityApplicationListParcel(_ParcelBase): - type_: Literal["security-localapp"] = Field(default="security-localapp", exclude=True) entries: List[Union[SecurityApplicationFamilyListEntry, SecurityApplicationListEntry]] = Field( default=[], validation_alias=AliasPath("data", "entries") ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/data_prefix.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/data_prefix.py index 5f5fab006..3d93c80d1 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/data_prefix.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/data_prefix.py @@ -1,7 +1,7 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv4Network -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -14,7 +14,6 @@ class SecurityDataPrefixEntry(BaseModel): class SecurityDataPrefixParcel(_ParcelBase): - type_: Literal["security-data-ip-prefix"] = Field(default="security-data-ip-prefix", exclude=True) entries: List[SecurityDataPrefixEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_prefix(self, ip_prefix: IPv4Network): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/fqdn.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/fqdn.py index aaf0b817e..3de298b3f 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/fqdn.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/fqdn.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -15,7 +15,6 @@ class FQDNListEntry(BaseModel): class FQDNDomainParcel(_ParcelBase): - type_: Literal["security-fqdn"] = Field(default="security-fqdn", exclude=True) entries: List[FQDNListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def from_fqdns(self, fqdns: List[str]): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/geolocation_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/geolocation_list.py index 95a59fc8b..81e6b8a35 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/geolocation_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/geolocation_list.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal, Optional +from typing import List, Optional from pydantic import AliasPath, BaseModel, Field, model_validator @@ -21,7 +21,6 @@ def check_country_xor_continent(self): class GeoLocationListParcel(_ParcelBase): - type_: Literal["security-geolocation"] = Field(default="security-geolocation", exclude=True) entries: List[GeoLocationListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_country(self, country: str): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/ips_signature.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/ips_signature.py index 9d4f5bd6b..06ae65d0e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/ips_signature.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/ips_signature.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator @@ -30,7 +30,6 @@ def check_signature_id(cls, signature_id: Global): class IPSSignatureParcel(_ParcelBase): - type_: Literal["security-ipssignature"] = Field(default="security-ipssignature", exclude=True) entries: List[IPSSignatureListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_signature(self, signature: str): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/local_domain.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/local_domain.py index 27b2e9311..5a53a8f42 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/local_domain.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/local_domain.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -15,7 +15,6 @@ class LocalDomainListEntry(BaseModel): class LocalDomainParcel(_ParcelBase): - type_: Literal["security-localdomain"] = Field(default="security-localdomain", exclude=True) entries: List[LocalDomainListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def from_local_domains(self, domains: List[str]): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/protocol_list.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/protocol_list.py index 179034c53..fccc5698b 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/protocol_list.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/protocol_list.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -13,7 +13,6 @@ class ProtocolListEntry(BaseModel): class ProtocolListParcel(_ParcelBase): - type_: Literal["security-protocolname"] = Field(default="security-protocolname", exclude=True) entries: List[ProtocolListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_protocol(self, protocol: str): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/security_port.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/security_port.py index a06147eb0..346b68149 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/security_port.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/security_port.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field, field_validator @@ -29,7 +29,6 @@ def check_port(cls, port: Global[str]): class SecurityPortParcel(_ParcelBase): - type_: Literal["security-port"] = Field(default="security-port", exclude=True) entries: List[SecurityPortListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_port(self, port: str): diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url.py index 5371c6f9e..b16eb0a1e 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/url.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal +from typing import List from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -13,7 +13,6 @@ class BaseURLListEntry(BaseModel): class BaseURLParcel(_ParcelBase): - type_: Literal["security-urllist"] = Field(default="security-urllist", exclude=True) entries: List[BaseURLListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_url(self, pattern: str): @@ -21,14 +20,8 @@ def add_url(self, pattern: str): class URLAllowParcel(BaseURLParcel): - type_: Literal["security-urllist"] = Field(default="security-urllist", exclude=True) - parcel_type: Literal["urlallowed"] = Field( - default="urlallowed", validation_alias="type", serialization_alias="type" - ) + parcel_type: str = Field(default="urlallowed", validation_alias="type", serialization_alias="type") class URLBlockParcel(BaseURLParcel): - type_: Literal["security-urllist"] = Field(default="security-urllist", exclude=True) - parcel_type: Literal["urlblocked"] = Field( - default="urlblocked", validation_alias="type", serialization_alias="type" - ) + parcel_type: str = Field(default="urlblocked", validation_alias="type", serialization_alias="type") diff --git a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/zone.py b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/zone.py index 5af30f521..33b7f5f91 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/zone.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/policy_object/security/zone.py @@ -1,6 +1,6 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates -from typing import List, Literal, Optional +from typing import List, Optional from pydantic import AliasPath, BaseModel, Field, field_validator, model_validator @@ -25,7 +25,6 @@ def check_vpn_xor_interface(self): class SecurityZoneListParcel(_ParcelBase): - type_: Literal["security-zone"] = Field(default="security-zone", exclude=True) entries: List[SecurityZoneListEntry] = Field(default=[], validation_alias=AliasPath("data", "entries")) def add_interface(self, interface: InterfaceType): @@ -38,6 +37,6 @@ def add_interface(self, interface: InterfaceType): def add_vpn(self, vpn: str): self.entries.append( SecurityZoneListEntry( - vpn=as_global(vpn), + vpn=as_global(vpn, InterfaceType), ) ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/__init__.py b/catalystwan/models/configuration/feature_profile/sdwan/system/__init__.py deleted file mode 100644 index b4edc5b3d..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -from typing import List, Union - -from pydantic import Field -from typing_extensions import Annotated - -from .aaa import AAAParcel -from .banner import BannerParcel -from .basic import BasicParcel -from .bfd import BFDParcel -from .global_parcel import GlobalParcel -from .logging_parcel import LoggingParcel -from .mrf import MRFParcel -from .ntp import NTPParcel -from .omp import OMPParcel -from .security import SecurityParcel -from .snmp import SNMPParcel - -AnySystemParcel = Annotated[ - Union[ - AAAParcel, - BFDParcel, - LoggingParcel, - BannerParcel, - BasicParcel, - GlobalParcel, - NTPParcel, - MRFParcel, - OMPParcel, - SecurityParcel, - SNMPParcel, - ], - Field(discriminator="type_"), -] - -__all__ = [ - "AAAParcel", - "BFDParcel", - "LoggingParcel", - "BannerParcel", - "BasicParcel", - "GlobalParcel", - "NTPParcel", - "MRFParcel", - "OMPParcel", - "SecurityParcel", - "SNMPParcel", - "AnySystemParcel", -] - - -def __dir__() -> "List[str]": - return list(__all__) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/aaa.py b/catalystwan/models/configuration/feature_profile/sdwan/system/aaa.py index 476bae9a6..07515f001 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/aaa.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/system/aaa.py @@ -1,7 +1,7 @@ # Copyright 2024 Cisco Systems, Inc. and its affiliates from ipaddress import IPv4Address, IPv6Address -from typing import List, Literal, Optional, Union +from typing import List, Optional, Union from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -9,7 +9,9 @@ class PubkeyChainItem(BaseModel): - model_config = ConfigDict(extra="forbid", populate_by_name=True) + model_config = ConfigDict( + extra="forbid", + ) key_string: Global[str] = Field( validation_alias="keyString", serialization_alias="keyString", @@ -27,7 +29,7 @@ class PubkeyChainItem(BaseModel): class UserItem(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict(extra="forbid", populate_by_name=True) name: Union[Global[str], Variable] = Field(description="Set the username") password: Union[Global[str], Variable] = Field( @@ -258,9 +260,7 @@ class AuthorizationRuleItem(BaseModel): ) -class AAAParcel(_ParcelBase): - type_: Literal["aaa"] = Field(default="aaa", exclude=True) - model_config = ConfigDict(extra="forbid", populate_by_name=True) +class AAA(_ParcelBase): authentication_group: Union[Variable, Global[bool], Default[bool]] = Field( default=as_default(False), validation_alias=AliasPath("data", "authenticationGroup"), @@ -278,9 +278,7 @@ class AAAParcel(_ParcelBase): max_length=4, description="ServerGroups priority order", ) - user: Optional[List[UserItem]] = Field( - default=None, validation_alias=AliasPath("data", "user"), description="Create local login account", min_length=1 - ) + user: Optional[List[UserItem]] = Field(default=None, description="Create local login account", min_length=1) radius: Optional[List[Radius]] = Field( default=None, validation_alias=AliasPath("data", "radius"), description="Configure the Radius serverGroup" ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/banner.py b/catalystwan/models/configuration/feature_profile/sdwan/system/banner.py deleted file mode 100644 index 0bdbcd681..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/banner.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Union - -from pydantic import AliasPath, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default, as_global - -EmptyString = Literal[""] - - -class BannerParcel(_ParcelBase): - type_: Literal["banner"] = Field(default="banner", exclude=True) - - model_config = ConfigDict(extra="forbid", populate_by_name=True) - login: Union[Variable, Global[str], Default[EmptyString], str] = Field( - default=as_default("", EmptyString), validation_alias=AliasPath("data", "login") - ) - motd: Union[Variable, Global[str], Default[EmptyString]] = Field( - default=as_default("", EmptyString), - validation_alias=AliasPath("data", "motd"), - description="Message of the day", - ) - - def add_login(self, value: str): - self.login = as_global(value) - - def add_motd(self, value: str): - self.motd = as_global(value) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/basic.py b/catalystwan/models/configuration/feature_profile/sdwan/system/basic.py deleted file mode 100644 index e840d5e73..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/basic.py +++ /dev/null @@ -1,289 +0,0 @@ -from __future__ import annotations - -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default -from catalystwan.utils.timezone import Timezone - -ConsoleBaudRate = Literal["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"] -DefaultConsoleBaudRate = Literal["9600"] -Epfr = Literal["disabled", "aggressive", "moderate", "conservative"] -DefaultEpfr = Literal["disabled"] -SiteType = Literal["type-1", "type-2", "type-3", "cloud", "branch", "br", "spoke"] -DefaultTimezone = Literal["UTC"] - - -class Clock(BaseModel): - timezone: Union[Variable, Global[Timezone], Default[DefaultTimezone]] = Field( - default=as_default("UTC", DefaultTimezone), description="Set the timezone" - ) - - -class MobileNumberItem(BaseModel): - number: Union[Global[str], Variable] = Field(..., description="Mobile number, ex: 1231234414") - - -class Sms(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - enable: Union[Global[bool], Default[bool]] = Field( - default=as_default(False), description="Enable device’s geo fencing SMS" - ) - mobile_number: Optional[List[MobileNumberItem]] = Field( - None, - serialization_alias="MobileNumber", - validation_alias="MobileNumber", - description="Set device’s geo fencing SMS phone number", - ) - - -class GeoFencing(BaseModel): - enable: Union[Global[bool], Default[bool]] = Field(default=as_default(False), description="Enable Geo fencing") - range: Union[Global[int], Variable, Default[int]] = Field( - default=as_default(100), description="Set the device’s geo fencing range" - ) - sms: Sms = Field(default_factory=Sms, description="Set device’s geo fencing SMS") # type: ignore - - -class GpsVariable(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - longitude: Union[Variable, Global[float], Default[None]] = Field( - default=Default[None](value=None), description="Set the device physical longitude" - ) - latitude: Union[Variable, Global[float], Default[None]] = Field( - default=Default[None](value=None), description="Set the device physical latitude" - ) - geo_fencing: GeoFencing = Field( - default_factory=GeoFencing, - serialization_alias="geoFencing", - validation_alias="geoFencing", - ) - - -class OnDemand(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - on_demand_enable: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="onDemandEnable", - validation_alias="onDemandEnable", - description="Enable or disable On-demand Tunnel", - ) - on_demand_idle_timeout: Union[ - Variable, - Global[int], - Default[int], - ] = Field( - default=as_default(10), - serialization_alias="onDemandIdleTimeout", - validation_alias="onDemandIdleTimeout", - description="Set the idle timeout for on-demand tunnels", - ) - - -class AffinityPerVrfItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - affinity_group_number: Union[ - Variable, - Global[int], - Default[None], - ] = Field( - default=Default[None](value=None), - serialization_alias="affinityGroupNumber", - validation_alias="affinityGroupNumber", - description="Affinity Group Number", - ) - vrf_range: Union[Variable, Global[str], Default[None]] = Field( - default=Default[None](value=None), - serialization_alias="vrfRange", - validation_alias="vrfRange", - description="Range of VRFs", - ) - - -class BasicParcel(_ParcelBase): - type_: Literal["basic"] = Field(default="basic", exclude=True) - - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - clock: Clock = Field(default_factory=Clock, validation_alias=AliasPath("data", "clock")) - description: Union[Variable, Global[str], Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "description"), - description="Set a text description of the device", - ) - location: Union[Variable, Global[str], Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "location"), - description="Set the location of the device", - ) - gps_location: GpsVariable = Field( - default_factory=GpsVariable, - validation_alias=AliasPath("data", "gpsLocation"), - ) - device_groups: Union[Variable, Global[List[str]], Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "deviceGroups"), - description="Device groups", - ) - controller_group_list: Optional[ - Union[ - Variable, - Global[List[int]], - Default[None], - ] - ] = Field( - None, - validation_alias=AliasPath("data", "controllerGroupList"), - description="Configure a list of comma-separated controller groups", - ) - overlay_id: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(1), - validation_alias=AliasPath("data", "overlayId"), - description="Set the Overlay ID", - ) - port_offset: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(0), - validation_alias=AliasPath("data", "portOffset"), - description="Set the TLOC port offset when multiple devices are behind a NAT", - ) - port_hop: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - validation_alias=AliasPath("data", "portHop"), - description="Enable port hopping", - ) - control_session_pps: Optional[Union[Variable, Global[int], Default[int]]] = Field( - None, - validation_alias=AliasPath("data", "controlSessionPps"), - description="Set the policer rate for control sessions", - ) - track_transport: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - None, - validation_alias=AliasPath("data", "trackTransport"), - description="Configure tracking of transport", - ) - track_interface_tag: Optional[Union[Variable, Global[int], Default[None]]] = Field( - None, - validation_alias=AliasPath("data", "trackInterfaceTag"), - description="OMP Tag attached to routes based on interface tracking", - ) - console_baud_rate: Union[Variable, Global[ConsoleBaudRate], Default[DefaultConsoleBaudRate]] = Field( - default=as_default("9600", DefaultConsoleBaudRate), - validation_alias=AliasPath("data", "consoleBaudRate"), - description="Set the console baud rate", - ) - max_omp_sessions: Union[Variable, Global[int], Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "maxOmpSessions"), - description="Set the maximum number of OMP sessions <1..100> the device can have", - ) - multi_tenant: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - None, - validation_alias=AliasPath("data", "multiTenant"), - description="Device is multi-tenant", - ) - track_default_gateway: Optional[ - Union[ - Variable, - Global[bool], - Default[bool], - ] - ] = Field( - None, - validation_alias=AliasPath("data", "trackDefaultGateway"), - description="Enable or disable default gateway tracking", - ) - tracker_dia_stabilize_status: Optional[ - Union[ - Variable, - Global[bool], - Default[bool], - ] - ] = Field( - None, - validation_alias=AliasPath("data", "trackerDiaStabilizeStatus"), - description="Enable or disable endpoint tracker diaStabilize status", - ) - admin_tech_on_failure: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - validation_alias=AliasPath("data", "adminTechOnFailure"), - description="Collect admin-tech before reboot due to daemon failure", - ) - idle_timeout: Optional[Union[Variable, Global[int], Default[None]]] = Field( - None, - validation_alias=AliasPath("data", "idleTimeout"), - description="Idle CLI timeout in minutes", - ) - on_demand: OnDemand = Field( - default_factory=OnDemand, - validation_alias=AliasPath("data", "onDemand"), - ) - transport_gateway: Optional[Union[Global[bool], Variable, Default[bool]]] = Field( - None, - validation_alias=AliasPath("data", "transportGateway"), - description="Enable transport gateway", - ) - epfr: Optional[Union[Global[Epfr], Default[DefaultEpfr], Variable]] = Field( - None, - validation_alias=AliasPath("data", "epfr"), - description="Enable SLA Dampening and Enhanced App Routing.", - ) - site_type: Optional[Union[Variable, Global[List[SiteType]], Default[None]]] = Field( - None, - validation_alias=AliasPath("data", "siteType"), - description="Site Type", - ) - affinity_group_number: Optional[ - Union[ - Variable, - Global[int], - Default[None], - ] - ] = Field( - None, - validation_alias=AliasPath("data", "affinityGroupNumber"), - description="Affinity Group Number", - ) - affinity_group_preference: Optional[ - Union[ - Variable, - Global[List[int]], - Default[None], - ] - ] = Field( - None, - validation_alias=AliasPath("data", "affinityGroupPreference"), - description="Affinity Group Preference", - ) - affinity_preference_auto: Optional[ - Union[ - Variable, - Global[bool], - Default[bool], - ] - ] = Field( - None, - validation_alias=AliasPath("data", "affinityPreferenceAuto"), - description="Affinity Group Preference Auto", - ) - affinity_per_vrf: Optional[List[AffinityPerVrfItem]] = Field( - None, - validation_alias=AliasPath("data", "affinityPerVrf"), - description="Affinity Group Range for VRFs", - max_length=4, - min_length=0, - ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py b/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py deleted file mode 100644 index 0e47e66e0..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/bfd.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import List, Literal, Optional - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Global, _ParcelBase, as_global -from catalystwan.models.common import TLOCColor - - -class Color(BaseModel): - color: Global[TLOCColor] - hello_interval: Optional[Global[int]] = Field( - default=as_global(1000), validation_alias="helloInterval", serialization_alias="helloInterval" - ) - multiplier: Optional[Global[int]] = as_global(7) - pmtu_discovery: Optional[Global[bool]] = Field( - default=as_global(True), validation_alias="pmtuDiscovery", serialization_alias="pmtuDiscovery" - ) - dscp: Optional[Global[int]] = as_global(48) - model_config = ConfigDict(populate_by_name=True) - - -class BFDParcel(_ParcelBase): - type_: Literal["bfd"] = Field(default="bfd", exclude=True) - model_config = ConfigDict(arbitrary_types_allowed=True, populate_by_name=True) - - multiplier: Optional[Global[int]] = Field(default=as_global(6), validation_alias=AliasPath("data", "multiplier")) - poll_interval: Optional[Global[int]] = Field( - default=as_global(600000), - validation_alias=AliasPath("data", "pollInterval"), - description="Poll Interval (In Millisecond)", - ) - default_dscp: Optional[Global[int]] = Field( - default=as_global(48), - validation_alias=AliasPath("data", "defaultDscp"), - description="DSCP Values for BFD Packets (decimal)", - ) - colors: Optional[List[Color]] = Field(default=None, validation_alias=AliasPath("data", "colors")) - - def set_muliplier(self, value: int): - self.multiplier = as_global(value) - - def set_poll_interval(self, value: int): - self.poll_interval = as_global(value) - - def set_default_dscp(self, value: int): - self.default_dscp = as_global(value) - - def add_color( - self, - color: TLOCColor, - hello_interval: int = 1000, - multiplier: int = 7, - pmtu_discovery: bool = True, - dscp: int = 48, - ): - if not self.colors: - self.colors = [] - self.colors.append( - Color( - color=Global[TLOCColor](value=color), - hello_interval=as_global(hello_interval), - multiplier=as_global(multiplier), - pmtu_discovery=as_global(pmtu_discovery), - dscp=as_global(dscp), - ) - ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/global_parcel.py b/catalystwan/models/configuration/feature_profile/sdwan/system/global_parcel.py deleted file mode 100644 index 00b938335..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/global_parcel.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default - - -class ServicesIp(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - http_server: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpHttpServer", - validation_alias="servicesGlobalServicesIpHttpServer", - ) - https_server: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpHttpsServer", - validation_alias="servicesGlobalServicesIpHttpsServer", - ) - ftp_passive: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpFtpPassive", - validation_alias="servicesGlobalServicesIpFtpPassive", - ) - domain_lookup: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpDomainLookup", - validation_alias="servicesGlobalServicesIpDomainLookup", - ) - arp_proxy: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpArpProxy", - validation_alias="servicesGlobalServicesIpArpProxy", - ) - rcmd: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpRcmd", - validation_alias="servicesGlobalServicesIpRcmd", - ) - line_vty: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="servicesGlobalServicesIpLineVty", - validation_alias="servicesGlobalServicesIpLineVty", - ) - cdp: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - serialization_alias="servicesGlobalServicesIpCdp", - validation_alias="servicesGlobalServicesIpCdp", - ) - lldp: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - serialization_alias="servicesGlobalServicesIpLldp", - validation_alias="servicesGlobalServicesIpLldp", - ) - source_intrf: Union[Variable, Global[str], Default[None]] = Field( - default=Default[None](value=None), - serialization_alias="servicesGlobalServicesIpSourceIntrf", - validation_alias="servicesGlobalServicesIpSourceIntrf", - ) - tcp_keepalives_in: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - serialization_alias="globalOtherSettingsTcpKeepalivesIn", - validation_alias="globalOtherSettingsTcpKeepalivesIn", - ) - keepalives_out: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - serialization_alias="globalOtherSettingsTcpKeepalivesOut", - validation_alias="globalOtherSettingsTcpKeepalivesOut", - ) - small_servers: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="globalOtherSettingsTcpSmallServers", - validation_alias="globalOtherSettingsTcpSmallServers", - ) - udp_small_servers: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="globalOtherSettingsUdpSmallServers", - validation_alias="globalOtherSettingsUdpSmallServers", - ) - console_logging: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - serialization_alias="globalOtherSettingsConsoleLogging", - validation_alias="globalOtherSettingsConsoleLogging", - ) - ip_source_route: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="globalOtherSettingsIPSourceRoute", - validation_alias="globalOtherSettingsIPSourceRoute", - ) - vty_line_logging: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - serialization_alias="globalOtherSettingsVtyLineLogging", - validation_alias="globalOtherSettingsVtyLineLogging", - ) - snmp_ifindex_persist: (Union[Variable, Global[bool], Default[bool]]) = Field( - default=as_default(True), - serialization_alias="globalOtherSettingsSnmpIfindexPersist", - validation_alias="globalOtherSettingsSnmpIfindexPersist", - ) - ignore_bootp: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - serialization_alias="globalOtherSettingsIgnoreBootp", - validation_alias="globalOtherSettingsIgnoreBootp", - ) - nat64_udp_timeout: Union[Variable, Global[bool], Default[int]] = Field( - default=as_default(300), - serialization_alias="globalSettingsNat64UdpTimeout", - validation_alias="globalSettingsNat64UdpTimeout", - ) - nat64_tcp_timeout: Union[Variable, Global[bool], Default[int]] = Field( - default=as_default(3600), - serialization_alias="globalSettingsNat64TcpTimeout", - validation_alias="globalSettingsNat64TcpTimeout", - ) - http_authentication: Union[Variable, Global[bool], Default[None]] = Field( - default=Default[None](value=None), - serialization_alias="globalSettingsHttpAuthentication", - validation_alias="globalSettingsHttpAuthentication", - ) - ssh_version: Union[Variable, Global[bool], Default[None]] = Field( - default=Default[None](value=None), - serialization_alias="globalSettingsSSHVersion", - validation_alias="globalSettingsSSHVersion", - ) - - -class ServicesGlobal(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - services_ip: ServicesIp = Field(default_factory=ServicesIp) - - -class GlobalParcel(_ParcelBase): - type_: Literal["global"] = Field(default="global", exclude=True) - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - services_global: ServicesGlobal = Field( - default_factory=ServicesGlobal, validation_alias=AliasPath("data", "services_global") - ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py b/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py deleted file mode 100644 index a2f2b409b..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py +++ /dev/null @@ -1,159 +0,0 @@ -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, _ParcelBase, as_default, as_global - -Priority = Literal["information", "debugging", "notice", "warn", "error", "critical", "alert", "emergency"] -TlsVersion = Literal["TLSv1.1", "TLSv1.2"] -AuthType = Literal["Server", "Mutual"] -CypherSuite = Literal[ - "rsa-aes-cbc-sha2", - "rsa-aes-gcm-sha2", - "ecdhe-rsa-aes-gcm-sha2", - "aes-128-cbc-sha", - "aes-256-cbc-sha", - "dhe-aes-cbc-sha2", - "dhe-aes-gcm-sha2", - "ecdhe-ecdsa-aes-gcm-sha2", - "ecdhe-rsa-aes-cbc-sha2", -] - - -class TlsProfile(BaseModel): - profile: Global[str] - version: Union[Global[TlsVersion], Default[TlsVersion]] = Field( - default=as_default("TLSv1.1", TlsVersion), serialization_alias="tlsVersion", validation_alias="tlsVersion" - ) - auth_type: Default[AuthType] = Field( - default=as_default("Server", AuthType), serialization_alias="authType", validation_alias="authType" - ) # Value can't be changed in the UI - ciphersuite_list: Union[Global[List[CypherSuite]], Default[None]] = Field( - default=Default[None](value=None), serialization_alias="cipherSuiteList", validation_alias="cipherSuiteList" - ) - model_config = ConfigDict(populate_by_name=True) - - -class Server(BaseModel): - name: Global[str] - vpn: Union[Global[int], Default[int]] = Field(default=as_default(0)) - source_interface: Union[Global[str], Default[None]] = Field( - default=Default[None](value=None), serialization_alias="sourceInterface", validation_alias="sourceInterface" - ) - priority: Union[Global[Priority], Default[Priority]] = Field(default=as_default("information", Priority)) - enable_tls: Union[Global[bool], Default[bool]] = Field( - default=as_default(False), serialization_alias="tlsEnable", validation_alias="tlsEnable" - ) - custom_profile: Optional[Union[Global[bool], Default[bool]]] = Field( - default=None, - serialization_alias="tlsPropertiesCustomProfile", - validation_alias="tlsPropertiesCustomProfile", - ) - profile_properties: Optional[Global[str]] = Field( - default=None, serialization_alias="tlsPropertiesProfile", validation_alias="tlsPropertiesProfile" - ) - model_config = ConfigDict(populate_by_name=True) - - -class File(BaseModel): - disk_file_size: Optional[Union[Global[int], Default[int]]] = Field( - default=as_default(10), serialization_alias="diskFileSize", validation_alias="diskFileSize" - ) - disk_file_rotate: Optional[Union[Global[int], Default[int]]] = Field( - default=as_default(10), serialization_alias="diskFileRotate", validation_alias="diskFileRotate" - ) - - -class Disk(BaseModel): - disk_enable: Optional[Global[bool]] = Field( - default=None, serialization_alias="diskEnable", validation_alias="diskEnable" - ) - file: File = Field(default_factory=File) - - -class LoggingParcel(_ParcelBase): - type_: Literal["logging"] = Field(default="logging", exclude=True) - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - disk: Disk = Field(default_factory=Disk, validation_alias=AliasPath("data", "disk")) - tls_profile: Optional[List[TlsProfile]] = Field(default=[], validation_alias=AliasPath("data", "tlsProfile")) - server: Optional[List[Server]] = Field(default=[], validation_alias=AliasPath("data", "server")) - ipv6_server: Optional[List[Server]] = Field(default=[], validation_alias=AliasPath("data", "ipv6Server")) - - def set_disk(self, enable: bool, disk_file_size: int = 10, disk_file_rotate: int = 10): - self.disk.disk_enable = as_global(enable) - self.disk.file.disk_file_size = as_global(disk_file_size) - self.disk.file.disk_file_rotate = as_global(disk_file_rotate) - - def add_tls_profile( - self, profile: str, version: TlsVersion = "TLSv1.1", ciphersuite_list: Optional[List[CypherSuite]] = None - ): - if not self.tls_profile: - self.tls_profile = [] - self.tls_profile.append( - TlsProfile( - profile=as_global(profile), - version=as_global(version, TlsVersion), - ciphersuite_list=Global[List[CypherSuite]](value=ciphersuite_list) - if ciphersuite_list - else Default[None](value=None), - ) - ) - - def add_ipv4_server( - self, - name: str, - vpn: int = 0, - source_interface: Optional[str] = None, - priority: Priority = "information", - enable_tls: bool = False, - custom_profile: bool = False, - profile_properties: Optional[str] = None, - ): - item = self._create_server_item( - name, vpn, source_interface, priority, enable_tls, custom_profile, profile_properties - ) - if not self.server: - self.server = [] - self.server.append(item) - return item - - def add_ipv6_server( - self, - name: str, - vpn: int = 0, - source_interface: Optional[str] = None, - priority: Priority = "information", - enable_tls: bool = False, - custom_profile: bool = False, - profile_properties: Optional[str] = None, - ): - item = self._create_server_item( - name, vpn, source_interface, priority, enable_tls, custom_profile, profile_properties - ) - if not self.ipv6_server: - self.ipv6_server = [] - self.ipv6_server.append(item) - return item - - def _create_server_item( - self, - name: str, - vpn: int, - source_interface: Optional[str] = None, - priority: Priority = "information", - enable_tls: bool = False, - custom_profile: bool = False, - profile_properties: Optional[str] = None, - ): - return Server( - name=as_global(name), - vpn=as_global(vpn), - source_interface=as_global(source_interface) if source_interface else Default[None](value=None), - priority=as_global(priority, Priority), - enable_tls=as_global(enable_tls), - custom_profile=as_global(custom_profile) if custom_profile else None, - profile_properties=as_global(profile_properties) if profile_properties else None, - ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/mrf.py b/catalystwan/models/configuration/feature_profile/sdwan/system/mrf.py deleted file mode 100644 index cd49d0fea..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/mrf.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import annotations - -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default - -EnableMrfMigration = Literal["enabled", "enabled-from-bgp-core"] -Role = Literal["edge-router", "border-router"] - - -class ManagementRegion(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - vrf_id: Union[Global[int], Default[None], Variable] = Field( - default=Default[None](value=None), - serialization_alias="vrfId", - validation_alias="vrfId", - description="VRF name for management region", - ) - gateway_preference: Optional[Union[Global[List[int]], Default[None], Variable]] = Field( - default=Default[None](value=None), - serialization_alias="gatewayPreference", - validation_alias="gatewayPreference", - description="List of affinity group preferences for VRF", - ) - management_gateway: Union[Global[bool], Default[bool], Variable] = Field( - default=as_default(False), - serialization_alias="managementGateway", - validation_alias="managementGateway", - description="Enable management gateway", - ) - - -class MRFParcel(_ParcelBase): - type_: Literal["mrf"] = Field(default="mrf", exclude=True) - - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - secondary_region: Union[Global[int], Variable, Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "secondaryRegion"), - description="Set secondary region ID", - ) - role: Union[Global[Role], Variable, Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "role"), - description="Set the role for router", - ) - enable_mrf_migration: Union[Global[EnableMrfMigration], Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "enableMrfMigration"), - description="Enable migration mode to Multi-Region Fabric", - ) - migration_bgp_community: Optional[Union[Global[int], Default[None]]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "migrationBgpCommunity"), - description="Set BGP community during migration from BGP-core based network", - ) - enable_management_region: Union[Global[bool], Default[bool], Variable] = Field( - default=as_default(False), - validation_alias=AliasPath("data", "enableManagementRegion"), - description="Enable management region", - ) - management_region: ManagementRegion = Field( - default_factory=ManagementRegion, - validation_alias=AliasPath("data", "managementRegion"), - description="Management Region", - ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py b/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py deleted file mode 100644 index c5b0eac76..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -from ipaddress import IPv4Address, IPv6Address -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default - - -class ServerItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - name: Union[Variable, Global[str], Global[IPv6Address], Global[IPv4Address]] = Field( - ..., description="Set hostname or IP address of server" - ) - key: Optional[Union[Variable, Global[int], Default[None]]] = Field( - None, description="Set authentication key for the server" - ) - vpn: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(0), description="Set VPN in which NTP server is located" - ) - version: Union[Variable, Global[int], Default[int]] = Field(default=as_default(4), description="Set NTP version") - source_interface: Optional[Union[Variable, Global[str], Default[None]]] = Field( - None, - serialization_alias="sourceInterface", - validation_alias="sourceInterface", - description="Set interface to use to reach NTP server", - ) - prefer: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), description="Variable this NTP server" - ) - - -class AuthenticationVariable(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - key_id: Union[Variable, Global[int]] = Field( - ..., serialization_alias="keyId", validation_alias="keyId", description="MD5 authentication key ID" - ) - md5_value: Union[Variable, Global[str]] = Field( - ..., - alias="md5Value", - description="Enter cleartext or AES-encrypted MD5 authentication key" - "[Note: Catalyst SD-WAN Manager will encrypt this field before saving." - "Cleartext strings will not be returned back to the user in GET responses for sensitive fields.]", - ) - - -class Authentication(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - authentication_keys: List[AuthenticationVariable] = Field( - default=[], - serialization_alias="authenticationKeys", - validation_alias="authenticationKeys", - description="Set MD5 authentication key", - ) - trusted_keys: Optional[Union[Variable, Global[List[int]], Default[None]]] = Field( - None, - serialization_alias="trustedKeys", - validation_alias="trustedKeys", - description="Designate authentication key as trustworthy", - ) - - -class Leader(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - enable: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), description="Variable device as NTP Leader" - ) - stratum: Optional[Union[Variable, Global[int], Default[None]]] = Field( - None, description="Variable device as NTP Leader" - ) - source: Optional[Union[Variable, Global[str], Default[None]]] = Field( - None, description="Variable device as NTP Leader" - ) - - -class NTPParcel(_ParcelBase): - type_: Literal["ntp"] = Field(default="ntp", exclude=True) - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - server: List[ServerItem] = Field( - default=[], validation_alias=AliasPath("data", "server"), description="Configure NTP servers" - ) - authentication: Authentication = Field( - default_factory=Authentication, validation_alias=AliasPath("data", "authentication") # type: ignore - ) - leader: Leader = Field(default_factory=Leader, validation_alias=AliasPath("data", "leader")) # type: ignore diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/omp.py b/catalystwan/models/configuration/feature_profile/sdwan/system/omp.py deleted file mode 100644 index e1ef82b46..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/omp.py +++ /dev/null @@ -1,110 +0,0 @@ -from __future__ import annotations - -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default - -SiteTypesForTransportGateway = Literal["type-1", "type-2", "type-3", "cloud", "branch", "br", "spoke"] -TransportGateway = Literal["prefer", "ecmp-with-direct-path"] - - -class AdvertiseIp(BaseModel): - model_config = ConfigDict( - extra="forbid", - ) - bgp: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="BGP") - ospf: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="OSPF") - connected: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="Variable") - static: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="Variable") - eigrp: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="EIGRP") - lisp: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="LISP") - isis: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="ISIS") - - -class AdvertiseIpv6(AdvertiseIp): - pass - - -class AdvertiseIpv4(AdvertiseIp): - ospfv3: Union[Variable, Global[bool], Default[bool]] = Field(default=as_default(False), description="OSPF") - - -class OMPParcel(_ParcelBase): - type_: Literal["omp"] = Field(default="omp", exclude=True) - - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - graceful_restart: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), - validation_alias=AliasPath("data", "gracefulRestart"), - description="Graceful Restart for OMP", - ) - overlay_as: Union[Variable, Global[float], Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "overlayAs"), - description="Overlay AS Number", - ) - send_path_limit: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(4), - validation_alias=AliasPath("data", "sendPathLimit"), - description="Number of Paths Advertised per Prefix", - ) - ecmp_limit: Union[Variable, Global[float], Default[int]] = Field( - default=as_default(4), - validation_alias=AliasPath("data", "ecmpLimit"), - description="Set maximum number of OMP paths to install in cEdge route table", - ) - shutdown: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), validation_alias=AliasPath("data", "shutdown"), description="Variable" - ) - omp_admin_distance_ipv4: Union[Variable, Global[int], Default[Optional[int]]] = Field( - default=as_default(251), - validation_alias=AliasPath("data", "ompAdminDistanceIpv4"), - description="OMP Admin Distance IPv4", - ) - omp_admin_distance_ipv6: Union[Variable, Global[int], Default[Optional[int]]] = Field( - default=as_default(251), - validation_alias=AliasPath("data", "ompAdminDistanceIpv6"), - description="OMP Admin Distance IPv6", - ) - advertisement_interval: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(1), - validation_alias=AliasPath("data", "advertisementInterval"), - description="Advertisement Interval (seconds)", - ) - graceful_restart_timer: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(43200), - validation_alias=AliasPath("data", "gracefulRestartTimer"), - description="Graceful Restart Timer (seconds)", - ) - eor_timer: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(300), validation_alias=AliasPath("data", "eorTimer"), description="EOR Timer" - ) - holdtime: Union[Variable, Global[int], Default[int]] = Field( - default=as_default(60), validation_alias=AliasPath("data", "holdtime"), description="Hold Time (seconds)" - ) - advertise_ipv4: AdvertiseIpv4 = Field( - default_factory=AdvertiseIpv4, validation_alias=AliasPath("data", "advertiseIpv4") - ) - advertise_ipv6: AdvertiseIpv6 = Field( - default_factory=AdvertiseIpv6, validation_alias=AliasPath("data", "advertiseIpv6") - ) - ignore_region_path_length: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - None, - validation_alias=AliasPath("data", "ignoreRegionPathLength"), - description="Treat hierarchical and direct (secondary region) paths equally", - ) - transport_gateway: Optional[Union[Variable, Global[TransportGateway], Default[None]]] = Field( - None, validation_alias=AliasPath("data", "transportGateway"), description="Transport Gateway Path Behavior" - ) - site_types_for_transport_gateway: Optional[ - Union[ - Variable, - Global[List[SiteTypesForTransportGateway]], - Default[None], - ] - ] = Field(None, validation_alias=AliasPath("data", "siteTypesForVariable"), description="Site Types") diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/security.py b/catalystwan/models/configuration/feature_profile/sdwan/system/security.py deleted file mode 100644 index 7b82402df..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/security.py +++ /dev/null @@ -1,177 +0,0 @@ -from __future__ import annotations - -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default, as_variable - -IntegrityType = Literal["esp", "ip-udp-esp", "none", "ip-udp-esp-no-id"] -ReplayWindow = Literal["64", "128", "256", "512", "1024", "2048", "4096", "8192"] -DefaultReplayWindow = Literal["512"] -Tcp = Literal["aes-128-cmac", "hmac-sha-1", "hmac-sha-256"] - - -class KeychainItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - name: Global[str] = Field(..., description="Specify the name of the Keychain") - id: Global[int] = Field(..., description="Specify the Key ID") - - -class OneOfendChoice1(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - infinite: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(True), description="Infinite lifetime" - ) - - -class OneOfendChoice2(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - duration: Union[Global[int], Variable] = Field(..., description="Send lifetime Duration (seconds)") - - -class OneOfendChoice3(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - exact: Global[float] = Field(..., description="Configure Key lifetime end time") - - -class SendLifetime(BaseModel): - """ - Send Lifetime Settings - """ - - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - local: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - default=None, description="Configure Send lifetime Local" - ) - start_epoch: Global[float] = Field( - ..., - serialization_alias="startEpoch", - validation_alias="startEpoch", - description="Configure Key lifetime start time", - ) - one_ofend_choice: Optional[Union[OneOfendChoice1, OneOfendChoice2, OneOfendChoice3]] = Field( - default=None, serialization_alias="oneOfendChoice", validation_alias="oneOfendChoice" - ) - - -class AcceptLifetime(BaseModel): - """ - Accept Lifetime Settings - """ - - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - local: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - default=None, description="Configure Send lifetime Local" - ) - start_epoch: Global[float] = Field( - ..., - serialization_alias="startEpoch", - validation_alias="startEpoch", - description="Configure Key lifetime start time", - ) - one_ofend_choice: Optional[Union[OneOfendChoice1, OneOfendChoice2, OneOfendChoice3]] = Field( - default=None, serialization_alias="oneOfendChoice", validation_alias="oneOfendChoice" - ) - - -class KeyItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - id: Global[int] = Field(..., description="Select the Key ID") - name: Global[str] = Field(..., description="Select the chain name") - send_id: Union[Global[int], Variable] = Field( - ..., serialization_alias="recvId", validation_alias="srecvId", description="Specify the Send ID" - ) - recv_id: Union[Global[int], Variable] = Field( - ..., serialization_alias="recvId", validation_alias="recvId", description="Specify the Receiver ID" - ) - include_tcp_options: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - default=None, - serialization_alias="includeTcpOptions", - validation_alias="includeTcpOptions", - description="Configure Include TCP Options", - ) - accept_ao_mismatch: Optional[Union[Variable, Global[bool], Default[bool]]] = Field( - default=None, - serialization_alias="acceptAoMismatch", - validation_alias="acceptAoMismatch", - description="Configure Accept AO Mismatch", - ) - tcp: Global[Tcp] = Field(..., description="Crypto Algorithm") - key_string: Union[Global[str], Variable] = Field( - ..., - serialization_alias="keyString", - validation_alias="keyString", - description="Specify the Key String [Note: Catalyst SD-WAN Manager will encrypt this field before saving." - "Cleartext strings will not be returned back to the user in GET responses for sensitive fields.]", - ) - send_lifetime: Optional[SendLifetime] = Field( - default=None, - serialization_alias="sendLifetime", - validation_alias="sendLifetime", - description="Send Lifetime Settings", - ) - accept_lifetime: Optional[AcceptLifetime] = Field( - default=None, - serialization_alias="acceptLifetime", - validation_alias="acceptLifetime", - description="Accept Lifetime Settings", - ) - - -class SecurityParcel(_ParcelBase): - type_: Literal["security"] = Field(default="security", exclude=True) - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - rekey: Union[Global[int], Variable, Default[int]] = Field( - default=as_default(86400), - validation_alias=AliasPath("data", "rekey"), - description="Set how often to change the AES key for DTLS connections", - ) - replay_window: Optional[Union[Global[ReplayWindow], Variable, Default[DefaultReplayWindow]]] = Field( - default=as_default("512", DefaultReplayWindow), - validation_alias=AliasPath("data", "replayWindow"), - description="Set the sliding replay window size", - ) - extended_ar_window: Optional[Union[Global[int], Variable, Default[int]]] = Field( - default=None, - validation_alias=AliasPath("data", "extendedArWindow"), - description="Extended Anti-Replay Window", - ) - integrity_type: Union[Global[List[IntegrityType]], Variable] = Field( - default=as_variable("{{security_auth_type_inte}}"), - validation_alias=AliasPath("data", "integrityType"), - description="Set the authentication type for DTLS connections", - ) - pairwise_keying: Union[Variable, Global[bool], Default[bool]] = Field( - default=as_default(False), - validation_alias=AliasPath("data", "pairwiseKeying"), - description="Enable or disable IPsec pairwise-keying", - ) - keychain: List[KeychainItem] = Field( - default=[], validation_alias=AliasPath("data", "keychain"), description="Configure a Keychain" - ) - key: List[KeyItem] = Field(default=[], validation_alias=AliasPath("data", "key"), description="Configure a Key") diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/snmp.py b/catalystwan/models/configuration/feature_profile/sdwan/system/snmp.py deleted file mode 100644 index bbddffa0d..000000000 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/snmp.py +++ /dev/null @@ -1,172 +0,0 @@ -from __future__ import annotations - -from ipaddress import IPv4Address, IPv6Address -from typing import List, Literal, Optional, Union - -from pydantic import AliasPath, BaseModel, ConfigDict, Field - -from catalystwan.api.configuration_groups.parcel import Default, Global, Variable, _ParcelBase, as_default - -Authorization = Literal["read-only", "read-write"] -Priv = Literal["aes-cfb-128", "aes-256-cfb-128"] -SecurityLevel = Literal["no-auth-no-priv", "auth-no-priv", "auth-priv"] - - -class OidItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - id: Union[Global[str], Variable] = Field(..., description="Configure identifier of subtree of MIB objects") - exclude: Optional[Union[Global[bool], Variable]] = Field(default=None, description="Exclude the OID") - - -class ViewItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - name: Global[str] = Field(..., description="Set the name of the SNMP view") - oid: Optional[List[OidItem]] = Field(default=None, description="Configure SNMP object identifier") - - -class CommunityItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - name: Global[str] = Field( - ..., - description="Set name of the SNMP community" - "[Note: Catalyst SD-WAN Manager will encrypt this field before saving." - "Cleartext strings will not be returned back to the user in GET responses for sensitive fields.]", - ) - user_label: Optional[Global[str]] = Field( - default=None, - serialization_alias="userLabel", - validation_alias="userLabel", - description="Set user label of the SNMP community", - ) - view: Union[Global[str], Variable] = Field(..., description="Set name of the SNMP view") - authorization: Optional[Union[Global[Authorization], Variable]] = Field( - default=None, description="Configure access permissions" - ) - - -class GroupItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - name: Global[str] = Field(..., description="Name of the SNMP group") - security_level: Global[SecurityLevel] = Field( - ..., - serialization_alias="securityLevel", - validation_alias="securityLevel", - description="Configure security level", - ) - view: Union[Global[str], Variable] = Field(..., description="Name of the SNMP view") - - -class UserItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - name: Global[str] = Field(..., description="Name of the SNMP user") - auth: Optional[Union[Global[Literal["sha"]], Variable, Default[None]]] = Field( - default=None, description="Configure authentication protocol" - ) - auth_password: Optional[Union[Global[str], Variable, Default[None]]] = Field( - default=None, - serialization_alias="authPassword", - validation_alias="authPassword", - description="Specify authentication protocol password", - ) - priv: Optional[Union[Global[Priv], Variable, Default[None]]] = Field( - default=None, description="Configure privacy protocol" - ) - priv_password: Optional[Union[Global[str], Variable, Default[None]]] = Field( - default=None, - serialization_alias="privPassword", - validation_alias="privPassword", - description="Specify privacy protocol password", - ) - group: Union[Global[str], Variable] = Field(..., description="Name of the SNMP group") - - -class TargetItem(BaseModel): - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - vpn_id: Union[Global[int], Variable] = Field( - ..., - serialization_alias="vpnId", - validation_alias="vpnId", - description="Set VPN in which SNMP server is located", - ) - ip: Union[Global[Union[IPv4Address, IPv6Address]], Variable] = Field( - ..., description="Set IPv4/IPv6 address of SNMP server" - ) - port: Union[Global[int], Variable] = Field(..., description="Set UDP port number to connect to SNMP server") - user_label: Optional[Global[str]] = Field( - default=None, - serialization_alias="userLabel", - validation_alias="userLabel", - description="Set user label of the SNMP community", - ) - community_name: Optional[Union[Global[str], Variable]] = Field( - default=None, - serialization_alias="communityName", - validation_alias="communityName", - description="Set name of the SNMP community" - "[Note: Catalyst SD-WAN Manager will encrypt this field before saving." - "Cleartext strings will not be returned back to the user in GET responses for sensitive fields.]." - "DEPRECATED. Use userLabel field instead", - ) - user: Optional[Union[Global[str], Variable]] = Field(default=None, description="Set name of the SNMP user") - source_interface: Optional[Union[Global[str], Variable]] = Field( - default=None, - serialization_alias="sourceInterface", - validation_alias="sourceInterface", - description="Source interface for outgoing SNMP traps", - ) - - -class SNMPParcel(_ParcelBase): - type_: Literal["snmp"] = Field(default="snmp", exclude=True) - model_config = ConfigDict( - extra="forbid", - populate_by_name=True, - ) - shutdown: Union[Global[bool], Variable, Default[bool]] = Field( - default=as_default(False), validation_alias=AliasPath("data", "shutdown"), description="Enable or disable SNMP" - ) - contact: Union[Global[str], Variable, Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "contact"), - description="Set the contact for this managed node", - ) - location: Union[Global[str], Variable, Default[None]] = Field( - default=Default[None](value=None), - validation_alias=AliasPath("data", "location"), - description="Set the physical location of this managed node", - ) - view: List[ViewItem] = Field( - default=[], validation_alias=AliasPath("data", "view"), description="Configure a view record" - ) - community: List[CommunityItem] = Field( - default=[], validation_alias=AliasPath("data", "community"), description="Configure SNMP community" - ) - group: List[GroupItem] = Field( - default=[], validation_alias=AliasPath("data", "group"), description="Configure an SNMP group" - ) - user: List[UserItem] = Field( - default=[], validation_alias=AliasPath("data", "user"), description="Configure an SNMP user" - ) - target: List[TargetItem] = Field( - default=[], - validation_alias=AliasPath("data", "target"), - description="Configure SNMP server to receive SNMP traps", - ) diff --git a/catalystwan/models/configuration/topology_group.py b/catalystwan/models/configuration/topology_group.py deleted file mode 100644 index 47ecddb7f..000000000 --- a/catalystwan/models/configuration/topology_group.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import List, Literal, Optional -from uuid import UUID - -from pydantic import BaseModel, Field - - -class TopologyGroup(BaseModel): - name: str - solution: Literal["sdwan"] = "sdwan" - profiles: List[UUID] = [] - from_topology_group: Optional[UUID] = Field( - default=None, serialization_alias="fromTopologyGroup", validation_alias="fromTopologyGroup" - ) diff --git a/catalystwan/models/policy/centralized.py b/catalystwan/models/policy/centralized.py index 3f494bb7f..50ad91b03 100644 --- a/catalystwan/models/policy/centralized.py +++ b/catalystwan/models/policy/centralized.py @@ -3,7 +3,7 @@ from typing import List, Literal, Optional, Union, overload from uuid import UUID -from pydantic import BaseModel, ConfigDict, Field, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from typing_extensions import Annotated from catalystwan.models.policy.policy import ( @@ -161,27 +161,12 @@ class MeshPolicyItem(AssemblyItemBase): type: Literal["mesh"] = "mesh" -class AppRoutePolicyItem(AssemblyItemBase): - type: Literal["appRoute"] = "appRoute" - - -class CFlowDPolicyItem(AssemblyItemBase): - type: Literal["cflowd"] = "cflowd" - - -class VpnMembershipGroupPolicyItem(AssemblyItemBase): - type: Literal["vpnMembershipGroup"] = "vpnMembershipGroup" - - AnyAssemblyItem = Annotated[ Union[ TrafficDataPolicyItem, ControlPolicyItem, MeshPolicyItem, HubAndSpokePolicyItem, - AppRoutePolicyItem, - CFlowDPolicyItem, - VpnMembershipGroupPolicyItem, ], Field(discriminator="type"), ] @@ -201,7 +186,7 @@ class CentralizedPolicy(PolicyCreationPayload): serialization_alias="policyDefinition", validation_alias="policyDefinition", ) - policy_type: Literal["feature", "cli"] = Field( + policy_type: Literal["feature"] = Field( default="feature", serialization_alias="policyType", validation_alias="policyType" ) @@ -221,23 +206,19 @@ def add_mesh_policy(self, mesh_policy_id: UUID) -> None: def add_hub_and_spoke_policy(self, hub_and_spoke_policy_id: UUID) -> None: self.policy_definition.assembly.append(HubAndSpokePolicyItem(definition_id=hub_and_spoke_policy_id)) - @model_validator(mode="before") + @field_validator("policy_definition", mode="before") @classmethod - def try_parse_policy_definition_string(cls, values): + def try_parse(cls, policy_definition): # this is needed because GET /template/policy/vsmart contains string in policyDefinition field # while POST /template/policy/vsmart requires a regular object # it makes sense to reuse that model for both requests and present parsed data to the user - # TODO: this is workaround, probably it is better to provide separate models for "cli" and "feature" - if policy_definition := values.get("policyDefinition") and values.get("policyType") != "cli": - if isinstance(policy_definition, str): - values["policyDefinition"] = CentralizedPolicyDefinition.model_validate_json(policy_definition) - else: - values["policyDefinition"] = CentralizedPolicyDefinition() - return values + if isinstance(policy_definition, str): + return CentralizedPolicyDefinition.parse_raw(policy_definition) + return policy_definition class CentralizedPolicyEditPayload(PolicyEditPayload, CentralizedPolicy): - rid: Optional[int] = Field(default=None, serialization_alias="@rid", validation_alias="@rid") + rid: Optional[str] = Field(default=None, serialization_alias="@rid", validation_alias="@rid") class CentralizedPolicyInfo(PolicyInfo, CentralizedPolicyEditPayload): diff --git a/catalystwan/models/policy/definitions/access_control_list.py b/catalystwan/models/policy/definitions/access_control_list.py index 1fab42806..d05b4f2a2 100644 --- a/catalystwan/models/policy/definitions/access_control_list.py +++ b/catalystwan/models/policy/definitions/access_control_list.py @@ -91,8 +91,8 @@ def match_high_plp(self) -> None: def match_protocols(self, protocols: Set[int]) -> None: self._insert_match(ProtocolEntry.from_protocol_set(protocols)) - def match_source_data_prefix_list(self, data_prefix_lists: List[UUID]) -> None: - self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_lists)) + def match_source_data_prefix_list(self, data_prefix_list_id: UUID) -> None: + self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_list_id)) def match_source_ip(self, networks: List[IPv4Network]) -> None: self._insert_match(SourceIPEntry.from_ipv4_networks(networks)) diff --git a/catalystwan/models/policy/definitions/device_access.py b/catalystwan/models/policy/definitions/device_access.py index 817c0803d..10e00ec74 100644 --- a/catalystwan/models/policy/definitions/device_access.py +++ b/catalystwan/models/policy/definitions/device_access.py @@ -61,8 +61,8 @@ class DeviceAccessPolicySequence(PolicyDefinitionSequenceBase): def match_device_access_protocol(self, port: DeviceAccessProtocol) -> None: self._insert_match(DestinationPortEntry.from_port_set_and_ranges(ports={port})) - def match_source_data_prefix_list(self, data_prefix_lists: List[UUID]) -> None: - self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_lists)) + def match_source_data_prefix_list(self, data_prefix_list_id: UUID) -> None: + self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_list_id)) def match_source_ip(self, networks: List[IPv4Network]) -> None: self._insert_match(SourceIPEntry.from_ipv4_networks(networks)) diff --git a/catalystwan/models/policy/definitions/qos_map.py b/catalystwan/models/policy/definitions/qos_map.py index 2576b62b7..01ac62719 100644 --- a/catalystwan/models/policy/definitions/qos_map.py +++ b/catalystwan/models/policy/definitions/qos_map.py @@ -5,7 +5,6 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from catalystwan.models.common import IntStr from catalystwan.models.policy.policy_definition import PolicyDefinitionBase QoSScheduling = Literal[ @@ -20,17 +19,11 @@ class QoSScheduler(BaseModel): - queue: IntStr = Field(ge=0, le=8) - class_map_ref: Optional[UUID] = Field( - default=None, serialization_alias="classMapRef", validation_alias="classMapRef" - ) - bandwidth_percent: IntStr = Field( - default=1, ge=1, le=100, serialization_alias="bandwidthPercent", validation_alias="bandwidthPercent" - ) - buffer_percent: IntStr = Field( - default=1, ge=1, le=100, serialization_alias="bufferPercent", validation_alias="bufferPercent" - ) - burst: Optional[IntStr] = Field(default=None, ge=5000, le=10_000_000) + queue: str + class_map_ref: Union[UUID, Literal[""]] = Field(serialization_alias="classMapRef", validation_alias="classMapRef") + bandwidth_percent: str = Field("1", serialization_alias="bandwidthPercent", validation_alias="bandwidthPercent") + buffer_percent: str = Field("1", serialization_alias="bufferPercent", validation_alias="bufferPercent") + burst: Optional[str] = None scheduling: QoSScheduling = "wrr" drops: QoSDropType = "tail-drop" temp_key_values: Optional[str] = Field( @@ -40,23 +33,35 @@ class QoSScheduler(BaseModel): @staticmethod def get_default_control_scheduler() -> "QoSScheduler": return QoSScheduler( - queue=0, - bandwidth_percent=100, - buffer_percent=100, - burst=15000, + queue="0", + class_map_ref="", + bandwidth_percent="100", + buffer_percent="100", + burst="15000", scheduling="llq", drops="tail-drop", ) model_config = ConfigDict(populate_by_name=True) - @field_validator("class_map_ref", mode="before") + @field_validator("queue") + @classmethod + def check_queue(cls, queue_str: str): + assert 0 <= int(queue_str) <= 7 + return queue_str + + @field_validator("bandwidth_percent", "buffer_percent") + @classmethod + def check_bandwidth_and_buffer_percent(cls, percent_str: str): + assert 1 <= int(percent_str) <= 100 + return percent_str + + @field_validator("burst") @classmethod - def check_optional_class_map_ref(cls, class_map_ref: Union[str, None]): - # None and "" indicates missing value, both can be found in server responses - if not class_map_ref: - return None - return class_map_ref + def check_burst(cls, burst_val: Union[str, None]): + if burst_val is not None: + assert 5000 <= int(burst_val) <= 10_000_000 + return burst_val class QoSMapDefinition(BaseModel): @@ -81,11 +86,11 @@ def add_scheduler( ) -> None: self.definition.qos_schedulers.append( QoSScheduler( - queue=queue, + queue=str(queue), class_map_ref=class_map_ref, - bandwidth_percent=bandwidth, - buffer_percent=buffer, - burst=burst, + bandwidth_percent=str(bandwidth), + buffer_percent=str(buffer), + burst=str(burst) if burst is not None else None, scheduling=scheduling, drops=drops, ) diff --git a/catalystwan/models/policy/definitions/traffic_data.py b/catalystwan/models/policy/definitions/traffic_data.py index 2d3ffbee5..b5e63d4a8 100644 --- a/catalystwan/models/policy/definitions/traffic_data.py +++ b/catalystwan/models/policy/definitions/traffic_data.py @@ -7,7 +7,7 @@ from pydantic import ConfigDict, Field from typing_extensions import Annotated -from catalystwan.models.common import ICMPMessageType, ServiceChainNumber, TLOCColor +from catalystwan.models.common import ServiceChainNumber, TLOCColor from catalystwan.models.policy.lists_entries import EncapType from catalystwan.models.policy.policy_definition import ( AppListEntry, @@ -26,7 +26,6 @@ DSCPEntry, FallBackToRoutingAction, ForwardingClassEntry, - ICMPMessageEntry, LocalTLOCListEntry, LocalTLOCListEntryValue, LogAction, @@ -67,25 +66,24 @@ TrafficDataPolicySequenceEntry = Annotated[ Union[ - AppListEntry, - DestinationDataIPv6PrefixListEntry, - DestinationDataPrefixListEntry, - DestinationIPEntry, - DestinationPortEntry, - DestinationRegionEntry, - DNSAppListEntry, - DNSEntry, - DSCPEntry, - ICMPMessageEntry, PacketLengthEntry, PLPEntry, ProtocolEntry, - SourceDataIPv6PrefixListEntry, - SourceDataPrefixListEntry, + DSCPEntry, SourceIPEntry, SourcePortEntry, + DestinationIPEntry, + DestinationPortEntry, TCPEntry, + DNSEntry, TrafficToEntry, + SourceDataPrefixListEntry, + DestinationDataPrefixListEntry, + SourceDataIPv6PrefixListEntry, + DestinationDataIPv6PrefixListEntry, + DestinationRegionEntry, + DNSAppListEntry, + AppListEntry, ], Field(discriminator="field"), ] @@ -102,7 +100,7 @@ class TrafficDataPolicySequenceMatch(Match): class TrafficDataPolicySequence(PolicyDefinitionSequenceBase): - sequence_type: Literal["applicationFirewall", "qos", "serviceChaining", "trafficEngineering", "data"] = Field( + sequence_type: Literal["data"] = Field( default="data", serialization_alias="sequenceType", validation_alias="sequenceType" ) match: TrafficDataPolicySequenceMatch = TrafficDataPolicySequenceMatch() @@ -124,9 +122,6 @@ def match_dns_response(self) -> None: def match_dscp(self, dscp: int) -> None: self._insert_match(DSCPEntry(value=str(dscp))) - def match_icmp(self, icmp_message_types: List[ICMPMessageType]) -> None: - self._insert_match(ICMPMessageEntry(value=icmp_message_types)) - def match_packet_length(self, packet_lengths: Tuple[int, int]) -> None: self._insert_match(PacketLengthEntry.from_range(packet_lengths)) @@ -139,8 +134,8 @@ def match_high_plp(self) -> None: def match_protocols(self, protocols: Set[int]) -> None: self._insert_match(ProtocolEntry.from_protocol_set(protocols)) - def match_source_data_prefix_list(self, data_prefix_lists: List[UUID]) -> None: - self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_lists)) + def match_source_data_prefix_list(self, data_prefix_list_id: UUID) -> None: + self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_list_id)) def match_source_ip(self, networks: List[IPv4Network]) -> None: self._insert_match(SourceIPEntry.from_ipv4_networks(networks)) diff --git a/catalystwan/models/policy/definitions/zone_based_firewall.py b/catalystwan/models/policy/definitions/zone_based_firewall.py index cd2500bf6..4a7fe5a5d 100644 --- a/catalystwan/models/policy/definitions/zone_based_firewall.py +++ b/catalystwan/models/policy/definitions/zone_based_firewall.py @@ -9,10 +9,7 @@ from catalystwan.models.misc.application_protocols import ApplicationProtocol from catalystwan.models.policy.policy_definition import ( - AdvancedInspectionProfileAction, AppListEntry, - AppListFlatEntry, - ConnectionEventsAction, DefinitionWithSequencesCommonBase, DestinationDataPrefixListEntry, DestinationFQDNEntry, @@ -43,7 +40,6 @@ ZoneBasedFWPolicySequenceEntry = Annotated[ Union[ AppListEntry, - AppListFlatEntry, DestinationDataPrefixListEntry, DestinationFQDNEntry, DestinationGeoLocationEntry, @@ -75,15 +71,6 @@ Field(discriminator="field"), ] -ZoneBasedFWPolicyActions = Annotated[ - Union[ - AdvancedInspectionProfileAction, - ConnectionEventsAction, - LogAction, - ], - Field(discriminator="type"), -] - class ZoneBasedFWPolicyMatches(Match): entries: List[ZoneBasedFWPolicySequenceEntry] = [] @@ -95,7 +82,7 @@ class ZoneBasedFWPolicySequenceWithRuleSets(PolicyDefinitionSequenceBase): ) match: ZoneBasedFWPolicyMatches ruleset: bool = True - actions: List[ZoneBasedFWPolicyActions] = [] + actions: List[LogAction] = [] model_config = ConfigDict(populate_by_name=True) def match_rule_set_lists(self, rule_set_ids: Set[UUID]) -> None: @@ -158,8 +145,8 @@ def match_protocol_names(self, names: Set[str], protocol_map: Dict[str, Applicat def match_protocol_name_list(self, protocol_name_list_id: UUID) -> None: self._insert_match(ProtocolNameListEntry(ref=protocol_name_list_id)) - def match_source_data_prefix_list(self, data_prefix_lists: List[UUID]) -> None: - self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_lists)) + def match_source_data_prefix_list(self, data_prefix_list_id: UUID) -> None: + self._insert_match(SourceDataPrefixListEntry(ref=data_prefix_list_id)) def match_source_fqdn(self, fqdn: str) -> None: self._insert_match(SourceFQDNEntry(value=fqdn)) @@ -204,7 +191,7 @@ class ZoneBasedFWPolicyDefinition(DefinitionWithSequencesCommonBase): class ZoneBasedFWPolicy(ZoneBasedFWPolicyHeader): type: Literal["zoneBasedFW"] = "zoneBasedFW" - mode: Literal["security", "unified"] = "security" + mode: Literal["security"] = "security" definition: ZoneBasedFWPolicyDefinition = ZoneBasedFWPolicyDefinition() def add_ipv4_rule( diff --git a/catalystwan/models/policy/lists.py b/catalystwan/models/policy/lists.py index 0b5972e13..50570717e 100644 --- a/catalystwan/models/policy/lists.py +++ b/catalystwan/models/policy/lists.py @@ -1,19 +1,12 @@ # Copyright 2023 Cisco Systems, Inc. and its affiliates -from ipaddress import IPv4Address, IPv4Network, IPv6Interface +from ipaddress import IPv4Address, IPv4Network, IPv6Network from typing import Any, List, Literal, Optional, Set, Tuple from uuid import UUID from pydantic import BaseModel, Field from catalystwan.models.common import InterfaceType, TLOCColor, WellKnownBGPCommunities -from catalystwan.models.configuration.feature_profile.sdwan.policy_object import AnyPolicyObjectParcel -from catalystwan.models.configuration.feature_profile.sdwan.policy_object.policy.data_prefix import ( - DataPrefixEntry, - DataPrefixParcel, -) -from catalystwan.models.configuration.feature_profile.sdwan.policy_object.policy.tloc_list import TlocParcel -from catalystwan.models.configuration.feature_profile.sdwan.policy_object.security.zone import SecurityZoneListParcel from catalystwan.models.policy.lists_entries import ( AppListEntry, AppProbeClassListEntry, @@ -64,9 +57,6 @@ def _add_entry(self, entry: Any, single: bool = False) -> None: else: self.entries.append(entry) - def to_policy_object_parcel(self) -> Optional[AnyPolicyObjectParcel]: - return None - class DataPrefixList(PolicyListBase): type: Literal["dataPrefix"] = "dataPrefix" @@ -75,13 +65,6 @@ class DataPrefixList(PolicyListBase): def add_prefix(self, ip_prefix: IPv4Network) -> None: self._add_entry(DataPrefixListEntry(ip_prefix=ip_prefix)) - def to_policy_object_parcel(self) -> DataPrefixParcel: - return DataPrefixParcel( - parcel_name=self.name, - parcel_description=self.description, - entries=[DataPrefixEntry.from_ipv4_network(i.ip_prefix) for i in self.entries], - ) - class SiteList(PolicyListBase): type: Literal["site"] = "site" @@ -95,9 +78,6 @@ def add_site_range(self, site_range: Tuple[int, int]): entry = SiteListEntry(site_id=f"{site_range[0]}-{site_range[1]}") self._add_entry(entry) - def to_policy_object_parcel(self) -> None: - return None - class VPNList(PolicyListBase): type: Literal["vpn"] = "vpn" @@ -111,9 +91,6 @@ def add_vpn_range(self, vpn_range: Tuple[int, int]): entry = VPNListEntry(vpn=f"{vpn_range[0]}-{vpn_range[1]}") self._add_entry(entry) - def to_policy_object_parcel(self) -> None: - return None - class ZoneList(PolicyListBase): type: Literal["zone"] = "zone" @@ -125,18 +102,6 @@ def assign_vpns(self, vpns: Set[int]) -> None: def assign_interfaces(self, ifs: Set[InterfaceType]) -> None: self.entries = [ZoneListEntry(interface=interface) for interface in ifs] - def to_policy_object_parcel(self) -> SecurityZoneListParcel: - parcel = SecurityZoneListParcel( - parcel_name=self.name, - parcel_description=self.description, - ) - for e in self.entries: - if e.vpn is not None: - parcel.add_vpn(e.vpn) - if e.interface is not None: - parcel.add_interface(e.interface) - return parcel - class FQDNList(PolicyListBase): type: Literal["fqdn"] = "fqdn" @@ -186,7 +151,7 @@ class DataIPv6PrefixList(PolicyListBase): type: Literal["dataIpv6Prefix"] = "dataIpv6Prefix" entries: List[DataIPv6PrefixListEntry] = [] - def add_prefix(self, ipv6_prefix: IPv6Interface) -> None: + def add_prefix(self, ipv6_prefix: IPv6Network) -> None: self._add_entry(DataIPv6PrefixListEntry(ipv6_prefix=ipv6_prefix)) @@ -309,15 +274,6 @@ def add_tloc(self, tloc: IPv4Address, color: TLOCColor, encap: EncapType, prefer _preference = str(preference) if preference is not None else None self.entries.append(TLOCListEntry(tloc=tloc, color=color, encap=encap, preference=_preference)) - def to_policy_object_parcel(self) -> TlocParcel: - parcel = TlocParcel( - parcel_name=self.name, - parcel_description=self.description, - ) - for i in self.entries: - parcel.add_entry(i.tloc, i.color, i.encap, i.preference) - return parcel - class PreferredColorGroupList(PolicyListBase): type: Literal["preferredColorGroup"] = "preferredColorGroup" diff --git a/catalystwan/models/policy/lists_entries.py b/catalystwan/models/policy/lists_entries.py index 9c610e600..8eba5358d 100644 --- a/catalystwan/models/policy/lists_entries.py +++ b/catalystwan/models/policy/lists_entries.py @@ -1,6 +1,6 @@ # Copyright 2023 Cisco Systems, Inc. and its affiliates -from ipaddress import IPv4Address, IPv4Network, IPv6Interface, IPv6Network +from ipaddress import IPv4Address, IPv4Network, IPv6Network from typing import List, Literal, Optional, Set from uuid import UUID @@ -9,21 +9,18 @@ from catalystwan.models.common import InterfaceType, TLOCColor, check_fields_exclusive -def check_jitter_ms(jitter_str: Optional[str]) -> Optional[str]: - if jitter_str is not None: - assert 1 <= int(jitter_str) <= 1000 +def check_jitter_ms(jitter_str: str) -> str: + assert 1 <= int(jitter_str) <= 1000 return jitter_str -def check_latency_ms(latency_str: Optional[str]) -> Optional[str]: - if latency_str is not None: - assert 1 <= int(latency_str) <= 1000 +def check_latency_ms(latency_str: str) -> str: + assert 1 <= int(latency_str) <= 1000 return latency_str -def check_loss_percent(loss_str: Optional[str]) -> Optional[str]: - if loss_str is not None: - assert 0 <= int(loss_str) <= 100 +def check_loss_percent(loss_str: str) -> str: + assert 0 <= int(loss_str) <= 100 return loss_str @@ -237,7 +234,7 @@ class ColorListEntry(BaseModel): class DataIPv6PrefixListEntry(BaseModel): model_config = ConfigDict(populate_by_name=True) - ipv6_prefix: IPv6Interface = Field(serialization_alias="ipv6Prefix", validation_alias="ipv6Prefix") + ipv6_prefix: IPv6Network = Field(serialization_alias="ipv6Prefix", validation_alias="ipv6Prefix") class LocalDomainListEntry(BaseModel): @@ -331,9 +328,7 @@ class SLAClassListEntry(BaseModel): latency: Optional[str] = None loss: Optional[str] = None jitter: Optional[str] = None - app_probe_class: Optional[UUID] = Field( - default=None, serialization_alias="appProbeClass", validation_alias="appProbeClass" - ) + app_probe_class: Optional[UUID] = Field(serialization_alias="appProbeClass", validation_alias="appProbeClass") fallback_best_tunnel: Optional[FallbackBestTunnel] = Field( default=None, serialization_alias="fallbackBestTunnel", validation_alias="fallbackBestTunnel" ) diff --git a/catalystwan/models/policy/policy.py b/catalystwan/models/policy/policy.py index 874995e4a..05a1296e5 100644 --- a/catalystwan/models/policy/policy.py +++ b/catalystwan/models/policy/policy.py @@ -1,7 +1,7 @@ # Copyright 2023 Cisco Systems, Inc. and its affiliates import datetime -from typing import List, Literal, Optional, Sequence, Union +from typing import List, Literal, Optional, Sequence from uuid import UUID from pydantic import BaseModel, ConfigDict, Field @@ -70,7 +70,7 @@ class PolicyCreationPayload(BaseModel): default="default description", serialization_alias="policyDescription", validation_alias="policyDescription" ) policy_type: str = Field(serialization_alias="policyType", validation_alias="policyType") - policy_definition: Union[PolicyDefinition, str] = Field( + policy_definition: PolicyDefinition = Field( serialization_alias="policyDefinition", validation_alias="policyDefinition" ) is_policy_activated: bool = Field( diff --git a/catalystwan/models/policy/policy_definition.py b/catalystwan/models/policy/policy_definition.py index dcdcd3399..362bd7a38 100644 --- a/catalystwan/models/policy/policy_definition.py +++ b/catalystwan/models/policy/policy_definition.py @@ -6,17 +6,10 @@ from typing import Any, Dict, List, MutableSequence, Optional, Protocol, Sequence, Set, Tuple, Union from uuid import UUID -from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator, model_validator +from pydantic import BaseModel, ConfigDict, Field, RootModel, model_validator from typing_extensions import Annotated, Literal -from catalystwan.models.common import ( - ICMPMessageType, - ServiceChainNumber, - TLOCColor, - check_fields_exclusive, - str_as_str_list, - str_as_uuid_list, -) +from catalystwan.models.common import ServiceChainNumber, TLOCColor, check_fields_exclusive from catalystwan.models.misc.application_protocols import ApplicationProtocol from catalystwan.models.policy.lists_entries import EncapType from catalystwan.typed_list import DataSequence @@ -494,18 +487,9 @@ def from_nat_vpn(fallback: bool, vpn: int = 0) -> "NATVPNEntry": return NATVPNEntry(root=[UseVPNEntry(value=str(vpn))]) -class ICMPMessageEntry(BaseModel): - field: Literal["icmpMessage"] = "icmpMessage" - value: List[ICMPMessageType] - - _value = field_validator("value", mode="before")(str_as_str_list) - - class SourceDataPrefixListEntry(BaseModel): field: Literal["sourceDataPrefixList"] = "sourceDataPrefixList" - ref: List[UUID] - - _ref = field_validator("ref", mode="before")(str_as_uuid_list) + ref: UUID class SourceDataIPv6PrefixListEntry(BaseModel): @@ -533,11 +517,6 @@ class AppListEntry(BaseModel): ref: UUID -class AppListFlatEntry(BaseModel): - field: Literal["appListFlat"] = "appListFlat" - ref: UUID - - class SourceFQDNListEntry(BaseModel): field: Literal["sourceFqdnList"] = "sourceFqdnList" ref: UUID @@ -773,16 +752,6 @@ class PolicerAction(BaseModel): parameter: Reference -class ConnectionEventsAction(BaseModel): - type: Literal["connectionEvents"] = "connectionEvents" - parameter: str = "" - - -class AdvancedInspectionProfileAction(BaseModel): - type: Literal["advancedInspectionProfile"] = "advancedInspectionProfile" - parameter: Reference - - ActionSetEntry = Annotated[ Union[ AffinityEntry, @@ -817,10 +786,8 @@ class ActionSet(BaseModel): ActionEntry = Annotated[ Union[ ActionSet, - AdvancedInspectionProfileAction, CFlowDAction, ClassMapAction, - ConnectionEventsAction, CountAction, DREOptimizationAction, FallBackToRoutingAction, @@ -843,7 +810,6 @@ class ActionSet(BaseModel): MatchEntry = Annotated[ Union[ AppListEntry, - AppListFlatEntry, CarrierEntry, ClassMapListEntry, ColorListEntry, @@ -865,7 +831,6 @@ class ActionSet(BaseModel): DSCPEntry, ExpandedCommunityListEntry, GroupIDEntry, - ICMPMessageEntry, NextHeaderEntry, OMPTagEntry, OriginatorEntry, @@ -946,9 +911,7 @@ class PolicyDefinitionSequenceBase(BaseModel): default="drop", serialization_alias="baseAction", validation_alias="baseAction" ) sequence_type: SequenceType = Field(serialization_alias="sequenceType", validation_alias="sequenceType") - sequence_ip_type: Optional[SequenceIpType] = Field( - default="ipv4", serialization_alias="sequenceIpType", validation_alias="sequenceIpType" - ) + sequence_ip_type: SequenceIpType = Field(serialization_alias="sequenceIpType", validation_alias="sequenceIpType") ruleset: Optional[bool] = None match: Match actions: Sequence[ActionEntry] diff --git a/catalystwan/tests/config_migration/test_converter_chooser.py b/catalystwan/tests/config_migration/test_converter_chooser.py deleted file mode 100644 index 0e2935d0a..000000000 --- a/catalystwan/tests/config_migration/test_converter_chooser.py +++ /dev/null @@ -1,26 +0,0 @@ -import unittest - -from parameterized import parameterized # type: ignore - -from catalystwan.exceptions import CatalystwanException -from catalystwan.utils.config_migration.converters.feature_template import choose_parcel_converter -from catalystwan.utils.config_migration.converters.feature_template.aaa import AAATemplateConverter -from catalystwan.utils.config_migration.converters.feature_template.bfd import BFDTemplateConverter - - -class TestParcelConverterChooser(unittest.TestCase): - @parameterized.expand( - [("cisco_aaa", AAATemplateConverter), ("cedge_aaa", AAATemplateConverter), ("cisco_bfd", BFDTemplateConverter)] - ) - def test_choose_parcel_converter_returns_correct_converter_when_supported(self, template_type, expected): - # Arrange, Act - converter = choose_parcel_converter(template_type) - # Assert - self.assertEqual(converter, expected) - - def test_choose_parcel_converter_throws_exception_when_template_type_not_supported(self): - # Arrange - not_supported_type = "!@#$%^&*()" - # Act, Assert - with self.assertRaises(CatalystwanException, msg=f"Template type {not_supported_type} not supported"): - choose_parcel_converter(not_supported_type) diff --git a/catalystwan/tests/config_migration/test_normalizer.py b/catalystwan/tests/config_migration/test_normalizer.py deleted file mode 100644 index 00ad48da6..000000000 --- a/catalystwan/tests/config_migration/test_normalizer.py +++ /dev/null @@ -1,98 +0,0 @@ -import unittest -from ipaddress import IPv4Address, IPv6Address -from typing import List, Literal -from unittest.mock import patch - -from catalystwan.api.configuration_groups.parcel import Global -from catalystwan.utils.config_migration.converters.feature_template import template_definition_normalization - -TestLiteral = Literal["castable_literal"] - - -class TestNormalizer(unittest.TestCase): - def setUp(self): - self.template_values = { - "key-one": "Simple string !@#$%^&*()-=[';/.,`~]", - "keyone": "Simplestring!@#$%^&*()-=[';/.,`~]", - "bool-value-as-string": "true", - "boolvalueasstring": "false", - "simple-int": 1, - "simpleint": 333333331231, - "simple-ints-in-list": [1, 2, 4, 5, 6, 7, 8, 9], - "simple-int-in-list": [1], - "simplestringsinlist": ["1232132", "!@#$%^&*()-=[';/.,`~]", ""], - "objects-in-list": [ - {"color": "lte", "hello-interval": 300000, "pmtu-discovery": "false"}, - {"color": "mpls", "pmtu-discovery": "false"}, - {"color": "biz-internet"}, - {"color": "public-internet"}, - ], - "nested-objects": [{"next-hop": [{"distance": 1}]}], - "ipv4-address": "10.0.0.2", - "ipv6addr": "2000:0:2:3::", - } - self.expected_result = { - "key_one": Global[str](value="Simple string !@#$%^&*()-=[';/.,`~]"), - "keyone": Global[str](value="Simplestring!@#$%^&*()-=[';/.,`~]"), - "bool_value_as_string": Global[bool](value=True), - "boolvalueasstring": Global[bool](value=False), - "simple_int": Global[int](value=1), - "simpleint": Global[int](value=333333331231), - "simple_ints_in_list": Global[List[int]](value=[1, 2, 4, 5, 6, 7, 8, 9]), - "simple_int_in_list": Global[List[int]](value=[1]), - "simplestringsinlist": Global[List[str]](value=["1232132", "!@#$%^&*()-=[';/.,`~]", ""]), - "objects_in_list": [ - { - "color": Global[str](value="lte"), - "hello_interval": Global[int](value=300000), - "pmtu_discovery": Global[bool](value=False), - }, - {"color": Global[str](value="mpls"), "pmtu_discovery": Global[bool](value=False)}, - {"color": Global[str](value="biz-internet")}, - {"color": Global[str](value="public-internet")}, - ], - "nested_objects": [{"next_hop": [{"distance": Global[int](value=1)}]}], - "ipv4_address": Global[IPv4Address](value=IPv4Address("10.0.0.2")), - "ipv6addr": Global[IPv6Address](value=IPv6Address("2000:0:2:3::")), - } - - def test_normalizer_handles_various_types_of_input(self): - # Arrange - expected_result = self.expected_result - # Act - returned_result = template_definition_normalization(self.template_values) - # Assert - self.assertDictEqual(expected_result, returned_result) - - def test_normalizer_handles_super_nested_input(self): - # Arrange - super_nested_input = { - "super_nested": {"level1": {"level2": {"level3": {"key_one": "value_one", "key_two": "value_two"}}}} - } - expected_result = { - "super_nested": { - "level1": { - "level2": { - "level3": {"key_one": Global[str](value="value_one"), "key_two": Global[str](value="value_two")} - } - } - } - } - - # Act - returned_result = template_definition_normalization(super_nested_input) - - # Assert - self.assertDictEqual(expected_result, returned_result) - - @patch("catalystwan.utils.config_migration.converters.feature_template.normalizer.CastableLiterals", [TestLiteral]) - def test_normalizer_literal_casting_when_literal_in_system_literals(self): - # Arrange - simple_input = {"in": "castable_literal"} - expected_result = {"in": Global[TestLiteral](value="castable_literal")} - - # Act - returned_result = template_definition_normalization(simple_input) - - # Assert - self.assertDictEqual(expected_result, returned_result) diff --git a/catalystwan/tests/test_feature_profile_api.py b/catalystwan/tests/test_feature_profile_api.py deleted file mode 100644 index 2174f0770..000000000 --- a/catalystwan/tests/test_feature_profile_api.py +++ /dev/null @@ -1,110 +0,0 @@ -import unittest -from unittest.mock import patch -from uuid import UUID - -from parameterized import parameterized # type: ignore - -from catalystwan.api.feature_profile_api import SystemFeatureProfileAPI -from catalystwan.models.configuration.feature_profile.sdwan.system import ( - AAAParcel, - BannerParcel, - BasicParcel, - BFDParcel, - GlobalParcel, - LoggingParcel, - MRFParcel, - NTPParcel, - OMPParcel, - SecurityParcel, - SNMPParcel, -) - -endpoint_mapping = { - AAAParcel: "aaa", - BannerParcel: "banner", - BasicParcel: "basic", - BFDParcel: "bfd", - GlobalParcel: "global", - LoggingParcel: "logging", - MRFParcel: "mrf", - NTPParcel: "ntp", - OMPParcel: "omp", - SecurityParcel: "security", - SNMPParcel: "snmp", -} - - -class TestSystemFeatureProfileAPI(unittest.TestCase): - def setUp(self): - self.profile_uuid = UUID("054d1b82-9fa7-43c6-98fb-4355da0d77ff") - self.parcel_uuid = UUID("7113505f-8cec-4420-8799-1a209357ba7e") - - @parameterized.expand(endpoint_mapping.items()) - @patch("catalystwan.session.ManagerSession") - @patch("catalystwan.endpoints.configuration.feature_profile.sdwan.system.SystemFeatureProfile") - def test_delete_method_with_valid_arguments(self, parcel, expected_path, mock_endpoint, mock_session): - # Arrange - api = SystemFeatureProfileAPI(mock_session) - api.endpoint = mock_endpoint - - # Act - api.delete(self.profile_uuid, parcel, self.parcel_uuid) - - # Assert - mock_endpoint.delete.assert_called_once_with(self.profile_uuid, expected_path, self.parcel_uuid) - - @parameterized.expand(endpoint_mapping.items()) - @patch("catalystwan.session.ManagerSession") - @patch("catalystwan.endpoints.configuration.feature_profile.sdwan.system.SystemFeatureProfile") - def test_get_method_with_valid_arguments(self, parcel, expected_path, mock_endpoint, mock_session): - # Arrange - api = SystemFeatureProfileAPI(mock_session) - api.endpoint = mock_endpoint - - # Act - api.get(self.profile_uuid, parcel, self.parcel_uuid) - - # Assert - mock_endpoint.get_by_id.assert_called_once_with(self.profile_uuid, expected_path, self.parcel_uuid) - - @parameterized.expand(endpoint_mapping.items()) - @patch("catalystwan.session.ManagerSession") - @patch("catalystwan.endpoints.configuration.feature_profile.sdwan.system.SystemFeatureProfile") - def test_get_all_method_with_valid_arguments(self, parcel, expected_path, mock_endpoint, mock_session): - # Arrange - api = SystemFeatureProfileAPI(mock_session) - api.endpoint = mock_endpoint - - # Act - api.get(self.profile_uuid, parcel) - - # Assert - mock_endpoint.get_all.assert_called_once_with(self.profile_uuid, expected_path) - - @parameterized.expand(endpoint_mapping.items()) - @patch("catalystwan.session.ManagerSession") - @patch("catalystwan.endpoints.configuration.feature_profile.sdwan.system.SystemFeatureProfile") - def test_create_method_with_valid_arguments(self, parcel, expected_path, mock_endpoint, mock_session): - # Arrange - api = SystemFeatureProfileAPI(mock_session) - api.endpoint = mock_endpoint - - # Act - api.create(self.profile_uuid, parcel) - - # Assert - mock_endpoint.create.assert_called_once_with(self.profile_uuid, expected_path, parcel) - - @parameterized.expand(endpoint_mapping.items()) - @patch("catalystwan.session.ManagerSession") - @patch("catalystwan.endpoints.configuration.feature_profile.sdwan.system.SystemFeatureProfile") - def test_update_method_with_valid_arguments(self, parcel, expected_path, mock_endpoint, mock_session): - # Arrange - api = SystemFeatureProfileAPI(mock_session) - api.endpoint = mock_endpoint - - # Act - api.update(self.profile_uuid, parcel, self.parcel_uuid) - - # Assert - mock_endpoint.update.assert_called_once_with(self.profile_uuid, expected_path, self.parcel_uuid, parcel) diff --git a/catalystwan/utils/config_migration/converters/feature_template/__init__.py b/catalystwan/utils/config_migration/converters/feature_template/__init__.py deleted file mode 100644 index 477bbd793..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import List - -from .factory_method import choose_parcel_converter, create_parcel_from_template -from .normalizer import template_definition_normalization - -__all__ = ["create_parcel_from_template", "choose_parcel_converter", "template_definition_normalization"] - - -def __dir__() -> "List[str]": - return list(__all__) diff --git a/catalystwan/utils/config_migration/converters/feature_template/aaa.py b/catalystwan/utils/config_migration/converters/feature_template/aaa.py deleted file mode 100644 index 4e9ac4d17..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/aaa.py +++ /dev/null @@ -1,79 +0,0 @@ -from copy import deepcopy -from typing import List - -from catalystwan.api.configuration_groups.parcel import Global -from catalystwan.models.configuration.feature_profile.sdwan.system import AAAParcel - - -class AAATemplateConverter: - supported_template_types = ("cisco_aaa", "cedge_aaa", "aaa") - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> AAAParcel: - """ - Creates an AAAParcel object based on the provided template values. - - Args: - name (str): The name of the AAAParcel. - description (str): The description of the AAAParcel. - template_values (dict): A dictionary containing the template values. - - Returns: - AAAParcel: An AAAParcel object with the provided template values. - """ - - def assign_authorization_servers(auth_server_list: List) -> None: - for auth_server in auth_server_list: - servers = auth_server.get("server", {}) - for server in servers: - key_enum = server.get("key_enum") - server["key_enum"] = Global[str](value=str(key_enum.value)) - - def assign_rules(rules: List) -> None: - for rule_item in rules: - rule_item["group"] = Global[List[str]](value=rule_item["group"].value.split(",")) - - parcel_values = deepcopy(template_values.get("aaa", template_values)) - parcel_values["parcel_name"] = name - parcel_values["parcel_description"] = description - - # Templates "aaa" and "cedge_aaa" have "auth_order" key, while "cisco_aaa" has "server_auth_order" key - if server_auth_order := parcel_values.get("server_auth_order"): - parcel_values["server_auth_order"] = Global[List[str]](value=server_auth_order.value.split(",")) - - if server_auth_order := parcel_values.get("auth_order"): - parcel_values["server_auth_order"] = server_auth_order - - if accounting := parcel_values.get("accounting"): - parcel_values["accounting_group"] = accounting["dot1x"]["default"]["start_stop"]["accounting_group"] - - if authorization := parcel_values.get("authentication"): - parcel_values["authentication_group"] = authorization["dot1x"]["default"]["authentication_group"] - - for server in ["radius", "tacacs"]: - if auth_server_list := parcel_values.get(server): - assign_authorization_servers(auth_server_list) - - for rule in ["accounting_rule", "authorization_rule"]: - if existing_rule := parcel_values.get(rule): - assign_rules(existing_rule) - - for key in [ - "radius_client", - "radius_trustsec_group", - "rda_server_key", - "domain_stripping", - "auth_type", - "port", - "cts_auth_list", - "auth_order", - "usergroup", - "ciscotacro_user", - "ciscotacrw_user", - "accounting", - "authentication", - "radius_trustsec", - ]: - parcel_values.pop(key, None) - - return AAAParcel(**parcel_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/banner.py b/catalystwan/utils/config_migration/converters/feature_template/banner.py deleted file mode 100644 index 5aa4550be..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/banner.py +++ /dev/null @@ -1,20 +0,0 @@ -from catalystwan.models.configuration.feature_profile.sdwan.system import BannerParcel - - -class BannerTemplateConverter: - supported_template_types = ("cisco_banner",) - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> BannerParcel: - """ - Creates a BannerParcel object based on the provided template values. - - Args: - name (str): The name of the BannerParcel. - description (str): The description of the BannerParcel. - template_values (dict): A dictionary containing the template values. - - Returns: - BannerParcel: A BannerParcel object with the provided template values. - """ - return BannerParcel(parcel_name=name, parcel_description=description, **template_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/base.py b/catalystwan/utils/config_migration/converters/feature_template/base.py deleted file mode 100644 index c3144e720..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/base.py +++ /dev/null @@ -1,9 +0,0 @@ -from typing_extensions import Protocol - -from catalystwan.models.configuration.feature_profile.sdwan.system import AnySystemParcel - - -class FeatureTemplateConverter(Protocol): - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> AnySystemParcel: - ... diff --git a/catalystwan/utils/config_migration/converters/feature_template/basic.py b/catalystwan/utils/config_migration/converters/feature_template/basic.py deleted file mode 100644 index 968c50aea..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/basic.py +++ /dev/null @@ -1,74 +0,0 @@ -from copy import deepcopy - -from catalystwan.api.configuration_groups.parcel import Global, as_default, as_global -from catalystwan.models.configuration.feature_profile.sdwan.system import BasicParcel -from catalystwan.models.configuration.feature_profile.sdwan.system.basic import ConsoleBaudRate -from catalystwan.utils.timezone import Timezone - - -class SystemToBasicTemplateConverter: - supported_template_types = ("cisco_system", "system-vsmart", "system-vedge") - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> BasicParcel: - """ - Converts the provided template values into a BasicParcel object. - - Args: - name (str): The name of the BasicParcel. - description (str): The description of the BasicParcel. - template_values (dict): A dictionary containing the template values. - - Returns: - BasicParcel: A BasicParcel object with the provided template values. - """ - parcel_values = deepcopy(template_values) - parcel_values = { - "parcel_name": name, - "parcel_description": description, - } - - track_default_gateway = template_values.get("track_default_gateway", as_default(False)).value - if track_default_gateway == "": - track_default_gateway = False - parcel_values["track_default_gateway"] = as_global(track_default_gateway) - - clock_timezone = template_values.get("timezone", as_default("UTC")).value - parcel_values["clock"] = {"timezone": Global[Timezone](value=clock_timezone)} - - console_baud_rate = template_values.get("console_baud_rate", as_default("9600")).value - if console_baud_rate == "": - console_baud_rate = "9600" # Default value for console baud rate - parcel_values["console_baud_rate"] = Global[ConsoleBaudRate](value=console_baud_rate) - - parcel_values["gps_location"] = {} - - longitude = parcel_values.get("longitude", as_default("")).value - latitude = parcel_values.get("latitude", as_default("")).value - if longitude and latitude: - parcel_values["gps_location"]["longitude"] = longitude - parcel_values["gps_location"]["latitude"] = latitude - - if mobile_number := parcel_values.get("mobile_number", []): - parcel_values["gps_location"]["geo_fencing"] = { - "enable": as_global(True), - "range": parcel_values.get("range", as_default(100)), - "sms": {"enable": as_global(True), "mobile_number": mobile_number}, - } - - # Remove unnecessary keys from template_values - for key in [ - "timezone", - "longitude", - "latitude", - "mobile_number", - "range", - "site_id", - "system_ip", - "host_name", - "enable", - "tracker", - ]: - parcel_values.pop(key, None) - - return BasicParcel(**parcel_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/bfd.py b/catalystwan/utils/config_migration/converters/feature_template/bfd.py deleted file mode 100644 index 5b30dc854..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/bfd.py +++ /dev/null @@ -1,25 +0,0 @@ -from catalystwan.models.configuration.feature_profile.sdwan.system import BFDParcel - - -class BFDTemplateConverter: - supported_template_types = ("cisco_bfd", "bfd-vedge") - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> BFDParcel: - """ - Creates a BFDParcel object based on the provided template values. - - Args: - name (str): The name of the BFDParcel. - description (str): The description of the BFDParcel. - template_values (dict): A dictionary containing the template values. - - Returns: - BFDParcel: A BFDParcel object with the provided template values. - """ - parcel_values = { - "parcel_name": name, - "parcel_description": description, - "colors": template_values.get("color"), - } - return BFDParcel(**parcel_values) # type: ignore diff --git a/catalystwan/utils/config_migration/converters/feature_template/factory_method.py b/catalystwan/utils/config_migration/converters/feature_template/factory_method.py deleted file mode 100644 index ed87775f8..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/factory_method.py +++ /dev/null @@ -1,81 +0,0 @@ -import json -import logging -from typing import Any, Dict, cast - -from catalystwan.api.template_api import FeatureTemplateInformation -from catalystwan.exceptions import CatalystwanException -from catalystwan.models.configuration.feature_profile.sdwan.system import AnySystemParcel -from catalystwan.utils.feature_template.find_template_values import find_template_values - -from .aaa import AAATemplateConverter -from .banner import BannerTemplateConverter -from .base import FeatureTemplateConverter -from .basic import SystemToBasicTemplateConverter -from .bfd import BFDTemplateConverter -from .global_ import GlobalTemplateConverter -from .logging_ import LoggingTemplateConverter -from .normalizer import template_definition_normalization -from .ntp import NTPTemplateConverter -from .omp import OMPTemplateConverter -from .security import SecurityTemplateConverter - -logger = logging.getLogger(__name__) - -available_converters = [ - AAATemplateConverter, - BannerTemplateConverter, - SecurityTemplateConverter, - SystemToBasicTemplateConverter, - BFDTemplateConverter, - GlobalTemplateConverter, - LoggingTemplateConverter, - OMPTemplateConverter, - NTPTemplateConverter, -] - - -supported_parcel_converters: Dict[Any, Any] = { - converter.supported_template_types: converter for converter in available_converters # type: ignore -} - - -def choose_parcel_converter(template_type: str) -> FeatureTemplateConverter: - """ - This function is used to choose the correct parcel factory based on the template type. - - Args: - template_type (str): The template type used to determine the correct factory. - - Returns: - BaseFactory: The chosen parcel factory. - - Raises: - ValueError: If the template type is not supported. - """ - for key in supported_parcel_converters.keys(): - if template_type in key: - converter = supported_parcel_converters[key] - logger.debug(f"Choosen converter {converter} based on template type {template_type}") - return converter - raise CatalystwanException(f"Template type {template_type} not supported") - - -def create_parcel_from_template(template: FeatureTemplateInformation) -> AnySystemParcel: - """ - Creates a new instance of a _ParcelBase based on the given template. - - Args: - template (FeatureTemplateInformation): The template to use for creating the _ParcelBase instance. - - Returns: - _ParcelBase: The created _ParcelBase instance. - - Raises: - ValueError: If the given template type is not supported. - """ - converter = choose_parcel_converter(template.template_type) - template_definition_as_dict = json.loads(cast(str, template.template_definiton)) - template_values = find_template_values(template_definition_as_dict) - template_values_normalized = template_definition_normalization(template_values) - logger.debug(f"Normalized template {template.name}: {template_values_normalized}") - return converter.create_parcel(template.name, template.description, template_values_normalized) diff --git a/catalystwan/utils/config_migration/converters/feature_template/global_.py b/catalystwan/utils/config_migration/converters/feature_template/global_.py deleted file mode 100644 index ade77de6f..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/global_.py +++ /dev/null @@ -1,15 +0,0 @@ -from catalystwan.models.configuration.feature_profile.sdwan.system import GlobalParcel - - -class GlobalTemplateConverter: - supported_template_types = ("cedge_global",) - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> GlobalParcel: - """ - Creates an Logging object based on the provided template values. - - Returns: - GlobalParcel: A GlobalParcel object with the provided template values. - """ - return GlobalParcel(parcel_name=name, parcel_description=description, **template_values) # type: ignore diff --git a/catalystwan/utils/config_migration/converters/feature_template/logging_.py b/catalystwan/utils/config_migration/converters/feature_template/logging_.py deleted file mode 100644 index 399be09f1..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/logging_.py +++ /dev/null @@ -1,50 +0,0 @@ -from copy import deepcopy -from typing import Dict, List - -from catalystwan.api.configuration_groups.parcel import Global -from catalystwan.models.configuration.feature_profile.sdwan.system import LoggingParcel -from catalystwan.models.configuration.feature_profile.sdwan.system.logging_parcel import CypherSuite - - -class LoggingTemplateConverter: - supported_template_types = ("cisco_logging", "logging") - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> LoggingParcel: - """ - Creates an Logging object based on the provided template values. - - Returns: - Logging: An Logging object with the provided template values. - """ - - def parse_server_name(servers: List) -> None: - for server in servers: - server["name"] = Global[str](value=str(server["name"].value)) - - def set_disk(parcel_values: Dict) -> None: - parcel_values["disk"] = { - "disk_enable": parcel_values["enable"], - "file": {"disk_file_size": parcel_values["size"], "disk_file_rotate": parcel_values["rotate"]}, - } - for key in ["enable", "size", "rotate"]: - parcel_values.pop(key, None) - - parcel_values = deepcopy(template_values) - parcel_values["name"] = name - parcel_values["description"] = description - - if tls_profiles := parcel_values.get("tls_profile"): - for profile in tls_profiles: - del profile["auth_type"] - if profile.get("ciphersuite_list"): - profile["ciphersuite_list"] = Global[List[CypherSuite]](value=profile["ciphersuite_list"].value) - - for server in ["server", "ipv6_server"]: - if target_server := parcel_values.get(server): - parse_server_name(target_server) - - if parcel_values.get("enable"): - set_disk(parcel_values) - - return LoggingParcel(**parcel_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/normalizer.py b/catalystwan/utils/config_migration/converters/feature_template/normalizer.py deleted file mode 100644 index ecc1aa74b..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/normalizer.py +++ /dev/null @@ -1,76 +0,0 @@ -from ipaddress import AddressValueError, IPv4Address, IPv6Address -from typing import List, Union, get_args - -from catalystwan.api.configuration_groups.parcel import Global, as_global -from catalystwan.models.common import TLOCColor -from catalystwan.models.configuration.feature_profile.sdwan.system.logging_parcel import ( - AuthType, - CypherSuite, - Priority, - TlsVersion, -) -from catalystwan.models.configuration.feature_profile.sdwan.system.mrf import EnableMrfMigration, Role - -CastableLiterals = [Priority, TlsVersion, AuthType, CypherSuite, Role, EnableMrfMigration, TLOCColor] - -CastedTypes = Union[ - Global[bool], - Global[str], - Global[int], - Global[List[str]], - Global[List[int]], - Global[IPv4Address], - Global[IPv6Address], -] - - -def to_snake_case(s: str) -> str: - """Converts a string from kebab-case to snake_case.""" - return s.replace("-", "_") - - -def cast_value_to_global(value: Union[str, int, List[str], List[int]]) -> CastedTypes: - """Casts value to Global.""" - if isinstance(value, list): - value_type = Global[List[int]] if isinstance(value[0], int) else Global[List[str]] - return value_type(value=value) # type: ignore - - if isinstance(value, str): - if value.lower() == "true": - return Global[bool](value=True) - elif value.lower() == "false": - return Global[bool](value=False) - try: - ipv4_address = IPv4Address(value) - return Global[IPv4Address](value=ipv4_address) - except AddressValueError: - pass - try: - ipv6_address = IPv6Address(value) - return Global[IPv6Address](value=ipv6_address) - except AddressValueError: - pass - for literal in CastableLiterals: - if value in get_args(literal): - return Global[literal](value=value) # type: ignore - - return as_global(value) # type: ignore - - -def transform_dict(d: dict) -> dict: - """Transforms a nested dictionary into a normalized form.""" - - def transform_value(value: Union[dict, list, str, int]) -> Union[CastedTypes, dict, list]: - if isinstance(value, dict): - return transform_dict(value) - elif isinstance(value, list): - if all(isinstance(v, dict) for v in value): - return [transform_value(item) for item in value] - return cast_value_to_global(value) - - return {to_snake_case(key): transform_value(val) for key, val in d.items()} - - -def template_definition_normalization(template_definition: dict) -> dict: - """Normalizes a template definition by changing keys to snake_case and casting all leafs values to global types.""" - return transform_dict(template_definition) diff --git a/catalystwan/utils/config_migration/converters/feature_template/ntp.py b/catalystwan/utils/config_migration/converters/feature_template/ntp.py deleted file mode 100644 index a766abcff..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/ntp.py +++ /dev/null @@ -1,27 +0,0 @@ -from catalystwan.models.configuration.feature_profile.sdwan.system import NTPParcel - - -class NTPTemplateConverter: - supported_template_types = ("cisco_ntp", "ntp") - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> NTPParcel: - """ - Creates an Logging object based on the provided template values. - - Returns: - Logging: An Logging object with the provided template values. - """ - parcel_values = { - "parcel_name": name, - "parcel_description": description, - "server": template_values.get("server", []), - } - - if keys := template_values.get("keys", {}): - parcel_values["authentication"] = { - "authentication_keys": keys.get("authentication_keys", []), - "trusted_keys": keys.get("trusted", None), - } - - return NTPParcel(**parcel_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/omp.py b/catalystwan/utils/config_migration/converters/feature_template/omp.py deleted file mode 100644 index e3703f667..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/omp.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Dict, List - -from catalystwan.api.configuration_groups.parcel import Global, as_default, as_global -from catalystwan.models.configuration.feature_profile.sdwan.system import OMPParcel - - -class OMPTemplateConverter: - supported_template_types = ("cisco_omp", "omp-vedge", "omp-vsmart") - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> OMPParcel: - """ - Creates an OMPParcel object based on the provided template values. - - Args: - name (str): The name of the OMPParcel. - description (str): The description of the OMPParcel. - template_values (dict): A dictionary containing the template values. - - Returns: - OMPParcel: An OMPParcel object with the provided template values. - """ - - def create_advertise_dict(advertise_list: List) -> Dict: - return {definition["protocol"].value: Global[bool](value=True) for definition in advertise_list} - - parcel_values = { - "parcel_name": name, - "parcel_description": description, - "ecmp_limit": as_global(float(template_values.get("ecmp_limit", as_default(4)).value)), - "advertise_ipv4": create_advertise_dict(template_values.get("advertise", [])), - "advertise_ipv6": create_advertise_dict(template_values.get("ipv6_advertise", [])), - } - - return OMPParcel(**parcel_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/security.py b/catalystwan/utils/config_migration/converters/feature_template/security.py deleted file mode 100644 index c080efc1b..000000000 --- a/catalystwan/utils/config_migration/converters/feature_template/security.py +++ /dev/null @@ -1,42 +0,0 @@ -from typing import List - -from catalystwan.api.configuration_groups.parcel import Global, as_default -from catalystwan.models.configuration.feature_profile.sdwan.system import SecurityParcel -from catalystwan.models.configuration.feature_profile.sdwan.system.security import IntegrityType - - -class SecurityTemplateConverter: - """ - A class for converting template values into a SecurityParcel object. - - Attributes: - supported_template_types (tuple): A tuple of supported template types. - """ - - supported_template_types = ( - "cisco_security", - "security", - "security-vsmart", - "security-vedge", - ) - - @staticmethod - def create_parcel(name: str, description: str, template_values: dict) -> SecurityParcel: - """ - Creates a SecurityParcel object based on the provided template values. - - Args: - name (str): The name of the SecurityParcel. - description (str): The description of the SecurityParcel. - template_values (dict): A dictionary containing the template values. - - Returns: - SecurityParcel: A SecurityParcel object with the provided template values. - """ - parcel_values = { - "parcel_name": name, - "parcel_description": description, - } - if integrity_type := template_values.get("integrity_type", as_default([])).value: - parcel_values["integrity_type"] = Global[List[IntegrityType]](value=integrity_type) # type: ignore - return SecurityParcel(**parcel_values) # type: ignore diff --git a/catalystwan/utils/config_migration/creators/config_group.py b/catalystwan/utils/config_migration/creators/config_group.py deleted file mode 100644 index 4294b5460..000000000 --- a/catalystwan/utils/config_migration/creators/config_group.py +++ /dev/null @@ -1,123 +0,0 @@ -import logging -from datetime import datetime -from typing import List -from uuid import UUID - -from catalystwan.endpoints.configuration_feature_profile import ConfigurationFeatureProfile -from catalystwan.endpoints.configuration_group import ConfigGroup -from catalystwan.models.configuration.config_migration import UX2Config -from catalystwan.models.configuration.feature_profile.common import FeatureProfileCreationPayload -from catalystwan.session import ManagerSession - - -class ConfigGroupCreator: - """ - Creates a configuration group and attach feature profiles for migrating UX1 templates to UX2. - """ - - def __init__(self, session: ManagerSession, config: UX2Config, logger: logging.Logger): - """ - Args: - session (ManagerSession): A valid Manager API session. - config (UX2Config): The UX2 configuration to migrate. - logger (logging.Logger): A logger for logging messages. - """ - self.session = session - self.config = config - self.logger = logger - self.profile_ids: List[UUID] = [] - - def create(self) -> ConfigGroup: - """ - Creates a configuration group and attach feature profiles for migrating UX1 templates to UX2. - - Returns: - ConfigGroup: The created configuration group. - """ - self.created_at = datetime.now().strftime("%Y_%m_%d_%H_%M_%S") - self._create_sdwan_system_feature_profile() - self._create_sdwan_policy_objects_feature_profile() - config_group_id = self._create_configuration_group() - return self.session.api.config_group.get(config_group_id) # type: ignore[return-value] - - def _create_sdwan_system_feature_profile(self): - """ - Creates a SDWAN System Feature Profile for migrating UX1 Templates to UX2. - - Args: - session (ManagerSession): A valid Manager API session. - name (str): The name of the SDWAN System Feature Profile. - - Returns: - UUID: The ID of the created SDWAN System Feature Profile. - - Raises: - ManagerHTTPError: If the SDWAN System Feature Profile cannot be created. - """ - system_name = f"MIGRATION_SDWAN_SYSTEM_FEATURE_PROFILE_{self.created_at}" - profile_system = FeatureProfileCreationPayload( - name=system_name, description="Profile for migrating UX1 Templates to UX2" - ) - system_id = self.session.endpoints.configuration_feature_profile.create_sdwan_system_feature_profile( - profile_system - ).id - self.logger.info(f"Created SDWAN System Feature Profile {system_name} with ID: {system_id}") - self.profile_ids.append(system_id) - - def _create_sdwan_policy_objects_feature_profile(self): - """ - Creates a SDWAN Policy Objects Feature Profile for migrating UX1 Policies to UX2. - - Args: - session (ManagerSession): A valid Manager API session. - name (str): The name of the SDWAN Policy Objects Feature Profile. - - Returns: - UUID: The ID of the created SDWAN Policy Objects Feature Profile. - - Raises: - ManagerHTTPError: If the SDWAN Policy Objects Feature Profile cannot be created. - """ - policy_objects_name = f"MIGRATION_SDWAN_POLICY_OBJECTS_FEATURE_PROFILE_{self.created_at}" - # TODO: Find a way to create a policy object profile - # for now there is no API or UI for creating a policy object profile - profile_policy_objects = FeatureProfileCreationPayload( # noqa: F841 - name=policy_objects_name, description="Profile for migrating UX1 Policies to UX2" - ) - - # Using default profile name for SDWAN Policy Objects Feature Profile - policy_object_id = ( - ConfigurationFeatureProfile(self.session) - .get_sdwan_feature_profiles() - .filter(profile_name="Default_Policy_Object_Profile") - .single_or_default() - ).profile_id - self.logger.info( - f"Created SDWAN Policy Object Feature Profile {policy_objects_name} with ID: {policy_object_id}" - ) - self.profile_ids.append(policy_object_id) - - def _create_configuration_group(self): - """ - Creates a configuration group and attach feature profiles for migrating UX1 templates to UX2. - - Args: - session (ManagerSession): A valid Manager API session. - name (str): The name of the configuration group. - profile_ids (List[UUID]): The IDs of the feature profiles to include in the configuration group. - - Returns: - UUID: The ID of the created configuration group. - - Raises: - ManagerHTTPError: If the configuration cannot be pushed. - """ - config_group_name = f"SDWAN_CONFIG_GROUP_{self.created_at}" - config_group_id = self.session.api.config_group.create( - name=config_group_name, - description="SDWAN Config Group created for migrating UX1 Templates to UX2", - solution="sdwan", - profile_ids=self.profile_ids, - ).id - self.logger.info(f"Created SDWAN Configuration Group {config_group_name} with ID: {config_group_id}") - return config_group_id diff --git a/catalystwan/utils/timezone.py b/catalystwan/utils/timezone.py index a607a0d77..e210b891e 100644 --- a/catalystwan/utils/timezone.py +++ b/catalystwan/utils/timezone.py @@ -1,420 +1,422 @@ -from typing import Literal +# Copyright 2023 Cisco Systems, Inc. and its affiliates -Timezone = Literal[ - "Europe/Andorra", - "Asia/Dubai", - "Asia/Kabul", - "America/Antigua", - "America/Anguilla", - "Europe/Tirane", - "Asia/Yerevan", - "Africa/Luanda", - "Antarctica/McMurdo", - "Antarctica/Rothera", - "Antarctica/Palmer", - "Antarctica/Mawson", - "Antarctica/Davis", - "Antarctica/Casey", - "Antarctica/Vostok", - "Antarctica/DumontDUrville", - "Antarctica/Syowa", - "America/Argentina/Buenos_Aires", - "America/Argentina/Cordoba", - "America/Argentina/Salta", - "America/Argentina/Jujuy", - "America/Argentina/Tucuman", - "America/Argentina/Catamarca", - "America/Argentina/La_Rioja", - "America/Argentina/San_Juan", - "America/Argentina/Mendoza", - "America/Argentina/San_Luis", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Ushuaia", - "Pacific/Pago_Pago", - "Europe/Vienna", - "Australia/Lord_Howe", - "Antarctica/Macquarie", - "Australia/Hobart", - "Australia/Currie", - "Australia/Melbourne", - "Australia/Sydney", - "Australia/Broken_Hill", - "Australia/Brisbane", - "Australia/Lindeman", - "Australia/Adelaide", - "Australia/Darwin", - "Australia/Perth", - "Australia/Eucla", - "America/Aruba", - "Europe/Mariehamn", - "Asia/Baku", - "Europe/Sarajevo", - "America/Barbados", - "Asia/Dhaka", - "Europe/Brussels", - "Africa/Ouagadougou", - "Europe/Sofia", - "Asia/Bahrain", - "Africa/Bujumbura", - "Africa/Porto-Novo", - "America/St_Barthelemy", - "Atlantic/Bermuda", - "Asia/Brunei", - "America/La_Paz", - "America/Kralendijk", - "America/Noronha", - "America/Belem", - "America/Fortaleza", - "America/Recife", - "America/Araguaina", - "America/Maceio", - "America/Bahia", - "America/Sao_Paulo", - "America/Campo_Grande", - "America/Cuiaba", - "America/Santarem", - "America/Porto_Velho", - "America/Boa_Vista", - "America/Manaus", - "America/Eirunepe", - "America/Rio_Branco", - "America/Nassau", - "Asia/Thimphu", - "Africa/Gaborone", - "Europe/Minsk", - "America/Belize", - "America/St_Johns", - "America/Halifax", - "America/Glace_Bay", - "America/Moncton", - "America/Goose_Bay", - "America/Blanc-Sablon", - "America/Toronto", - "America/Nipigon", - "America/Thunder_Bay", - "America/Iqaluit", - "America/Pangnirtung", - "America/Resolute", - "America/Atikokan", - "America/Rankin_Inlet", - "America/Winnipeg", - "America/Rainy_River", - "America/Regina", - "America/Swift_Current", - "America/Edmonton", - "America/Cambridge_Bay", - "America/Yellowknife", - "America/Inuvik", - "America/Creston", - "America/Dawson_Creek", - "America/Vancouver", - "America/Whitehorse", - "America/Dawson", - "Indian/Cocos", - "Africa/Kinshasa", - "Africa/Lubumbashi", - "Africa/Bangui", - "Africa/Brazzaville", - "Europe/Zurich", - "Africa/Abidjan", - "Pacific/Rarotonga", - "America/Santiago", - "Pacific/Easter", - "Africa/Douala", - "Asia/Shanghai", - "Asia/Harbin", - "Asia/Chongqing", - "Asia/Urumqi", - "Asia/Kashgar", - "America/Bogota", - "America/Costa_Rica", - "America/Havana", - "Atlantic/Cape_Verde", - "America/Curacao", - "Indian/Christmas", - "Asia/Nicosia", - "Europe/Prague", - "Europe/Berlin", - "Europe/Busingen", - "Africa/Djibouti", - "Europe/Copenhagen", - "America/Dominica", - "America/Santo_Domingo", - "Africa/Algiers", - "America/Guayaquil", - "Pacific/Galapagos", - "Europe/Tallinn", - "Africa/Cairo", - "Africa/El_Aaiun", - "Africa/Asmara", - "Europe/Madrid", - "Africa/Ceuta", - "Atlantic/Canary", - "Africa/Addis_Ababa", - "Europe/Helsinki", - "Pacific/Fiji", - "Atlantic/Stanley", - "Pacific/Chuuk", - "Pacific/Pohnpei", - "Pacific/Kosrae", - "Atlantic/Faroe", - "Europe/Paris", - "Africa/Libreville", - "Europe/London", - "America/Grenada", - "Asia/Tbilisi", - "America/Cayenne", - "Europe/Guernsey", - "Africa/Accra", - "Europe/Gibraltar", - "America/Godthab", - "America/Danmarkshavn", - "America/Scoresbysund", - "America/Thule", - "Africa/Banjul", - "Africa/Conakry", - "America/Guadeloupe", - "Africa/Malabo", - "Europe/Athens", - "Atlantic/South_Georgia", - "America/Guatemala", - "Pacific/Guam", - "Africa/Bissau", - "America/Guyana", - "Asia/Hong_Kong", - "America/Tegucigalpa", - "Europe/Zagreb", - "America/Port-au-Prince", - "Europe/Budapest", - "Asia/Jakarta", - "Asia/Pontianak", - "Asia/Makassar", - "Asia/Jayapura", - "Europe/Dublin", - "Asia/Jerusalem", - "Europe/Isle_of_Man", - "Asia/Kolkata", - "Indian/Chagos", - "Asia/Baghdad", - "Asia/Tehran", - "Atlantic/Reykjavik", - "Europe/Rome", - "Europe/Jersey", - "America/Jamaica", - "Asia/Amman", - "Asia/Tokyo", - "Africa/Nairobi", - "Asia/Bishkek", - "Asia/Phnom_Penh", - "Pacific/Tarawa", - "Pacific/Enderbury", - "Pacific/Kiritimati", - "Indian/Comoro", - "America/St_Kitts", - "Asia/Pyongyang", - "Asia/Seoul", - "Asia/Kuwait", - "America/Cayman", - "Asia/Almaty", - "Asia/Qyzylorda", - "Asia/Aqtobe", - "Asia/Aqtau", - "Asia/Oral", - "Asia/Vientiane", - "Asia/Beirut", - "America/St_Lucia", - "Europe/Vaduz", - "Asia/Colombo", - "Africa/Monrovia", - "Africa/Maseru", - "Europe/Vilnius", - "Europe/Luxembourg", - "Europe/Riga", - "Africa/Tripoli", - "Africa/Casablanca", - "Europe/Monaco", - "Europe/Chisinau", - "Europe/Podgorica", - "America/Marigot", - "Indian/Antananarivo", - "Pacific/Majuro", - "Pacific/Kwajalein", - "Europe/Skopje", - "Africa/Bamako", - "Asia/Rangoon", - "Asia/Ulaanbaatar", - "Asia/Hovd", - "Asia/Choibalsan", - "Asia/Macau", - "Pacific/Saipan", - "America/Martinique", - "Africa/Nouakchott", - "America/Montserrat", - "Europe/Malta", - "Indian/Mauritius", - "Indian/Maldives", - "Africa/Blantyre", - "America/Mexico_City", - "America/Cancun", - "America/Merida", - "America/Monterrey", - "America/Matamoros", - "America/Mazatlan", - "America/Chihuahua", - "America/Ojinaga", - "America/Hermosillo", - "America/Tijuana", - "America/Santa_Isabel", - "America/Bahia_Banderas", - "Asia/Kuala_Lumpur", - "Asia/Kuching", - "Africa/Maputo", - "Africa/Windhoek", - "Pacific/Noumea", - "Africa/Niamey", - "Pacific/Norfolk", - "Africa/Lagos", - "America/Managua", - "Europe/Amsterdam", - "Europe/Oslo", - "Asia/Kathmandu", - "Pacific/Nauru", - "Pacific/Niue", - "Pacific/Auckland", - "Pacific/Chatham", - "Asia/Muscat", - "America/Panama", - "America/Lima", - "Pacific/Tahiti", - "Pacific/Marquesas", - "Pacific/Gambier", - "Pacific/Port_Moresby", - "Asia/Manila", - "Asia/Karachi", - "Europe/Warsaw", - "America/Miquelon", - "Pacific/Pitcairn", - "America/Puerto_Rico", - "Asia/Gaza", - "Asia/Hebron", - "Europe/Lisbon", - "Atlantic/Madeira", - "Atlantic/Azores", - "Pacific/Palau", - "America/Asuncion", - "Asia/Qatar", - "Indian/Reunion", - "Europe/Bucharest", - "Europe/Belgrade", - "Europe/Kaliningrad", - "Europe/Moscow", - "Europe/Volgograd", - "Europe/Samara", - "Asia/Yekaterinburg", - "Asia/Omsk", - "Asia/Novosibirsk", - "Asia/Novokuznetsk", - "Asia/Krasnoyarsk", - "Asia/Irkutsk", - "Asia/Yakutsk", - "Asia/Khandyga", - "Asia/Vladivostok", - "Asia/Sakhalin", - "Asia/Ust-Nera", - "Asia/Magadan", - "Asia/Kamchatka", - "Asia/Anadyr", - "Africa/Kigali", - "Asia/Riyadh", - "Pacific/Guadalcanal", - "Indian/Mahe", - "Africa/Khartoum", - "Europe/Stockholm", - "Asia/Singapore", - "Atlantic/St_Helena", - "Europe/Ljubljana", - "Arctic/Longyearbyen", - "Europe/Bratislava", - "Africa/Freetown", - "Europe/San_Marino", - "Africa/Dakar", - "Africa/Mogadishu", - "America/Paramaribo", - "Africa/Juba", - "Africa/Sao_Tome", - "America/El_Salvador", - "America/Lower_Princes", - "Asia/Damascus", - "Africa/Mbabane", - "America/Grand_Turk", - "Africa/Ndjamena", - "Indian/Kerguelen", - "Africa/Lome", - "Asia/Bangkok", - "Asia/Dushanbe", - "Pacific/Fakaofo", - "Asia/Dili", - "Asia/Ashgabat", - "Africa/Tunis", - "Pacific/Tongatapu", - "Europe/Istanbul", - "America/Port_of_Spain", - "Pacific/Funafuti", - "Asia/Taipei", - "Africa/Dar_es_Salaam", - "Europe/Kiev", - "Europe/Uzhgorod", - "Europe/Zaporozhye", - "Europe/Simferopol", - "Africa/Kampala", - "Pacific/Johnston", - "Pacific/Midway", - "Pacific/Wake", - "America/New_York", - "America/Detroit", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Indiana/Indianapolis", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indiana/Vevay", - "America/Chicago", - "America/Indiana/Tell_City", - "America/Indiana/Knox", - "America/Menominee", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/North_Dakota/Beulah", - "America/Denver", - "America/Boise", - "America/Phoenix", - "America/Los_Angeles", - "America/Anchorage", - "America/Juneau", - "America/Sitka", - "America/Yakutat", - "America/Nome", - "America/Adak", - "America/Metlakatla", - "Pacific/Honolulu", - "America/Montevideo", - "Asia/Samarkand", - "Asia/Tashkent", - "Europe/Vatican", - "America/St_Vincent", - "America/Caracas", - "America/Tortola", - "America/St_Thomas", - "Asia/Ho_Chi_Minh", - "Pacific/Efate", - "Pacific/Wallis", - "Pacific/Apia", - "Asia/Aden", - "Indian/Mayotte", - "Africa/Johannesburg", - "Africa/Lusaka", - "Africa/Harare", - "UTC", -] +from enum import Enum + + +class Timezone(str, Enum): + EUROPE_ANDORRA = "Europe/Andorra" + ASIA_DUBAI = "Asia/Dubai" + ASIA_KABUL_ = "Asia/Kabul" + AMERICA_ANTIGUA_ = "America/Antigua" + AMERICA_ANGUILLA_ = "America/Anguilla" + EUROPE_TIRANE_ = "Europe/Tirane" + ASIA_YEREVAN_ = "Asia/Yerevan" + AFRICA_LUANDA_ = "Africa/Luanda" + ANTARCTICA_MCMURDO = "Antarctica/McMurdo" + ANTARCTICA_ROTHERA = "Antarctica/Rothera" + ANTARCTICA_PALMER = "Antarctica/Palmer" + ANTARCTICA_MAWSON = "Antarctica/Mawson" + ANTARCTICA_DAVIS = "Antarctica/Davis" + ANTARCTICA_CASEY = "Antarctica/Casey" + ANTARCTICA_VOSTOK = "Antarctica/Vostok" + ANTARCTICA_DUMONTDURVILLE = "Antarctica/DumontDUrville" + ANTARCTICA_SYOWA = "Antarctica/Syowa" + AMERICA_ARGENTINA_BUENOS_AIRES = "America/Argentina/Buenos_Aires" + AMERICA_ARGENTINA_CORDOBA = "America/Argentina/Cordoba" + AMERICA_ARGENTINA_SALTA = "America/Argentina/Salta" + AMERICA_ARGENTINA_JUJUY = "America/Argentina/Jujuy" + AMERICA_ARGENTINA_TUCUMAN = "America/Argentina/Tucuman" + AMERICA_ARGENTINA_CATAMARCA = "America/Argentina/Catamarca" + AMERICA_ARGENTINA_LA_RIOJA = "America/Argentina/La_Rioja" + AMERICA_ARGENTINA_SAN_JUAN = "America/Argentina/San_Juan" + AMERICA_ARGENTINA_MENDOZA = "America/Argentina/Mendoza" + AMERICA_ARGENTINA_SAN_LUIS = "America/Argentina/San_Luis" + AMERICA_ARGENTINA_RIO_GALLEGOS = "America/Argentina/Rio_Gallegos" + AMERICA_ARGENTINA_USHUAIA = "America/Argentina/Ushuaia" + PACIFIC_PAGO_PAGO = "Pacific/Pago_Pago" + EUROPE_VIENNA = "Europe/Vienna" + AUSTRALIA_LORD_HOWE = "Australia/Lord_Howe" + ANTARCTICA_MACQUARIE = "Antarctica/Macquarie" + AUSTRALIA_HOBART = "Australia/Hobart" + AUSTRALIA_CURRIE = "Australia/Currie" + AUSTRALIA_MELBOURNE = "Australia/Melbourne" + AUSTRALIA_SYDNEY = "Australia/Sydney" + AUSTRALIA_BROKEN_HILL = "Australia/Broken_Hill" + AUSTRALIA_BRISBANE = "Australia/Brisbane" + AUSTRALIA_LINDEMAN = "Australia/Lindeman" + AUSTRALIA_ADELAIDE = "Australia/Adelaide" + AUSTRALIA_DARWIN = "Australia/Darwin" + AUSTRALIA_PERTH = "Australia/Perth" + AUSTRALIA_EUCLA = "Australia/Eucla" + AMERICA_ARUBA = "America/Aruba" + EUROPE_MARIEHAMN = "Europe/Mariehamn" + ASIA_BAKU = "Asia/Baku" + EUROPE_SARAJEVO = "Europe/Sarajevo" + AMERICA_BARBADOS = "America/Barbados" + ASIA_DHAKA = "Asia/Dhaka" + EUROPE_BRUSSELS = "Europe/Brussels" + AFRICA_OUAGADOUGOU = "Africa/Ouagadougou" + EUROPE_SOFIA = "Europe/Sofia" + ASIA_BAHRAIN = "Asia/Bahrain" + AFRICA_BUJUMBURA = "Africa/Bujumbura" + AFRICA_PORTO_NOVO = "Africa/Porto-Novo" + AMERICA_ST_BARTHELEMY = "America/St_Barthelemy" + ATLANTIC_BERMUDA = "Atlantic/Bermuda" + ASIA_BRUNEI = "Asia/Brunei" + AMERICA_LA_PAZ = "America/La_Paz" + AMERICA_KRALENDIJK = "America/Kralendijk" + AMERICA_NORONHA = "America/Noronha" + AMERICA_BELEM = "America/Belem" + AMERICA_FORTALEZA = "America/Fortaleza" + AMERICA_RECIFE = "America/Recife" + AMERICA_ARAGUAINA = "America/Araguaina" + AMERICA_MACEIO = "America/Maceio" + AMERICA_BAHIA = "America/Bahia" + AMERICA_SAO_PAULO = "America/Sao_Paulo" + AMERICA_CAMPO_GRANDE = "America/Campo_Grande" + AMERICA_CUIABA = "America/Cuiaba" + AMERICA_SANTAREM = "America/Santarem" + AMERICA_PORTO_VELHO = "America/Porto_Velho" + AMERICA_BOA_VISTA = "America/Boa_Vista" + AMERICA_MANAUS = "America/Manaus" + AMERICA_EIRUNEPE = "America/Eirunepe" + AMERICA_RIO_BRANCO = "America/Rio_Branco" + AMERICA_NASSAU = "America/Nassau" + ASIA_THIMPHU = "Asia/Thimphu" + AFRICA_GABORONE = "Africa/Gaborone" + EUROPE_MINSK = "Europe/Minsk" + AMERICA_BELIZE = "America/Belize" + AMERICA_ST_JOHNS = "America/St_Johns" + AMERICA_HALIFAX = "America/Halifax" + AMERICA_GLACE_BAY = "America/Glace_Bay" + AMERICA_MONCTON = "America/Moncton" + AMERICA_GOOSE_BAY = "America/Goose_Bay" + AMERICA_BLANC_SABLON = "America/Blanc-Sablon" + AMERICA_TORONTO = "America/Toronto" + AMERICA_NIPIGON = "America/Nipigon" + AMERICA_THUNDER_BAY = "America/Thunder_Bay" + AMERICA_IQALUIT = "America/Iqaluit" + AMERICA_PANGNIRTUNG = "America/Pangnirtung" + AMERICA_RESOLUTE = "America/Resolute" + AMERICA_ATIKOKAN = "America/Atikokan" + AMERICA_RANKIN_INLET = "America/Rankin_Inlet" + AMERICA_WINNIPEG = "America/Winnipeg" + AMERICA_RAINY_RIVER = "America/Rainy_River" + AMERICA_REGINA = "America/Regina" + AMERICA_SWIFT_CURRENT = "America/Swift_Current" + AMERICA_EDMONTON = "America/Edmonton" + AMERICA_CAMBRIDGE_BAY = "America/Cambridge_Bay" + AMERICA_YELLOWKNIFE = "America/Yellowknife" + AMERICA_INUVIK = "America/Inuvik" + AMERICA_CRESTON = "America/Creston" + AMERICA_DAWSON_CREEK = "America/Dawson_Creek" + AMERICA_VANCOUVER = "America/Vancouver" + AMERICA_WHITEHORSE = "America/Whitehorse" + AMERICA_DAWSON = "America/Dawson" + INDIAN_COCOS = "Indian/Cocos" + AFRICA_KINSHASA = "Africa/Kinshasa" + AFRICA_LUBUMBASHI = "Africa/Lubumbashi" + AFRICA_BANGUI = "Africa/Bangui" + AFRICA_BRAZZAVILLE = "Africa/Brazzaville" + EUROPE_ZURICH = "Europe/Zurich" + AFRICA_ABIDJAN = "Africa/Abidjan" + PACIFIC_RAROTONGA = "Pacific/Rarotonga" + AMERICA_SANTIAGO = "America/Santiago" + PACIFIC_EASTER = "Pacific/Easter" + AFRICA_DOUALA = "Africa/Douala" + ASIA_SHANGHAI = "Asia/Shanghai" + ASIA_HARBIN = "Asia/Harbin" + ASIA_CHONGQING = "Asia/Chongqing" + ASIA_URUMQI = "Asia/Urumqi" + ASIA_KASHGAR = "Asia/Kashgar" + AMERICA_BOGOTA = "America/Bogota" + AMERICA_COSTA_RICA = "America/Costa_Rica" + AMERICA_HAVANA = "America/Havana" + ATLANTIC_CAPE_VERDE = "Atlantic/Cape_Verde" + AMERICA_CURACAO = "America/Curacao" + INDIAN_CHRISTMAS = "Indian/Christmas" + ASIA_NICOSIA = "Asia/Nicosia" + EUROPE_PRAGUE = "Europe/Prague" + EUROPE_BERLIN = "Europe/Berlin" + EUROPE_BUSINGEN = "Europe/Busingen" + AFRICA_DJIBOUTI = "Africa/Djibouti" + EUROPE_COPENHAGEN = "Europe/Copenhagen" + AMERICA_DOMINICA = "America/Dominica" + AMERICA_SANTO_DOMINGO = "America/Santo_Domingo" + AFRICA_ALGIERS = "Africa/Algiers" + AMERICA_GUAYAQUIL = "America/Guayaquil" + PACIFIC_GALAPAGOS = "Pacific/Galapagos" + EUROPE_TALLINN = "Europe/Tallinn" + AFRICA_CAIRO = "Africa/Cairo" + AFRICA_EL_AAIUN = "Africa/El_Aaiun" + AFRICA_ASMARA = "Africa/Asmara" + EUROPE_MADRID = "Europe/Madrid" + AFRICA_CEUTA = "Africa/Ceuta" + ATLANTIC_CANARY = "Atlantic/Canary" + AFRICA_ADDIS_ABABA = "Africa/Addis_Ababa" + EUROPE_HELSINKI = "Europe/Helsinki" + PACIFIC_FIJI = "Pacific/Fiji" + ATLANTIC_STANLEY = "Atlantic/Stanley" + PACIFIC_CHUUK = "Pacific/Chuuk" + PACIFIC_POHNPEI = "Pacific/Pohnpei" + PACIFIC_KOSRAE = "Pacific/Kosrae" + ATLANTIC_FAROE = "Atlantic/Faroe" + EUROPE_PARIS = "Europe/Paris" + AFRICA_LIBREVILLE = "Africa/Libreville" + EUROPE_LONDON = "Europe/London" + AMERICA_GRENADA = "America/Grenada" + ASIA_TBILISI = "Asia/Tbilisi" + AMERICA_CAYENNE = "America/Cayenne" + EUROPE_GUERNSEY = "Europe/Guernsey" + AFRICA_ACCRA = "Africa/Accra" + EUROPE_GIBRALTAR = "Europe/Gibraltar" + AMERICA_GODTHAB = "America/Godthab" + AMERICA_DANMARKSHAVN = "America/Danmarkshavn" + AMERICA_SCORESBYSUND = "America/Scoresbysund" + AMERICA_THULE = "America/Thule" + AFRICA_BANJUL = "Africa/Banjul" + AFRICA_CONAKRY = "Africa/Conakry" + AMERICA_GUADELOUPE = "America/Guadeloupe" + AFRICA_MALABO = "Africa/Malabo" + EUROPE_ATHENS = "Europe/Athens" + ATLANTIC_SOUTH_GEORGIA = "Atlantic/South_Georgia" + AMERICA_GUATEMALA = "America/Guatemala" + PACIFIC_GUAM = "Pacific/Guam" + AFRICA_BISSAU = "Africa/Bissau" + AMERICA_GUYANA = "America/Guyana" + ASIA_HONG_KONG = "Asia/Hong_Kong" + AMERICA_TEGUCIGALPA = "America/Tegucigalpa" + EUROPE_ZAGREB = "Europe/Zagreb" + AMERICA_PORT_AU_PRINCE = "America/Port-au-Prince" + EUROPE_BUDAPEST = "Europe/Budapest" + ASIA_JAKARTA = "Asia/Jakarta" + ASIA_PONTIANAK = "Asia/Pontianak" + ASIA_MAKASSAR = "Asia/Makassar" + ASIA_JAYAPURA = "Asia/Jayapura" + EUROPE_DUBLIN = "Europe/Dublin" + ASIA_JERUSALEM = "Asia/Jerusalem" + EUROPE_ISLE_OF_MAN = "Europe/Isle_of_Man" + ASIA_KOLKATA = "Asia/Kolkata" + INDIAN_CHAGOS = "Indian/Chagos" + ASIA_BAGHDAD = "Asia/Baghdad" + ASIA_TEHRAN = "Asia/Tehran" + ATLANTIC_REYKJAVIK = "Atlantic/Reykjavik" + EUROPE_ROME = "Europe/Rome" + EUROPE_JERSEY = "Europe/Jersey" + AMERICA_JAMAICA = "America/Jamaica" + ASIA_AMMAN = "Asia/Amman" + ASIA_TOKYO = "Asia/Tokyo" + AFRICA_NAIROBI = "Africa/Nairobi" + ASIA_BISHKEK = "Asia/Bishkek" + ASIA_PHNOM_PENH = "Asia/Phnom_Penh" + PACIFIC_TARAWA = "Pacific/Tarawa" + PACIFIC_ENDERBURY = "Pacific/Enderbury" + PACIFIC_KIRITIMATI = "Pacific/Kiritimati" + INDIAN_COMORO = "Indian/Comoro" + AMERICA_ST_KITTS = "America/St_Kitts" + ASIA_PYONGYANG = "Asia/Pyongyang" + ASIA_SEOUL = "Asia/Seoul" + ASIA_KUWAIT = "Asia/Kuwait" + AMERICA_CAYMAN = "America/Cayman" + ASIA_ALMATY = "Asia/Almaty" + ASIA_QYZYLORDA = "Asia/Qyzylorda" + ASIA_AQTOBE = "Asia/Aqtobe" + ASIA_AQTAU = "Asia/Aqtau" + ASIA_ORAL = "Asia/Oral" + ASIA_VIENTIANE = "Asia/Vientiane" + ASIA_BEIRUT = "Asia/Beirut" + AMERICA_ST_LUCIA = "America/St_Lucia" + EUROPE_VADUZ = "Europe/Vaduz" + ASIA_COLOMBO = "Asia/Colombo" + AFRICA_MONROVIA = "Africa/Monrovia" + AFRICA_MASERU = "Africa/Maseru" + EUROPE_VILNIUS = "Europe/Vilnius" + EUROPE_LUXEMBOURG = "Europe/Luxembourg" + EUROPE_RIGA = "Europe/Riga" + AFRICA_TRIPOLI = "Africa/Tripoli" + AFRICA_CASABLANCA = "Africa/Casablanca" + EUROPE_MONACO = "Europe/Monaco" + EUROPE_CHISINAU = "Europe/Chisinau" + EUROPE_PODGORICA = "Europe/Podgorica" + AMERICA_MARIGOT = "America/Marigot" + INDIAN_ANTANANARIVO = "Indian/Antananarivo" + PACIFIC_MAJURO = "Pacific/Majuro" + PACIFIC_KWAJALEIN = "Pacific/Kwajalein" + EUROPE_SKOPJE = "Europe/Skopje" + AFRICA_BAMAKO = "Africa/Bamako" + ASIA_RANGOON = "Asia/Rangoon" + ASIA_ULAANBAATAR = "Asia/Ulaanbaatar" + ASIA_HOVD = "Asia/Hovd" + ASIA_CHOIBALSAN = "Asia/Choibalsan" + ASIA_MACAU = "Asia/Macau" + PACIFIC_SAIPAN = "Pacific/Saipan" + AMERICA_MARTINIQUE = "America/Martinique" + AFRICA_NOUAKCHOTT = "Africa/Nouakchott" + AMERICA_MONTSERRAT = "America/Montserrat" + EUROPE_MALTA = "Europe/Malta" + INDIAN_MAURITIUS = "Indian/Mauritius" + INDIAN_MALDIVES = "Indian/Maldives" + AFRICA_BLANTYRE = "Africa/Blantyre" + AMERICA_MEXICO_CITY = "America/Mexico_City" + AMERICA_CANCUN = "America/Cancun" + AMERICA_MERIDA = "America/Merida" + AMERICA_MONTERREY = "America/Monterrey" + AMERICA_MATAMOROS = "America/Matamoros" + AMERICA_MAZATLAN = "America/Mazatlan" + AMERICA_CHIHUAHUA = "America/Chihuahua" + AMERICA_OJINAGA = "America/Ojinaga" + AMERICA_HERMOSILLO = "America/Hermosillo" + AMERICA_TIJUANA = "America/Tijuana" + AMERICA_SANTA_ISABEL = "America/Santa_Isabel" + AMERICA_BAHIA_BANDERAS = "America/Bahia_Banderas" + ASIA_KUALA_LUMPUR = "Asia/Kuala_Lumpur" + ASIA_KUCHING = "Asia/Kuching" + AFRICA_MAPUTO = "Africa/Maputo" + AFRICA_WINDHOEK = "Africa/Windhoek" + PACIFIC_NOUMEA = "Pacific/Noumea" + AFRICA_NIAMEY = "Africa/Niamey" + PACIFIC_NORFOLK = "Pacific/Norfolk" + AFRICA_LAGOS = "Africa/Lagos" + AMERICA_MANAGUA = "America/Managua" + EUROPE_AMSTERDAM = "Europe/Amsterdam" + EUROPE_OSLO = "Europe/Oslo" + ASIA_KATHMANDU = "Asia/Kathmandu" + PACIFIC_NAURU = "Pacific/Nauru" + PACIFIC_NIUE = "Pacific/Niue" + PACIFIC_AUCKLAND = "Pacific/Auckland" + PACIFIC_CHATHAM = "Pacific/Chatham" + ASIA_MUSCAT = "Asia/Muscat" + AMERICA_PANAMA = "America/Panama" + AMERICA_LIMA = "America/Lima" + PACIFIC_TAHITI = "Pacific/Tahiti" + PACIFIC_MARQUESAS = "Pacific/Marquesas" + PACIFIC_GAMBIER = "Pacific/Gambier" + PACIFIC_PORT_MORESBY = "Pacific/Port_Moresby" + ASIA_MANILA = "Asia/Manila" + ASIA_KARACHI = "Asia/Karachi" + EUROPE_WARSAW = "Europe/Warsaw" + AMERICA_MIQUELON = "America/Miquelon" + PACIFIC_PITCAIRN = "Pacific/Pitcairn" + AMERICA_PUERTO_RICO = "America/Puerto_Rico" + ASIA_GAZA = "Asia/Gaza" + ASIA_HEBRON = "Asia/Hebron" + EUROPE_LISBON = "Europe/Lisbon" + ATLANTIC_MADEIRA = "Atlantic/Madeira" + ATLANTIC_AZORES = "Atlantic/Azores" + PACIFIC_PALAU = "Pacific/Palau" + AMERICA_ASUNCION = "America/Asuncion" + ASIA_QATAR = "Asia/Qatar" + INDIAN_REUNION = "Indian/Reunion" + EUROPE_BUCHAREST = "Europe/Bucharest" + EUROPE_BELGRADE = "Europe/Belgrade" + EUROPE_KALININGRAD = "Europe/Kaliningrad" + EUROPE_MOSCOW = "Europe/Moscow" + EUROPE_VOLGOGRAD = "Europe/Volgograd" + EUROPE_SAMARA = "Europe/Samara" + ASIA_YEKATERINBURG = "Asia/Yekaterinburg" + ASIA_OMSK = "Asia/Omsk" + ASIA_NOVOSIBIRSK = "Asia/Novosibirsk" + ASIA_NOVOKUZNETSK = "Asia/Novokuznetsk" + ASIA_KRASNOYARSK = "Asia/Krasnoyarsk" + ASIA_IRKUTSK = "Asia/Irkutsk" + ASIA_YAKUTSK = "Asia/Yakutsk" + ASIA_KHANDYGA = "Asia/Khandyga" + ASIA_VLADIVOSTOK = "Asia/Vladivostok" + ASIA_SAKHALIN = "Asia/Sakhalin" + ASIA_UST_NERA = "Asia/Ust-Nera" + ASIA_MAGADAN = "Asia/Magadan" + ASIA_KAMCHATKA = "Asia/Kamchatka" + ASIA_ANADYR = "Asia/Anadyr" + AFRICA_KIGALI = "Africa/Kigali" + ASIA_RIYADH = "Asia/Riyadh" + PACIFIC_GUADALCANAL = "Pacific/Guadalcanal" + INDIAN_MAHE = "Indian/Mahe" + AFRICA_KHARTOUM = "Africa/Khartoum" + EUROPE_STOCKHOLM = "Europe/Stockholm" + ASIA_SINGAPORE = "Asia/Singapore" + ATLANTIC_ST_HELENA = "Atlantic/St_Helena" + EUROPE_LJUBLJANA = "Europe/Ljubljana" + ARCTIC_LONGYEARBYEN = "Arctic/Longyearbyen" + EUROPE_BRATISLAVA = "Europe/Bratislava" + AFRICA_FREETOWN = "Africa/Freetown" + EUROPE_SAN_MARINO = "Europe/San_Marino" + AFRICA_DAKAR = "Africa/Dakar" + AFRICA_MOGADISHU = "Africa/Mogadishu" + AMERICA_PARAMARIBO = "America/Paramaribo" + AFRICA_JUBA = "Africa/Juba" + AFRICA_SAO_TOME = "Africa/Sao_Tome" + AMERICA_EL_SALVADOR = "America/El_Salvador" + AMERICA_LOWER_PRINCES = "America/Lower_Princes" + ASIA_DAMASCUS = "Asia/Damascus" + AFRICA_MBABANE = "Africa/Mbabane" + AMERICA_GRAND_TURK = "America/Grand_Turk" + AFRICA_NDJAMENA = "Africa/Ndjamena" + INDIAN_KERGUELEN = "Indian/Kerguelen" + AFRICA_LOME = "Africa/Lome" + ASIA_BANGKOK = "Asia/Bangkok" + ASIA_DUSHANBE = "Asia/Dushanbe" + PACIFIC_FAKAOFO = "Pacific/Fakaofo" + ASIA_DILI = "Asia/Dili" + ASIA_ASHGABAT = "Asia/Ashgabat" + AFRICA_TUNIS = "Africa/Tunis" + PACIFIC_TONGATAPU = "Pacific/Tongatapu" + EUROPE_ISTANBUL = "Europe/Istanbul" + AMERICA_PORT_OF_SPAIN = "America/Port_of_Spain" + PACIFIC_FUNAFUTI = "Pacific/Funafuti" + ASIA_TAIPEI = "Asia/Taipei" + AFRICA_DAR_ES_SALAAM = "Africa/Dar_es_Salaam" + EUROPE_KIEV = "Europe/Kiev" + EUROPE_UZHGOROD = "Europe/Uzhgorod" + EUROPE_ZAPOROZHYE = "Europe/Zaporozhye" + EUROPE_SIMFEROPOL = "Europe/Simferopol" + AFRICA_KAMPALA = "Africa/Kampala" + PACIFIC_JOHNSTON = "Pacific/Johnston" + PACIFIC_MIDWAY = "Pacific/Midway" + PACIFIC_WAKE = "Pacific/Wake" + AMERICA_NEW_YORK = "America/New_York" + AMERICA_DETROIT = "America/Detroit" + AMERICA_KENTUCKY_LOUISVILLE = "America/Kentucky/Louisville" + AMERICA_KENTUCKY_MONTICELLO = "America/Kentucky/Monticello" + AMERICA_INDIANA_INDIANAPOLIS = "America/Indiana/Indianapolis" + AMERICA_INDIANA_VINCENNES = "America/Indiana/Vincennes" + AMERICA_INDIANA_WINAMAC = "America/Indiana/Winamac" + AMERICA_INDIANA_MARENGO = "America/Indiana/Marengo" + AMERICA_INDIANA_PETERSBURG = "America/Indiana/Petersburg" + AMERICA_INDIANA_VEVAY = "America/Indiana/Vevay" + AMERICA_CHICAGO = "America/Chicago" + AMERICA_INDIANA_TELL_CITY = "America/Indiana/Tell_City" + AMERICA_INDIANA_KNOX = "America/Indiana/Knox" + AMERICA_MENOMINEE = "America/Menominee" + AMERICA_NORTH_DAKOTA_CENTER = "America/North_Dakota/Center" + AMERICA_NORTH_DAKOTA_NEW_SALEM = "America/North_Dakota/New_Salem" + AMERICA_NORTH_DAKOTA_BEULAH = "America/North_Dakota/Beulah" + AMERICA_DENVER = "America/Denver" + AMERICA_BOISE = "America/Boise" + AMERICA_PHOENIX = "America/Phoenix" + AMERICA_LOS_ANGELES = "America/Los_Angeles" + AMERICA_ANCHORAGE = "America/Anchorage" + AMERICA_JUNEAU = "America/Juneau" + AMERICA_SITKA = "America/Sitka" + AMERICA_YAKUTAT = "America/Yakutat" + AMERICA_NOME = "America/Nome" + AMERICA_ADAK = "America/Adak" + AMERICA_METLAKATLA = "America/Metlakatla" + PACIFIC_HONOLULU = "Pacific/Honolulu" + AMERICA_MONTEVIDEO = "America/Montevideo" + ASIA_SAMARKAND = "Asia/Samarkand" + ASIA_TASHKENT = "Asia/Tashkent" + EUROPE_VATICAN = "Europe/Vatican" + AMERICA_ST_VINCENT = "America/St_Vincent" + AMERICA_CARACAS = "America/Caracas" + AMERICA_TORTOLA = "America/Tortola" + AMERICA_ST_THOMAS = "America/St_Thomas" + ASIA_HO_CHI_MINH = "Asia/Ho_Chi_Minh" + PACIFIC_EFATE = "Pacific/Efate" + PACIFIC_WALLIS = "Pacific/Wallis" + PACIFIC_APIA = "Pacific/Apia" + ASIA_ADEN = "Asia/Aden" + INDIAN_MAYOTTE = "Indian/Mayotte" + AFRICA_JOHANNESBURG = "Africa/Johannesburg" + AFRICA_LUSAKA = "Africa/Lusaka" + AFRICA_HARARE = "Africa/Harare" + UTC = "UTC" diff --git a/catalystwan/workflows/config_migration.py b/catalystwan/workflows/config_migration.py deleted file mode 100644 index cbb485aa7..000000000 --- a/catalystwan/workflows/config_migration.py +++ /dev/null @@ -1,126 +0,0 @@ -import logging -from typing import Callable - -from catalystwan.api.policy_api import POLICY_LIST_ENDPOINTS_MAP -from catalystwan.endpoints.configuration_group import ConfigGroup -from catalystwan.models.configuration.config_migration import UX1Config, UX2Config -from catalystwan.session import ManagerSession -from catalystwan.utils.config_migration.converters.feature_template import create_parcel_from_template -from catalystwan.utils.config_migration.creators.config_group import ConfigGroupCreator - -logger = logging.getLogger(__name__) - -SUPPORTED_TEMPLATE_TYPES = [ - "cisco_aaa", - "cedge_aaa", - "aaa", - "cisco_banner", - "cisco_security", - "security", - "security-vsmart", - "security-vedge", - "cisco_system", - "system-vsmart", - "system-vedge", - "cisco_bfd", - "bfd-vedge", - "cedge_global", - "cisco_logging", - "logging", - "cisco_omp", - "omp-vedge", - "omp-vsmart", - "cisco_ntp", - "ntp", -] - - -def log_progress(task: str, completed: int, total: int) -> None: - logger.info(f"{task} {completed}/{total}") - - -def transform(ux1: UX1Config) -> UX2Config: - ux2 = UX2Config() - # Feature Templates - for ft in ux1.templates.feature_templates: - if ft.template_type in SUPPORTED_TEMPLATE_TYPES: - ux2.profile_parcels.append(create_parcel_from_template(ft)) - # Policy Lists - for policy_list in ux1.policies.policy_lists: - if (parcel := policy_list.to_policy_object_parcel()) is not None: - ux2.profile_parcels.append(parcel) - return ux2 - - -def collect_ux1_config(session: ManagerSession, progress: Callable[[str, int, int], None] = log_progress) -> UX1Config: - ux1 = UX1Config() - - """Collect Policies""" - policy_api = session.api.policy - progress("Collecting Policy Info", 0, 3) - - centralized_policy_ids = [info.policy_id for info in policy_api.centralized.get()] - progress("Collecting Policy Info", 1, 3) - - localized_policy_ids = [info.policy_id for info in policy_api.localized.get()] - progress("Collecting Policy Info", 2, 3) - - policy_definition_types_and_ids = [ - (policy_type, info.definition_id) for policy_type, info in policy_api.definitions.get_all() - ] - progress("Collecting Policy Info", 3, 3) - - policy_list_types = POLICY_LIST_ENDPOINTS_MAP.keys() - for i, policy_list_type in enumerate(policy_list_types): - ux1.policies.policy_lists.extend(policy_api.lists.get(policy_list_type)) - progress("Collecting Policy Lists", i + 1, len(policy_list_types)) - - for i, type_and_id in enumerate(policy_definition_types_and_ids): - ux1.policies.policy_definitions.append(policy_api.definitions.get(*type_and_id)) - progress("Collecting Policy Definitions", i + 1, len(policy_definition_types_and_ids)) - - for i, cpid in enumerate(centralized_policy_ids): - ux1.policies.centralized_policies.append(policy_api.centralized.get(id=cpid)) - progress("Collecting Centralized Policies", i + 1, len(centralized_policy_ids)) - - for i, lpid in enumerate(localized_policy_ids): - ux1.policies.localized_policies.append(policy_api.localized.get(id=lpid)) - progress("Collecting Localized Policies", i + 1, len(localized_policy_ids)) - - """Collect Templates""" - template_api = session.api.templates - progress("Collecting Templates Info", 0, 2) - - ux1.templates.feature_templates = [t for t in template_api.get_feature_templates()] - progress("Collecting Templates Info", 1, 2) - - ux1.templates.device_templates = [t for t in template_api.get_device_templates()] - progress("Collecting Templates Info", 2, 2) - - return ux1 - - -def push_ux2_config(session: ManagerSession, config: UX2Config) -> ConfigGroup: - """ - Creates configuration group and pushes a UX2 configuration to the Cisco vManage. - - Args: - session (ManagerSession): A valid Manager API session. - config (UX2Config): The UX2 configuration to push. - - Returns: - UX2ConfigPushResult - - Raises: - ManagerHTTPError: If the configuration cannot be pushed. - """ - - config_group_creator = ConfigGroupCreator(session, config, logger) - config_group = config_group_creator.create() - feature_profiles = config_group.profiles # noqa: F841 - for parcels in config.profile_parcels: - # TODO: Create API that supports parcel creation on feature profiles - # Example: session.api.parcels.create(parcels=parcels, feature_profiles=feature_profiles) - pass - - return config_group diff --git a/endpoints-md.py b/endpoints-md.py index 9625f2806..409c63d82 100644 --- a/endpoints-md.py +++ b/endpoints-md.py @@ -201,7 +201,7 @@ def md(self) -> str: # this instantiates APIEndpoints classes triggering method decorators # endpoints not attached to container will be not documented ! - APIEndpointContainter(MagicMock()) + _ = APIEndpointContainter(MagicMock()) endpoint_registry = EndpointRegistry( meta_lookup=request.request_lookup, diff --git a/examples/policies_configuration_guide.py b/examples/policies_configuration_guide.py index 1024215ee..339196517 100644 --- a/examples/policies_configuration_guide.py +++ b/examples/policies_configuration_guide.py @@ -1,3 +1,5 @@ +# Copyright 2024 Cisco Systems, Inc. and its affiliates + """ This example demonstrates usage of PolicyAPI in catalystwan Code below provides same results as obtained after executing workflow manually via WEB-UI according to: @@ -20,7 +22,7 @@ import logging import sys from dataclasses import dataclass -from ipaddress import IPv4Address, IPv4Network, IPv6Interface +from ipaddress import IPv4Address, IPv4Network, IPv6Network from typing import List, Optional, Sequence from uuid import UUID @@ -46,12 +48,12 @@ PrefixList, RegionList, SiteList, + SLAClassList, TLOCList, TrafficDataPolicy, VPNList, VPNMembershipPolicy, ) -from catalystwan.models.policy.lists import SLAClassList logger = logging.getLogger(__name__) @@ -127,8 +129,8 @@ def configure_groups_of_interest(api: PolicyAPI) -> List[ConfigItem]: configured_items.append(ConfigItem(DataPrefixList, data_prefix_list.name, data_prefix_list_id)) data_ipv6_prefix_list = DataIPv6PrefixList(name="MyDataIPv6Prefixes") - data_ipv6_prefix_list.add_prefix(IPv6Interface("2001:db8::1000/124")) - data_ipv6_prefix_list.add_prefix(IPv6Interface("2001:db9::1000/124")) + data_ipv6_prefix_list.add_prefix(IPv6Network("2001:db8::1000/124")) + data_ipv6_prefix_list.add_prefix(IPv6Network("2001:db9::1000/124")) data_ipv6_prefix_list_id = api.lists.create(data_ipv6_prefix_list) configured_items.append(ConfigItem(DataIPv6PrefixList, data_ipv6_prefix_list.name, data_ipv6_prefix_list_id)) @@ -184,7 +186,7 @@ def configure_groups_of_interest(api: PolicyAPI) -> List[ConfigItem]: configured_items.append(ConfigItem(ClassMapList, class_map.name, class_map_id)) app_probe_class = AppProbeClassList(name="MyAppProbeClass") - app_probe_class.assign_forwarding_class("MyClassMap").add_color_mapping("green", 5) + app_probe_class.assign_forwarding_class("MyClassMap").add_color_mapping("3g", 5) app_probe_class_id = api.lists.create(app_probe_class) configured_items.append(ConfigItem(AppProbeClassList, app_probe_class.name, app_probe_class_id)) @@ -365,7 +367,7 @@ def create_traffic_data_policy(api: PolicyAPI, items: List[ConfigItem]) -> Confi seq_2.match_dns_response() seq_2.match_low_plp() seq_2.match_secondary_destination_region() - seq_2.match_source_data_prefix_list([find_id(items, "MyDataPrefixes")]) + seq_2.match_source_data_prefix_list(find_id(items, "MyDataPrefixes")) seq_2.match_traffic_to_core() seq_2.match_destination_data_prefix_list(find_id(items, "MyDataPrefixes")) seq_2.associate_loss_correction_packet_duplication_action() diff --git a/pyproject.toml b/pyproject.toml index dd38dee36..b5b39e7fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "catalystwan" -version = "0.31.0dev4" +version = "0.31.0" description = "Cisco Catalyst WAN SDK for Python" authors = ["kagorski "] readme = "README.md"