diff --git a/catalystwan/models/configuration/config_migration.py b/catalystwan/models/configuration/config_migration.py index 39d63ef15..acd7b58ba 100644 --- a/catalystwan/models/configuration/config_migration.py +++ b/catalystwan/models/configuration/config_migration.py @@ -4,6 +4,8 @@ from typing_extensions import Annotated 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.policy import ( @@ -43,8 +45,12 @@ class UX1Policies(BaseModel): class UX1Templates(BaseModel): - features: List[FeatureTemplateInformation] = Field(default=[]) - devices: List[DeviceTemplateInformation] = Field(default=[]) + 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): @@ -63,9 +69,18 @@ class UX1Config(BaseModel): class UX2Config(BaseModel): + # All UX2 Configuration items - Mega Model # All UX2 Configuration items - Mega Model model_config = ConfigDict(populate_by_name=True) - # TODO: config group name - config_group_presets: List[ConfigGroupPreset] = Field( - default=[], serialization_alias="configGroupPresets", validation_alias="configGroupPresets" + 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" ) diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py b/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py index d0a726863..9344a845b 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/system/logging_parcel.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Union +from typing import List, Literal, Optional, Union from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -76,6 +76,7 @@ class Disk(BaseModel): class LoggingParcel(_ParcelBase): + type_: Literal["logging"] = Field(default="logging", exclude=True) disk: Optional[Disk] = Field(default=None, 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")) diff --git a/catalystwan/models/policy/centralized.py b/catalystwan/models/policy/centralized.py index 9f28f0a7e..39992748a 100644 --- a/catalystwan/models/policy/centralized.py +++ b/catalystwan/models/policy/centralized.py @@ -159,12 +159,27 @@ 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"), ] diff --git a/catalystwan/models/policy/definitions/zone_based_firewall.py b/catalystwan/models/policy/definitions/zone_based_firewall.py index 716361994..690e5f0bf 100644 --- a/catalystwan/models/policy/definitions/zone_based_firewall.py +++ b/catalystwan/models/policy/definitions/zone_based_firewall.py @@ -7,7 +7,10 @@ from catalystwan.models.misc.application_protocols import ApplicationProtocol from catalystwan.models.policy.policy_definition import ( + AdvancedInspectionProfileAction, AppListEntry, + AppListFlatEntry, + ConnectionEventsAction, DefinitionWithSequencesCommonBase, DestinationDataPrefixListEntry, DestinationFQDNEntry, @@ -38,6 +41,7 @@ ZoneBasedFWPolicySequenceEntry = Annotated[ Union[ AppListEntry, + AppListFlatEntry, DestinationDataPrefixListEntry, DestinationFQDNEntry, DestinationGeoLocationEntry, @@ -69,6 +73,15 @@ Field(discriminator="field"), ] +ZoneBasedFWPolicyActions = Annotated[ + Union[ + AdvancedInspectionProfileAction, + ConnectionEventsAction, + LogAction, + ], + Field(discriminator="type"), +] + class ZoneBasedFWPolicyMatches(Match): entries: List[ZoneBasedFWPolicySequenceEntry] = [] @@ -80,7 +93,7 @@ class ZoneBasedFWPolicySequenceWithRuleSets(PolicyDefinitionSequenceBase): ) match: ZoneBasedFWPolicyMatches ruleset: bool = True - actions: List[LogAction] = [] + actions: List[ZoneBasedFWPolicyActions] = [] model_config = ConfigDict(populate_by_name=True) def match_rule_set_lists(self, rule_set_ids: Set[UUID]) -> None: @@ -189,7 +202,7 @@ class ZoneBasedFWPolicyDefinition(DefinitionWithSequencesCommonBase): class ZoneBasedFWPolicy(ZoneBasedFWPolicyHeader): type: Literal["zoneBasedFW"] = "zoneBasedFW" - mode: Literal["security"] = "security" + mode: Literal["security", "unified"] = "security" definition: ZoneBasedFWPolicyDefinition = ZoneBasedFWPolicyDefinition() def add_ipv4_rule( diff --git a/catalystwan/models/policy/lists.py b/catalystwan/models/policy/lists.py index ea6944b12..41abcb5a3 100644 --- a/catalystwan/models/policy/lists.py +++ b/catalystwan/models/policy/lists.py @@ -1,4 +1,4 @@ -from ipaddress import IPv4Address, IPv4Network, IPv6Network +from ipaddress import IPv4Address, IPv4Network, IPv6Interface from typing import Any, List, Literal, Optional, Set, Tuple from uuid import UUID @@ -184,7 +184,7 @@ class DataIPv6PrefixList(PolicyListBase): type: Literal["dataIpv6Prefix"] = "dataIpv6Prefix" entries: List[DataIPv6PrefixListEntry] = [] - def add_prefix(self, ipv6_prefix: IPv6Network) -> None: + def add_prefix(self, ipv6_prefix: IPv6Interface) -> None: self._add_entry(DataIPv6PrefixListEntry(ipv6_prefix=ipv6_prefix)) diff --git a/catalystwan/models/policy/lists_entries.py b/catalystwan/models/policy/lists_entries.py index 747d77a54..f03df04db 100644 --- a/catalystwan/models/policy/lists_entries.py +++ b/catalystwan/models/policy/lists_entries.py @@ -1,4 +1,4 @@ -from ipaddress import IPv4Address, IPv4Network, IPv6Network +from ipaddress import IPv4Address, IPv4Network, IPv6Interface, IPv6Network from typing import List, Literal, Optional, Set from uuid import UUID @@ -235,7 +235,7 @@ class ColorListEntry(BaseModel): class DataIPv6PrefixListEntry(BaseModel): model_config = ConfigDict(populate_by_name=True) - ipv6_prefix: IPv6Network = Field(serialization_alias="ipv6Prefix", validation_alias="ipv6Prefix") + ipv6_prefix: IPv6Interface = Field(serialization_alias="ipv6Prefix", validation_alias="ipv6Prefix") class LocalDomainListEntry(BaseModel): diff --git a/catalystwan/models/policy/policy_definition.py b/catalystwan/models/policy/policy_definition.py index c8388eb71..693c84a64 100644 --- a/catalystwan/models/policy/policy_definition.py +++ b/catalystwan/models/policy/policy_definition.py @@ -515,6 +515,11 @@ class AppListEntry(BaseModel): ref: UUID +class AppListFlatEntry(BaseModel): + field: Literal["appListFlat"] = "appListFlat" + ref: UUID + + class SourceFQDNListEntry(BaseModel): field: Literal["sourceFqdnList"] = "sourceFqdnList" ref: UUID @@ -750,6 +755,16 @@ 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, @@ -784,8 +799,10 @@ class ActionSet(BaseModel): ActionEntry = Annotated[ Union[ ActionSet, + AdvancedInspectionProfileAction, CFlowDAction, ClassMapAction, + ConnectionEventsAction, CountAction, DREOptimizationAction, FallBackToRoutingAction, @@ -808,6 +825,7 @@ class ActionSet(BaseModel): MatchEntry = Annotated[ Union[ AppListEntry, + AppListFlatEntry, CarrierEntry, ClassMapListEntry, ColorListEntry, @@ -909,7 +927,9 @@ class PolicyDefinitionSequenceBase(BaseModel): default="drop", serialization_alias="baseAction", validation_alias="baseAction" ) sequence_type: SequenceType = Field(serialization_alias="sequenceType", validation_alias="sequenceType") - sequence_ip_type: SequenceIpType = Field(serialization_alias="sequenceIpType", validation_alias="sequenceIpType") + sequence_ip_type: Optional[SequenceIpType] = Field( + default="ipv4", serialization_alias="sequenceIpType", validation_alias="sequenceIpType" + ) ruleset: Optional[bool] = None match: Match actions: Sequence[ActionEntry] diff --git a/catalystwan/workflows/config_migration.py b/catalystwan/workflows/config_migration.py index abac5fcc5..7d79db6af 100644 --- a/catalystwan/workflows/config_migration.py +++ b/catalystwan/workflows/config_migration.py @@ -3,7 +3,7 @@ from catalystwan.api.policy_api import POLICY_LIST_ENDPOINTS_MAP from catalystwan.endpoints.configuration_group import ConfigGroup -from catalystwan.models.configuration.config_migration import ConfigGroupPreset, UX1Config, UX2Config +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 @@ -19,16 +19,14 @@ def log_progress(task: str, completed: int, total: int) -> None: def transform(ux1: UX1Config) -> UX2Config: ux2 = UX2Config() - ux2.config_group_presets.append(ConfigGroupPreset(config_group_name="Default_Config_Group")) - profile_parcels = ux2.config_group_presets[0].profile_parcels # Feature Templates - for ft in ux1.templates.features: + for ft in ux1.templates.feature_templates: if ft.template_type in SUPPORTED_TEMPLATE_TYPES: - profile_parcels.append(create_parcel_from_template(ft)) + 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: - profile_parcels.append(parcel) + ux2.profile_parcels.append(parcel) return ux2 @@ -71,10 +69,10 @@ def collect_ux1_config(session: ManagerSession, progress: Callable[[str, int, in template_api = session.api.templates progress("Collecting Templates Info", 0, 2) - ux1.templates.features = [t for t in template_api.get_feature_templates()] + ux1.templates.feature_templates = [t for t in template_api.get_feature_templates()] progress("Collecting Templates Info", 1, 2) - ux1.templates.devices = [t for t in template_api.get_device_templates()] + ux1.templates.device_templates = [t for t in template_api.get_device_templates()] progress("Collecting Templates Info", 2, 2) return ux1 @@ -98,7 +96,7 @@ def push_ux2_config(session: ManagerSession, config: UX2Config) -> ConfigGroup: config_group_creator = ConfigGroupCreator(session, config, logger) config_group = config_group_creator.create() feature_profiles = config_group.profiles # noqa: F841 - for parcels in config.config_group_presets: + 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