Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
Fix feature profile api, add transport profile builder, add descripti…
Browse files Browse the repository at this point in the history
…on to t1e1 model, add TransportAndManagementParcelPusher
  • Loading branch information
jpkrajewski committed May 7, 2024
1 parent c4d06ec commit 42b282b
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 27 deletions.
9 changes: 8 additions & 1 deletion catalystwan/api/builders/feature_profiles/builder_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@
from catalystwan.api.builders.feature_profiles.other import OtherFeatureProfileBuilder
from catalystwan.api.builders.feature_profiles.service import ServiceFeatureProfileBuilder
from catalystwan.api.builders.feature_profiles.system import SystemFeatureProfileBuilder
from catalystwan.api.builders.feature_profiles.transport import TransportAndManagementProfileBuilder
from catalystwan.exceptions import CatalystwanException
from catalystwan.models.configuration.feature_profile.common import ProfileType

if TYPE_CHECKING:
from catalystwan.session import ManagerSession

FeatureProfileBuilder = Union[ServiceFeatureProfileBuilder, SystemFeatureProfileBuilder, OtherFeatureProfileBuilder]
FeatureProfileBuilder = Union[
ServiceFeatureProfileBuilder,
SystemFeatureProfileBuilder,
OtherFeatureProfileBuilder,
TransportAndManagementProfileBuilder,
]

BUILDER_MAPPING: Mapping[ProfileType, Callable] = {
"service": ServiceFeatureProfileBuilder,
"system": SystemFeatureProfileBuilder,
"other": OtherFeatureProfileBuilder,
"transport": TransportAndManagementProfileBuilder,
}


Expand Down
117 changes: 115 additions & 2 deletions catalystwan/api/builders/feature_profiles/transport.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,116 @@
# Copyright 2023 Cisco Systems, Inc. and its affiliates
from __future__ import annotations

import logging
from collections import defaultdict
from typing import TYPE_CHECKING, Dict, List
from uuid import UUID, uuid4

from catalystwan.api.feature_profile_api import TransportFeatureProfileAPI
from catalystwan.endpoints.configuration.feature_profile.sdwan.transport import TransportFeatureProfile
from catalystwan.exceptions import ManagerHTTPError
from catalystwan.models.configuration.feature_profile.common import FeatureProfileCreationPayload
from catalystwan.models.configuration.feature_profile.sdwan.transport import (
AnyTransportSuperParcel,
AnyTransportVpnParcel,
AnyTransportVpnSubParcel,
)

logger = logging.getLogger(__name__)

if TYPE_CHECKING:
from catalystwan.session import ManagerSession


class TransportAndManagementProfileBuilder:
# TODO: Implement after all parcels for Transport and Management are ready
pass
"""
A class for building system feature profiles.
"""

def __init__(self, session: ManagerSession) -> None:
"""
Initialize a new instance of the Service class.
Args:
session (ManagerSession): The ManagerSession object used for API communication.
profile_uuid (UUID): The UUID of the profile.
"""
self._profile: FeatureProfileCreationPayload
self._api = TransportFeatureProfileAPI(session)
self._endpoints = TransportFeatureProfile(session)
self._independent_items: List[AnyTransportSuperParcel] = []
self._independent_items_vpns: Dict[UUID, AnyTransportVpnParcel] = {}
self._dependent_items_on_vpns: Dict[UUID, List[AnyTransportVpnSubParcel]] = defaultdict(list)

def add_profile_name_and_description(self, feature_profile: FeatureProfileCreationPayload) -> None:
"""
Adds a name and description to the feature profile.
Args:
name (str): The name of the feature profile.
description (str): The description of the feature profile.
Returns:
None
"""
self._profile = feature_profile

def add_parcel(self, parcel: AnyTransportSuperParcel) -> None:
"""
Adds a parcel to the feature profile.
Args:
parcel (AnyTransportSuperParcel): The parcel to add.
Returns:
None
"""
self._independent_items.append(parcel)

