From 30ca2cfc6621a53f38fcb9b8dbc7653c89ff4b9f Mon Sep 17 00:00:00 2001 From: Jakub Krajewski Date: Wed, 6 Mar 2024 17:32:29 +0100 Subject: [PATCH] Cover system models --- .../feature_profile/sdwan/system/ntp.py | 10 ++- .../converters/feature_template/aaa.py | 80 +++++++++--------- .../converters/feature_template/banner.py | 13 +-- .../converters/feature_template/basic.py | 81 ++++++++++++++----- .../converters/feature_template/bfd.py | 25 +++--- .../feature_template/factory_method.py | 2 + .../converters/feature_template/global_.py | 27 +++---- .../converters/feature_template/logging_.py | 48 +++++------ .../converters/feature_template/ntp.py | 20 +++++ .../converters/feature_template/omp.py | 52 ++++++------ .../converters/feature_template/security.py | 37 +++++---- 11 files changed, 232 insertions(+), 163 deletions(-) create mode 100644 catalystwan/utils/config_migration/converters/feature_template/ntp.py diff --git a/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py b/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py index 1b7a9acb..c5b0eac7 100644 --- a/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py +++ b/catalystwan/models/configuration/feature_profile/sdwan/system/ntp.py @@ -1,6 +1,6 @@ from __future__ import annotations -from ipaddress import IPv6Address +from ipaddress import IPv4Address, IPv6Address from typing import List, Literal, Optional, Union from pydantic import AliasPath, BaseModel, ConfigDict, Field @@ -13,21 +13,23 @@ class ServerItem(BaseModel): extra="forbid", populate_by_name=True, ) - name: Union[Variable, Global[IPv6Address]] = Field(..., description="Set hostname or IP address of server") + 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(..., description="Set NTP version") + 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[Literal[False]]] = Field( + prefer: Union[Variable, Global[bool], Default[bool]] = Field( default=as_default(False), description="Variable this NTP server" ) diff --git a/catalystwan/utils/config_migration/converters/feature_template/aaa.py b/catalystwan/utils/config_migration/converters/feature_template/aaa.py index 88c5c8f0..afe533c3 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/aaa.py +++ b/catalystwan/utils/config_migration/converters/feature_template/aaa.py @@ -1,3 +1,4 @@ +from copy import deepcopy from typing import List from catalystwan.api.configuration_groups.parcel import Global @@ -10,57 +11,52 @@ class AAATemplateConverter: @staticmethod def create_parcel(name: str, description: str, template_values: dict) -> AAAParcel: """ - Creates an AAA object based on the provided template values. + 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: - AAA: An AAA object with the provided template values. + AAAParcel: An AAAParcel object with the provided template values. """ - template_values["name"] = name - template_values["description"] = description - delete_properties = ( - "radius_client", - "radius_trustsec_group", - "rda_server_key", - "domain_stripping", - "auth_type", - "port", - "cts_auth_list", - ) - - if template_values.get("server_auth_order") is not None: - global_object = template_values["server_auth_order"] - template_values["server_auth_order"] = Global[List[str]](value=global_object.value.split(",")) - - if template_values.get("radius") is not None: - print(template_values["radius"]) - radius_list = template_values["radius"] - for radius in radius_list: - servers = radius.get("server", {}) + 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)) - if template_values.get("tacacs") is not None: - tacacs_list = template_values["tacacs"] - for tacacs in tacacs_list: - servers = tacacs.get("server", {}) - for server in servers: - key_enum = server.get("key_enum") - server["key_enum"] = Global[str](value=str(key_enum.value)) - - if template_values.get("accounting_rule") is not None: - accounting_rule = template_values["accounting_rule"] - for rule_item in accounting_rule: + def assign_rules(rules: List) -> None: + for rule_item in rules: rule_item["group"] = Global[List[str]](value=rule_item["group"].value.split(",")) - if template_values.get("authorization_rule") is not None: - authorization_rule = template_values["authorization_rule"] - for rule_item in authorization_rule: - rule_item["group"] = Global[List[str]](value=rule_item["group"].value.split(",")) + parcel_values = deepcopy(template_values) + parcel_values["parcel_name"] = name + parcel_values["parcel_description"] = description + + if server_auth_order := template_values.get("server_auth_order"): + parcel_values["server_auth_order"] = Global[List[str]](value=server_auth_order.value.split(",")) - for prop in delete_properties: - if template_values.get(prop) is not None: - del template_values[prop] + 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", + ]: + parcel_values.pop(key, None) - return AAAParcel(**template_values) + 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 index e7a061e1..5aa4550b 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/banner.py +++ b/catalystwan/utils/config_migration/converters/feature_template/banner.py @@ -7,11 +7,14 @@ class BannerTemplateConverter: @staticmethod def create_parcel(name: str, description: str, template_values: dict) -> BannerParcel: """ - Creates an AAA object based on the provided template values. + 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: - AAA: An AAA object with the provided template values. + BannerParcel: A BannerParcel object with the provided template values. """ - template_values["name"] = name - template_values["description"] = description - return BannerParcel(**template_values) + return BannerParcel(parcel_name=name, parcel_description=description, **template_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/basic.py b/catalystwan/utils/config_migration/converters/feature_template/basic.py index eb0d5e6a..968c50ae 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/basic.py +++ b/catalystwan/utils/config_migration/converters/feature_template/basic.py @@ -1,6 +1,9 @@ -from catalystwan.api.configuration_groups.parcel import Global +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: @@ -9,25 +12,63 @@ class SystemToBasicTemplateConverter: @staticmethod def create_parcel(name: str, description: str, template_values: dict) -> BasicParcel: """ - Creates an AAA object based on the provided template values. + 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: - AAA: An AAA object with the provided template values. + BasicParcel: A BasicParcel object with the provided template values. """ - template_values["name"] = name - template_values["parcel_description"] = description - if template_values.get("console_baud_rate") is not None: - value = template_values["console_baud_rate"].value - if value == "": - value = "9600" # Default value for console baud rate - template_values["console_baud_rate"] = Global[ConsoleBaudRate](value=value) - - if template_values.get("site_id") is not None: - del template_values["site_id"] - if template_values.get("system_ip") is not None: - del template_values["system_ip"] - if template_values.get("host_name") is not None: - del template_values["host_name"] - if template_values.get("enable") is not None: - del template_values["enable"] - return BasicParcel(**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 index 2a7cd1e0..5b30dc85 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/bfd.py +++ b/catalystwan/utils/config_migration/converters/feature_template/bfd.py @@ -2,21 +2,24 @@ class BFDTemplateConverter: - supported_template_types = ("cisco_bfd",) + supported_template_types = ("cisco_bfd", "bfd-vedge") @staticmethod def create_parcel(name: str, description: str, template_values: dict) -> BFDParcel: """ - Creates an BFD object based on the provided template values. + 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: - BFD: An BFD object with the provided template values. + BFDParcel: A BFDParcel object with the provided template values. """ - template_values["name"] = name - template_values["description"] = description - - if template_values.get("color") is not None: - template_values["colors"] = template_values["color"] - del template_values["color"] - - return BFDParcel(**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 index 78a0684f..a6748ca7 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/factory_method.py +++ b/catalystwan/utils/config_migration/converters/feature_template/factory_method.py @@ -15,6 +15,7 @@ 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 @@ -29,6 +30,7 @@ GlobalTemplateConverter, LoggingTemplateConverter, OMPTemplateConverter, + NTPTemplateConverter, ] diff --git a/catalystwan/utils/config_migration/converters/feature_template/global_.py b/catalystwan/utils/config_migration/converters/feature_template/global_.py index bbc97b4b..4e029346 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/global_.py +++ b/catalystwan/utils/config_migration/converters/feature_template/global_.py @@ -1,3 +1,6 @@ +from copy import deepcopy + +from catalystwan.api.configuration_groups.parcel import as_variable from catalystwan.models.configuration.feature_profile.sdwan.system import GlobalParcel @@ -10,19 +13,15 @@ def create_parcel(name: str, description: str, template_values: dict) -> GlobalP Creates an Logging object based on the provided template values. Returns: - Logging: An Logging object with the provided template values. + GlobalParcel: A GlobalParcel object with the provided template values. """ - template_values["services_global"] = {} - template_values["services_global"]["services_ip"] = {} - - keys_to_delete = [] - for key, value in template_values.items(): - template_values["services_global"]["services_ip"][key] = value - keys_to_delete.append(key) - - for key in keys_to_delete: - del template_values[key] + values = deepcopy(template_values) + if source_intrf := values.get("source_intrf"): + values["source_intrf"] = as_variable(source_intrf.value) - template_values["name"] = name - template_values["description"] = description - return GlobalParcel(**template_values) + parcel_values = { + "parcel_name": name, + "parcel_description": description, + "services_global": {"services_ip": {key: value for key, value in values.items()}}, + } + return GlobalParcel(**parcel_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 index 0796a05e..399be09f 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/logging_.py +++ b/catalystwan/utils/config_migration/converters/feature_template/logging_.py @@ -1,4 +1,5 @@ -from typing import List +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 @@ -16,33 +17,34 @@ def create_parcel(name: str, description: str, template_values: dict) -> Logging Returns: Logging: An Logging object with the provided template values. """ - template_values["name"] = name - template_values["description"] = description - if template_values.get("tls_profile"): - tls_profiles = template_values["tls_profile"] + 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) - if template_values.get("server"): - servers = template_values["server"] - for server in servers: - server["name"] = Global[str](value=str(server["name"].value)) + for server in ["server", "ipv6_server"]: + if target_server := parcel_values.get(server): + parse_server_name(target_server) - if template_values.get("ipv6_server"): - ipv6_servers = template_values["ipv6_server"] - for server in ipv6_servers: - server["name"] = Global[str](value=str(server["name"].value)) - - if template_values.get("enable") is not None: - template_values["disk"] = { - "disk_enable": template_values["enable"], - "file": {"disk_file_size": template_values["size"], "disk_file_rotate": template_values["rotate"]}, - } - del template_values["enable"] - del template_values["size"] - del template_values["rotate"] + if parcel_values.get("enable"): + set_disk(parcel_values) - return LoggingParcel(**template_values) + return LoggingParcel(**parcel_values) diff --git a/catalystwan/utils/config_migration/converters/feature_template/ntp.py b/catalystwan/utils/config_migration/converters/feature_template/ntp.py new file mode 100644 index 00000000..1a83836b --- /dev/null +++ b/catalystwan/utils/config_migration/converters/feature_template/ntp.py @@ -0,0 +1,20 @@ +from copy import deepcopy + +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 = deepcopy(template_values) + parcel_values["parcel_name"] = name + parcel_values["parcel_description"] = description + 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 index 4070a342..e3703f66 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/omp.py +++ b/catalystwan/utils/config_migration/converters/feature_template/omp.py @@ -1,4 +1,6 @@ -from catalystwan.api.configuration_groups.parcel import Global +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 @@ -8,34 +10,26 @@ class OMPTemplateConverter: @staticmethod def create_parcel(name: str, description: str, template_values: dict) -> OMPParcel: """ - Creates an Logging object based on the provided template values. + 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: - Logging: An Logging object with the provided template values. + OMPParcel: An OMPParcel object with the provided template values. """ - template_values["name"] = name - template_values["description"] = description - - # advertise_ipv4: AdvertiseIpv4 = AdvertiseIpv4, - # advertise_ipv6: AdvertiseIpv6 = AdvertiseIpv6, - - if template_values.get("advertise") is not None: - template_values["advertise_ipv4"] = template_values["advertise"] - del template_values["advertise"] - set_true_protocols = {} - for definition in template_values["advertise_ipv4"]: - protocol = definition["protocol"].value - set_true_protocols[protocol] = Global[bool](value=True) - template_values["advertise_ipv4"] = set_true_protocols - - if template_values.get("ipv6_advertise") is not None: - template_values["advertise_ipv6"] = template_values["ipv6_advertise"] - del template_values["ipv6_advertise"] - set_true_protocols = {} - for definition in template_values["advertise_ipv6"]: - protocol = definition["protocol"].value - set_true_protocols[protocol] = Global[bool](value=True) - template_values["advertise_ipv6"] = set_true_protocols - - print(template_values) - return OMPParcel(**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 index 94f0d0aa..c080efc1 100644 --- a/catalystwan/utils/config_migration/converters/feature_template/security.py +++ b/catalystwan/utils/config_migration/converters/feature_template/security.py @@ -1,11 +1,18 @@ from typing import List -from catalystwan.api.configuration_groups.parcel import Global +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", @@ -16,20 +23,20 @@ class SecurityTemplateConverter: @staticmethod def create_parcel(name: str, description: str, template_values: dict) -> SecurityParcel: """ - Creates an AAA object based on the provided template values. + 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: - AAA: An AAA object with the provided template values. + SecurityParcel: A SecurityParcel object with the provided template values. """ - template_values["name"] = name - template_values["description"] = description - - if template_values.get("integrity_type") is not None: - template_values["integrity_type"] = Global[List[IntegrityType]]( - value=template_values["integrity_type"].value - ) - - if template_values.get("authentication_type") is not None: - del template_values["authentication_type"] - - return SecurityParcel(**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