diff --git a/catalystwan/models/configuration/config_migration.py b/catalystwan/models/configuration/config_migration.py index 2420577f..00a82074 100644 --- a/catalystwan/models/configuration/config_migration.py +++ b/catalystwan/models/configuration/config_migration.py @@ -554,6 +554,7 @@ class PolicyConvertContext: zone_based_firewall_residues: Dict[UUID, List[ZoneBasedFWPolicyEntry]] = field(default_factory=dict) security_policy_residues: Dict[UUID, SecurityPolicyResidues] = field(default_factory=dict) qos_map_residues: Dict[UUID, List[QoSMapResidues]] = field(default_factory=dict) + as_path_list_num_mapping: Dict[str, int] = field(default_factory=dict) def get_vpn_id_to_vpn_name_map(self) -> Dict[Union[str, int], List[str]]: vpn_map: Dict[Union[str, int], List[str]] = {} @@ -562,6 +563,14 @@ def get_vpn_id_to_vpn_name_map(self) -> Dict[Union[str, int], List[str]]: vpn_map[v].append(k) return vpn_map + def generate_as_path_list_num_from_name(self, name: str) -> int: + """The UX1 and UX2 intersection in AS Path list name and ID but only for the ISR Edge router (number 1 to 500). + If there is number we can insert the value in as_path_list_num field otherwise we will + generate the value and keep track of it in the context.""" + number = len(self.as_path_list_num_mapping) + 1 + self.as_path_list_num_mapping[name] = number + return number + @staticmethod def from_configs( network_hierarchy: List[NodeInfo], diff --git a/catalystwan/tests/config_migration/policy_converters/test_aspath.py b/catalystwan/tests/config_migration/policy_converters/test_aspath.py new file mode 100644 index 00000000..4e099c8c --- /dev/null +++ b/catalystwan/tests/config_migration/policy_converters/test_aspath.py @@ -0,0 +1,53 @@ +import unittest + +from catalystwan.models.configuration.config_migration import PolicyConvertContext +from catalystwan.models.configuration.feature_profile.sdwan.policy_object.policy.as_path import AsPathParcel +from catalystwan.models.policy.list.as_path import ASPathList, ASPathListEntry +from catalystwan.utils.config_migration.converters.policy.policy_lists import convert + + +class TestAsPathConverter(unittest.TestCase): + def setUp(self) -> None: + self.context = PolicyConvertContext() + + def test_aspath_with_name_conversion(self): + # Arrange + name = "aspath" + description = "aspath description" + entry_1 = "600008" + entry_2 = "600009" + entry_3 = "600010" + aspath = ASPathList( + name=name, + description=description, + ) + for entry in [entry_1, entry_2, entry_3]: + aspath._add_entry(ASPathListEntry(as_path=entry)) + # Act + parcel = convert(aspath, self.context).output + # Assert + assert isinstance(parcel, AsPathParcel) + assert parcel.parcel_name == name + assert parcel.parcel_description == description + assert len(parcel.entries) == 3 + assert parcel.entries[0].as_path.value == entry_1 + assert parcel.entries[1].as_path.value == entry_2 + assert parcel.entries[2].as_path.value == entry_3 + assert parcel.as_path_list_num.value == self.context.as_path_list_num_mapping[name] + + def test_aspath_with_number_conversion(self): + # Arrange + name = "490" + description = "aspath description" + aspath = ASPathList( + name=name, + description=description, + ) + # Act + parcel = convert(aspath, self.context).output + # Assert + assert isinstance(parcel, AsPathParcel) + assert parcel.parcel_name == name + assert parcel.parcel_description == description + assert len(parcel.entries) == 0 + assert parcel.as_path_list_num.value == 490 diff --git a/catalystwan/utils/config_migration/converters/policy/policy_lists.py b/catalystwan/utils/config_migration/converters/policy/policy_lists.py index d94f50a9..229caf5c 100644 --- a/catalystwan/utils/config_migration/converters/policy/policy_lists.py +++ b/catalystwan/utils/config_migration/converters/policy/policy_lists.py @@ -4,6 +4,7 @@ from pydantic import ValidationError +from catalystwan.api.configuration_groups.parcel import as_global from catalystwan.models.common import int_range_serializer, int_range_str_validator from catalystwan.models.configuration.config_migration import ConvertResult, PolicyConvertContext from catalystwan.models.configuration.feature_profile.sdwan.policy_object import ( @@ -92,11 +93,33 @@ def app_list(in_: AppList, context) -> ConvertResult[ApplicationListParcel]: return ConvertResult[ApplicationListParcel](output=out, status="complete") -def as_path(in_: ASPathList, context) -> ConvertResult[AsPathParcel]: - out = AsPathParcel(**_get_parcel_name_desc(in_)) +def as_path(in_: ASPathList, context: PolicyConvertContext) -> ConvertResult[AsPathParcel]: + """There is a mismatch between UX1 and UX2 models: + UX1: + - AS Path List Name (Alphanumeric value for vEdge, or number from 1 to 500 for ISR Edge router) + - AS Path list + + UX2: + - Parcel name + - Parcel description + - AS Path List ID (Number from 1 to 500) + - AS Path list + + The UX1 and UX2 intersection in AS Path list name and ID but only for the ISR Edge router (number 1 to 500). + If there is number we can insert the value in as_path_list_num field otherwise we will + generate the value and keep track of it in the context. + """ + result = ConvertResult[AsPathParcel](output=None) + if in_.name.isdigit(): + as_path_list_num = int(in_.name) + else: + as_path_list_num = context.generate_as_path_list_num_from_name(in_.name) + result.update_status("partial", f"Mapped AS Path List Name: '{in_.name}' to ID: '{as_path_list_num}'") + out = AsPathParcel(**_get_parcel_name_desc(in_), as_path_list_num=as_global(as_path_list_num)) for entry in in_.entries: out.add_as_path(entry.as_path) - return ConvertResult[AsPathParcel](output=out, status="complete") + result.output = out + return result def class_map(in_: ClassMapList, context) -> ConvertResult[FowardingClassParcel]: @@ -388,7 +411,7 @@ def zone(in_: ZoneList, context) -> ConvertResult[SecurityZoneListParcel]: CONVERTERS: Mapping[Type[Input], Callable[..., Output]] = { AppProbeClassList: app_probe, AppList: app_list, - # ASPathList: as_path, + ASPathList: as_path, ClassMapList: class_map, ColorList: color, CommunityList: community,