def add_parcel_vpn(self, parcel: AnyTransportVpnParcel) -> UUID:
"""
Adds a VPN parcel to the builder.
Args:
parcel (LanVpnParcel): The VPN parcel to add.
Returns:
UUID: The UUID tag of the added VPN parcel.
"""
vpn_tag = uuid4()
logger.debug(f"Adding VPN parcel {parcel.parcel_name} with tag {vpn_tag}")
self._independent_items_vpns[vpn_tag] = parcel
return vpn_tag

def add_vpn_subparcel(self, parcel: AnyTransportVpnSubParcel, vpn_tag: UUID) -> None:
"""
Adds a parcel to the feature profile.
Args:
parcel (AnyTransportVpnSubParcel): The parcel to add.
Returns:
None
"""
self._dependent_items_on_vpns[vpn_tag].append(parcel)

def build(self) -> UUID:
"""
Builds the feature profile.
Returns:
UUID: The UUID of the created feature profile.
"""

profile_uuid = self._endpoints.create_transport_feature_profile(self._profile).id
try:
for parcel in self._independent_items:
self._api.create_parcel(profile_uuid, parcel)
for vpn_tag, vpn_parcel in self._independent_items_vpns.items():
# TODO: Add subparcels to VPN
vpn_uuid = self._api.create_parcel(profile_uuid, vpn_parcel).id
for subparcel in self._dependent_items_on_vpns[vpn_tag]:
self._api.create_parcel(profile_uuid, subparcel, vpn_uuid)
except ManagerHTTPError as e:
logger.error(f"Error occured during building profile: {e.info}")
return profile_uuid
6 changes: 3 additions & 3 deletions catalystwan/api/feature_profile_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,14 @@ def create_parcel(
Create Transport Parcel for selected profile_id based on payload type
"""
if vpn_uuid is not None:
vpn_parcel = self._get_vpn_parcel(profile_id, vpn_uuid)
if vpn_parcel.payload._get_parcel_type() == TransportVpnParcel._get_parcel_type():
vpn_parcel = self._get_vpn_parcel(profile_id, vpn_uuid).payload
if vpn_parcel._get_parcel_type() == TransportVpnParcel._get_parcel_type():
return self.endpoint.create_transport_vpn_sub_parcel(
profile_id, vpn_uuid, payload._get_parcel_type(), payload
)
else:
return self.endpoint.create_management_vpn_sub_parcel(
profile_id, vpn_uuid, payload._get_parcel_type(), vpn_parcel.parcel_id, payload
profile_id, vpn_uuid, payload._get_parcel_type(), payload
)
return self.endpoint.create_transport_parcel(profile_id, payload._get_parcel_type(), payload)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from catalystwan.models.configuration.feature_profile.parcel import Parcel, ParcelCreationResponse, ParcelId
from catalystwan.models.configuration.feature_profile.sdwan.transport import (
AnyTransportParcel,
AnyTransportVpnSubParcel,
CellularControllerParcel,
)
from catalystwan.models.configuration.feature_profile.sdwan.transport.vpn import ManagementVpnParcel
Expand Down Expand Up @@ -60,14 +59,14 @@ def delete_transport_feature_profile(self, profile_id: UUID) -> None:
@versions(supported_versions=(">=20.13"), raises=False)
@post("/v1/feature-profile/sdwan/transport/{profile_id}/{parcel_type}")
def create_transport_parcel(
self, profile_id: UUID, parcel_type: str, payload: AnyTransportParcel
self, profile_id: UUID, parcel_type: str, payload: _ParcelBase
) -> ParcelCreationResponse:
...

@versions(supported_versions=(">=20.13"), raises=False)
@post("/v1/feature-profile/sdwan/transport/{profile_id}/wan/vpn/{vpn_id}/{parcel_type}")
def create_transport_vpn_sub_parcel(
self, profile_id: UUID, vpn_id: UUID, parcel_type: str, payload: AnyTransportVpnSubParcel
self, profile_id: UUID, vpn_id: UUID, parcel_type: str, payload: _ParcelBase
) -> ParcelCreationResponse:
...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@
from .cellular_controller import CellularControllerParcel
from .t1e1controller import T1E1ControllerParcel
from .vpn import ManagementVpnParcel, TransportVpnParcel
from .wan.interface.t1e1serial import T1E1SerialParcel

AnyTransportVpnSubParcel = Annotated[Union[T1E1ControllerParcel, BGPParcel], Field(discriminator="type_")]

AnyTransportVpnSubParcel = Annotated[
Union[
T1E1SerialParcel
# Add wan interfaces here
],
Field(discriminator="type_"),
]
AnyTransportVpnParcel = Annotated[Union[ManagementVpnParcel, TransportVpnParcel], Field(discriminator="type_")]
AnyTransportSuperParcel = Annotated[
Union[T1E1ControllerParcel, CellularControllerParcel, BGPParcel, T1E1ControllerParcel], Field(discriminator="type_")
]
AnyTransportParcel = Annotated[
Union[CellularControllerParcel, ManagementVpnParcel, TransportVpnParcel, AnyTransportVpnSubParcel],
Union[AnyTransportSuperParcel, AnyTransportVpnParcel, AnyTransportVpnSubParcel],
Field(discriminator="type_"),
]

Expand All @@ -23,8 +33,9 @@
"ManagementVpnParcel",
"TransportVpnParcel",
"AnyTransportParcel",
"AnyTransportVpnSubParcel",
"T1E1ControllerParcel",
"AnyTransportSuperParcel",
"AnyTransportVpnSubParcel" "T1E1ControllerParcel",
"T1E1SerialParcel",
]


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,14 @@ class Advanced(BaseModel):
populate_by_name=True,
)
ip_mtu: Optional[Union[Global[int], Variable, Default[int]]] = Field(
default=None, validation_alias="ipMtu", serialization_alias="ipMtu"
default=None, validation_alias="ipMtu", serialization_alias="ipMtu", description="Value cannot be less than 576"
)
mtu: Optional[Union[Global[int], Variable, Default[int]]] = Field(default=None)
tcp_mss_adjust: Optional[Union[Global[int], Variable, Default[None]]] = Field(
default=None, validation_alias="tcpMssAdjust", serialization_alias="tcpMssAdjust"
default=None,
validation_alias="tcpMssAdjust",
serialization_alias="tcpMssAdjust",
description="Value must cannot be greater then 1460",
)
tloc_extension: Optional[Union[Global[str], Variable, Default[None]]] = Field(
default=None, validation_alias="tlocExtension", serialization_alias="tlocExtension"
Expand Down
9 changes: 8 additions & 1 deletion catalystwan/models/policy/definition/intrusion_prevention.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import List, Literal, Optional

from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, field_validator

from catalystwan.models.common import PolicyModeType, VpnId
from catalystwan.models.policy.policy_definition import (
Expand Down Expand Up @@ -31,6 +31,13 @@ class IntrusionPreventionDefinition(BaseModel):
default=False, validation_alias="customSignature", serialization_alias="customSignature"
)

@field_validator("signature_white_list", mode="before")
@classmethod
def convert_empty_dict_to_none(cls, value):
if not value:
return None
return value


class IntrusionPreventionPolicy(PolicyDefinitionBase):
type: Literal["intrusionPrevention"] = "intrusionPrevention"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _create_feature_profile_and_parcels(self, feature_profiles_ids: List[UUID])
f"and parcels: {transformed_feature_profile.header.subelements}"
)
profile_type = cast(ProfileType, transformed_feature_profile.header.type)
if profile_type in ["policy-object", "transport"]:
if profile_type in ["policy-object"]:
# TODO: Add builders for those profiles
logger.debug(f"Skipping profile: {transformed_feature_profile.feature_profile.name}")
continue
Expand Down
40 changes: 37 additions & 3 deletions catalystwan/utils/config_migration/creators/strategy/parcels.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import logging
from collections import defaultdict
from typing import Dict, List, cast
from uuid import UUID

from catalystwan.api.builders.feature_profiles.transport import TransportAndManagementProfileBuilder
from catalystwan.models.configuration.config_migration import TransformedParcel
from catalystwan.models.configuration.feature_profile.common import FeatureProfileCreationPayload, ProfileType
from catalystwan.models.configuration.feature_profile.sdwan.service import AnyAssociatoryParcel
from catalystwan.models.configuration.feature_profile.sdwan.service.lan.vpn import LanVpnParcel
from catalystwan.models.configuration.feature_profile.sdwan.transport.vpn import ManagementVpnParcel, TransportVpnParcel
from catalystwan.session import ManagerSession

logger = logging.getLogger(__name__)


class ParcelPusher:
"""
Expand Down Expand Up @@ -65,9 +70,11 @@ def push(
self.builder.add_parcel(parcel) # type: ignore
else:
vpn_tag = self.builder.add_parcel_vpn(parcel) # type: ignore
for transformed_subparcel in [
all_parcels.get(element) for element in transformed_parcel.header.subelements
]:
for element in transformed_parcel.header.subelements:
transformed_subparcel = all_parcels.get(element)
if transformed_subparcel is None:
logger.error(f"Subparcel {element} not found in profile parcels. Skipping.")
continue
parcel = self._resolve_parcel_naming(transformed_subparcel) # type: ignore
self.builder.add_parcel_vpn_subparcel(vpn_tag, parcel) # type: ignore
self.builder.add_profile_name_and_description(feature_profile)
Expand All @@ -86,3 +93,30 @@ def _resolve_parcel_naming(self, transformed_subparcel: TransformedParcel) -> An
parcel = parcel.model_copy(deep=True)
parcel.parcel_name += f"_{count_value}"
return parcel


class TransportAndManagementParcelPusher(ParcelPusher):
"""
Parcel pusher for transport and management feature profiles.
"""

builder: TransportAndManagementProfileBuilder

def push(
self,
feature_profile: FeatureProfileCreationPayload,
target_parcels: List[TransformedParcel],
all_parcels: Dict[UUID, TransformedParcel],
) -> UUID:
for transformed_parcel in target_parcels:
parcel = transformed_parcel.parcel
if isinstance(parcel, (ManagementVpnParcel, TransportVpnParcel)):
vpn_tag = self.builder.add_parcel_vpn(parcel) # type: ignore
for element in transformed_parcel.header.subelements:
transformed_subparcel = all_parcels.get(element)
if transformed_subparcel is None:
logger.error(f"Subparcel {element} not found in profile parcels. Skipping.")
continue
self.builder.add_vpn_subparcel(transformed_subparcel.parcel, vpn_tag) # type: ignore
self.builder.add_profile_name_and_description(feature_profile)
return self.builder.build()
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
PolicyObjectFeatureProfileAPI,
ServiceFeatureProfileAPI,
SystemFeatureProfileAPI,
TransportFeatureProfileAPI,
)
from catalystwan.models.configuration.feature_profile.common import ProfileType
from catalystwan.session import ManagerSession
Expand All @@ -14,10 +15,15 @@
"other": OtherFeatureProfileAPI,
"policy-object": PolicyObjectFeatureProfileAPI,
"service": ServiceFeatureProfileAPI,
"transport": TransportFeatureProfileAPI,
}

FeatureProfile = Union[
SystemFeatureProfileAPI, OtherFeatureProfileAPI, PolicyObjectFeatureProfileAPI, ServiceFeatureProfileAPI
SystemFeatureProfileAPI,
OtherFeatureProfileAPI,
PolicyObjectFeatureProfileAPI,
ServiceFeatureProfileAPI,
TransportFeatureProfileAPI,
]


Expand Down
2 changes: 2 additions & 0 deletions catalystwan/utils/config_migration/factories/parcel_pusher.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ParcelPusher,
ServiceParcelPusher,
SimpleParcelPusher,
TransportAndManagementParcelPusher,
)

logger = logging.getLogger(__name__)
Expand All @@ -16,6 +17,7 @@
"other": SimpleParcelPusher,
"system": SimpleParcelPusher,
"service": ServiceParcelPusher,
"transport": TransportAndManagementParcelPusher,
}


Expand Down
Loading

0 comments on commit 42b282b

Please sign in to comment.