From 6182b2cf5358103df1b66b6b462d7bb929bfe61b Mon Sep 17 00:00:00 2001 From: Mateusz Lapinski Date: Fri, 15 Sep 2023 06:27:43 -0700 Subject: [PATCH 1/5] renaming template fields + unittests --- vmngclient/api/template_api.py | 11 +- vmngclient/api/templates/cli_template.py | 15 +- .../device_template/device_template.py | 8 +- vmngclient/api/templates/feature_template.py | 8 +- .../tests/templates/models/cisco_aaa.py | 8 +- .../tests/templates/models/cisco_vpn.py | 6 +- .../tests/templates/models/omp_vsmart.py | 10 +- .../tests/templates/test_chose_model.py | 6 +- .../tests/templates/test_deserialize_model.py | 10 +- .../tests/templates/test_generate_payload.py | 12 +- vmngclient/tests/test_cli_template.py | 25 +-- vmngclient/tests/test_templates.py | 157 +++++++++++++++++- 12 files changed, 218 insertions(+), 58 deletions(-) diff --git a/vmngclient/api/template_api.py b/vmngclient/api/template_api.py index d068688ba..a6f49e7d6 100644 --- a/vmngclient/api/template_api.py +++ b/vmngclient/api/template_api.py @@ -424,7 +424,7 @@ def create(self, template, debug: bool = False): if not template_id: raise NotImplementedError() - logger.info(f"Template {template.name} ({template_type}) was created successfully ({template_id}).") + logger.info(f"Template {template.template_name} ({template_type}) was created successfully ({template_id}).") return template_id def _create_feature_template(self, template: FeatureTemplate) -> str: @@ -477,7 +477,10 @@ def parse_general_template( if edit: template_id = ( - self.session.api.templates.get(DeviceTemplate).filter(name=device_template.name).single_or_default().id + self.session.api.templates.get(DeviceTemplate) + .filter(name=device_template.template_name) + .single_or_default() + .id ) payload = json.loads(device_template.generate_payload()) response = self.session.put(f"/dataservice/template/device/{template_id}", json=payload) @@ -537,8 +540,8 @@ def generate_feature_template_payload( self, template: FeatureTemplate, schema: Any, debug: bool = False ) -> FeatureTemplatePayload: payload = FeatureTemplatePayload( - name=template.name, - description=template.description, + name=template.template_name, + description=template.template_description, template_type=template.type, device_types=[device_model.value for device_model in template.device_models], definition={}, diff --git a/vmngclient/api/templates/cli_template.py b/vmngclient/api/templates/cli_template.py index 6af36e56e..74235c11c 100644 --- a/vmngclient/api/templates/cli_template.py +++ b/vmngclient/api/templates/cli_template.py @@ -22,9 +22,8 @@ @define class CLITemplate: - - name: str - description: str + template_name: str + template_description: str device_model: DeviceModel config: CiscoConfParse = CiscoConfParse([]) @@ -64,8 +63,8 @@ def load_running(self, session: vManageSession, device: Device) -> CiscoConfPars def generate_payload(self) -> dict: config_str = "\n".join(self.config.ioscfg) payload = { - "templateName": self.name, - "templateDescription": self.description, + "templateName": self.template_name, + "templateDescription": self.template_description, "deviceType": self.device_model.value, "templateConfiguration": config_str, "factoryDefault": False, @@ -97,8 +96,8 @@ def update(self, session: vManageSession, id: str, config: CiscoConfParse) -> bo config_str = "\n".join(self.config.ioscfg) payload = { "templateId": id, - "templateName": self.name, - "templateDescription": self.description, + "templateName": self.template_name, + "templateDescription": self.template_description, "deviceType": self.device_model.value, "templateConfiguration": config_str, "factoryDefault": False, @@ -113,7 +112,7 @@ def update(self, session: vManageSession, id: str, config: CiscoConfParse) -> bo logger.error(f'Response message: {response["message"]}') logger.error(f'Response details: {response["details"]}') return False - logger.info(f"Template with name: {self.name} - updated.") + logger.info(f"Template with name: {self.template_name} - updated.") return True @staticmethod diff --git a/vmngclient/api/templates/device_template/device_template.py b/vmngclient/api/templates/device_template/device_template.py index c4b85d939..b091a508e 100644 --- a/vmngclient/api/templates/device_template/device_template.py +++ b/vmngclient/api/templates/device_template/device_template.py @@ -34,15 +34,15 @@ class DeviceTemplate(BaseModel): "default_banner", # Banner ] >>> device_template = DeviceTemplate( - name="python", - description="python", + template_name="python", + template_description="python", general_templates=templates ) >>> session.api.templates.create(device_template) """ - name: str = Field(alias="templateName") - description: str = Field(alias="templateName") + template_name: str = Field(alias="templateName") + template_description: str = Field(alias="templateDescription") general_templates: List[GeneralTemplate] = Field(alias="generalTemplates") device_role: str = Field(default="", alias="deviceRole") device_type: str = Field(default="", alias="deviceType") diff --git a/vmngclient/api/templates/feature_template.py b/vmngclient/api/templates/feature_template.py index 3c310dc63..8ea526e96 100644 --- a/vmngclient/api/templates/feature_template.py +++ b/vmngclient/api/templates/feature_template.py @@ -16,8 +16,8 @@ class FeatureTemplate(BaseModel, ABC): - name: str - description: str + template_name: str + template_description: str device_models: List[DeviceModel] = [] device_specific_variables: Dict[str, DeviceVariable] = {} @@ -93,8 +93,8 @@ def get(cls, session: vManageSession, name: str) -> FeatureTemplate: ) return feature_template_model( - name=template_info.name, - description=template_info.description, + template_name=template_info.name, + template_description=template_info.description, device_models=[DeviceModel(model) for model in template_info.device_type], device_specific_variables=device_specific_variables, **values_from_template_definition, diff --git a/vmngclient/tests/templates/models/cisco_aaa.py b/vmngclient/tests/templates/models/cisco_aaa.py index bb367fbde..c4e7888c2 100644 --- a/vmngclient/tests/templates/models/cisco_aaa.py +++ b/vmngclient/tests/templates/models/cisco_aaa.py @@ -14,8 +14,8 @@ # CiscoAAAModel(domain-stripping="?") cisco_aaa = CiscoAAAModel( - name="iuo", - description="zyx", + template_name="iuo", + template_description="zyx", device_models=[DeviceModel.VEDGE_C8000V], user=users, authentication_group=True, @@ -29,8 +29,8 @@ ) cisco_aaa_device_specific = CiscoAAAModel( - name="cisco_aaa_device_specific", - description="caaadp", + template_name="cisco_aaa_device_specific", + template_description="caaadp", device_models=[DeviceModel.VEDGE_C8000V], user=users, authentication_group=True, diff --git a/vmngclient/tests/templates/models/cisco_vpn.py b/vmngclient/tests/templates/models/cisco_vpn.py index 8823a4199..6bca2c66d 100644 --- a/vmngclient/tests/templates/models/cisco_vpn.py +++ b/vmngclient/tests/templates/models/cisco_vpn.py @@ -4,13 +4,13 @@ from vmngclient.utils.device_model import DeviceModel basic_cisco_vpn = CiscoVPNModel( - name="Basic_Cisco_VPN_Model", description="Primitive", device_models=[DeviceModel.VEDGE_C8000V] + template_name="Basic_Cisco_VPN_Model", template_description="Primitive", device_models=[DeviceModel.VEDGE_C8000V] ) # type: ignore complex_cisco_vpn = CiscoVPNModel( - name="Complex_CiscoVPN_Model", - description="Complex", + template_name="Complex_CiscoVPN_Model", + template_description="Complex", device_models=[DeviceModel.VEDGE_C8000V], vpn_id=123, vpn_name="VPN", diff --git a/vmngclient/tests/templates/models/omp_vsmart.py b/vmngclient/tests/templates/models/omp_vsmart.py index a487fab19..67780425f 100644 --- a/vmngclient/tests/templates/models/omp_vsmart.py +++ b/vmngclient/tests/templates/models/omp_vsmart.py @@ -3,12 +3,12 @@ from vmngclient.api.templates.models.omp_vsmart_model import OMPvSmart from vmngclient.utils.device_model import DeviceModel -default_omp = OMPvSmart(name="omp_1", description="default", device_models=[DeviceModel.VEDGE_C8000V]) +default_omp = OMPvSmart(template_name="omp_1", template_description="default", device_models=[DeviceModel.VEDGE_C8000V]) omp_2 = OMPvSmart( - name="omp_2", - description="some changes", + template_name="omp_2", + template_description="some changes", device_models=[DeviceModel.VEDGE_C8000V], graceful_restart=False, send_backup_paths=False, @@ -17,8 +17,8 @@ ) omp_3 = OMPvSmart( - name="omp_3", - description="advanced", + template_name="omp_3", + template_description="advanced", device_models=[DeviceModel.VEDGE_C8000V], graceful_restart=False, graceful_restart_timer=DeviceVariable(name="omp_graceful_restart_timer"), diff --git a/vmngclient/tests/templates/test_chose_model.py b/vmngclient/tests/templates/test_chose_model.py index c1ee3613d..69062599e 100644 --- a/vmngclient/tests/templates/test_chose_model.py +++ b/vmngclient/tests/templates/test_chose_model.py @@ -17,18 +17,18 @@ class TestChooseModel(unittest.TestCase): ("omp-vsmart", OMPvSmart), ("security-vsmart", SecurityvSmart), ("system-vsmart", SystemVsmart), - ("cisco_system", CiscoSystemModel) + ("cisco_system", CiscoSystemModel), ] ) def test_choose_model(self, model_type, model_cls): # Arrange name = "My model name" description = "My model description" - model_from_cls = model_cls(name=name, description=description) + model_from_cls = model_cls(template_name=name, template_description=description) # Act chosen_model = choose_model(model_type) - model_from_choice = chosen_model(name=name, description=description) + model_from_choice = chosen_model(template_name=name, template_description=description) # Assert self.assertEqual(model_from_choice, model_from_cls) diff --git a/vmngclient/tests/templates/test_deserialize_model.py b/vmngclient/tests/templates/test_deserialize_model.py index 0cc7822bb..69b28bbc2 100644 --- a/vmngclient/tests/templates/test_deserialize_model.py +++ b/vmngclient/tests/templates/test_deserialize_model.py @@ -22,7 +22,9 @@ def setUp(self): for template in map(models.__dict__.get, models.__all__): definition: Dict[str, Any] - with open(Path(__file__).resolve().parents[0] / Path("definitions") / Path(template.name + ".json")) as f: + with open( + Path(__file__).resolve().parents[0] / Path("definitions") / Path(template.template_name + ".json") + ) as f: definition = json.load(f) feature_template_response.append( { @@ -30,9 +32,9 @@ def setUp(self): "resource_group": "global", "id": "xxx", "factory_default": "False", - "name": template.name, + "name": template.template_name, "devices_attached": 1, - "description": template.description, + "description": template.template_description, "last_updated_on": 1111111111111, "template_type": template.type, "device_type": ["vedge-C8000V"], @@ -53,7 +55,7 @@ def test_get(self, template: FeatureTemplate, mock_session): mock_session.api.templates._get_feature_templates.return_value = self.get_feature_templates_response # Act - feature_template_from_get = FeatureTemplate.get(session=mock_session, name=template.name) + feature_template_from_get = FeatureTemplate.get(session=mock_session, name=template.template_name) # Assert self.assertEqual(feature_template_from_get, template) diff --git a/vmngclient/tests/templates/test_generate_payload.py b/vmngclient/tests/templates/test_generate_payload.py index 3b14e4de1..877ca3fff 100644 --- a/vmngclient/tests/templates/test_generate_payload.py +++ b/vmngclient/tests/templates/test_generate_payload.py @@ -19,8 +19,8 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - name: str = "test" - description: str = "test" + template_name: str = "test" + template_description: str = "test" payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "test_type" @@ -32,8 +32,8 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - name: str = "test" - description: str = "test" + template_name: str = "test" + template_description: str = "test" payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "test_type" @@ -63,8 +63,8 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - name: str = "test" - description: str = "test" + template_name: str = "test" + template_description: str = "test" payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "test_type" diff --git a/vmngclient/tests/test_cli_template.py b/vmngclient/tests/test_cli_template.py index 9c8cb6e0e..3d0a6860d 100644 --- a/vmngclient/tests/test_cli_template.py +++ b/vmngclient/tests/test_cli_template.py @@ -38,7 +38,7 @@ def test_load_success(self, mock_session): # Arrange mock_session.get_json.return_value = {"configType": "file", "templateConfiguration": self.config} # Act - temp = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE) + temp = CLITemplate(template_name="test", template_description="test", device_model=DeviceModel.VEDGE) answer = temp.load(session=mock_session, id="temp_id") # Assert self.assertEqual(answer.ioscfg, self.template.ioscfg) @@ -52,7 +52,7 @@ def test_load_raise(self, mock_session): "templateConfiguration": self.config, } # Act - temp = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE) + temp = CLITemplate(template_name="test", template_description="test", device_model=DeviceModel.VEDGE) def answer(): return temp.load(session=mock_session, id="temp_id") @@ -77,14 +77,16 @@ def test_load_running_success(self, mock_session): model="vedge-cloud", ) # Act - temp = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE) + temp = CLITemplate(template_name="test", template_description="test", device_model=DeviceModel.VEDGE) answer = temp.load_running(session=mock_session, device=device) # Assert self.assertEqual(answer.ioscfg, self.template.ioscfg) def test_generate_payload(self): # Arrange - template = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE, config=self.template) + template = CLITemplate( + template_name="test", template_description="test", device_model=DeviceModel.VEDGE, config=self.template + ) # Act answer = template.generate_payload() # Assert @@ -120,7 +122,10 @@ def test_generate_payload(self): def test_generate_payload_cedge(self): # Arrange template = CLITemplate( - name="test", description="test", device_model=DeviceModel.VEDGE_C8000V, config=self.template + template_name="test", + template_description="test", + device_model=DeviceModel.VEDGE_C8000V, + config=self.template, ) # Act answer = template.generate_payload() @@ -167,7 +172,7 @@ def test_update_suceess(self, mock_session): ) config = CiscoConfParse(templateConfiguration.splitlines()) # Act - template = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE) + template = CLITemplate(template_name="test", template_description="test", device_model=DeviceModel.VEDGE) result = template.update(session=mock_session, id="temp_id", config=config) # Assert self.assertTrue(result) @@ -184,7 +189,7 @@ def test_update_template_failure(self, mock_session): " system-ip 192.168.1.26\n" ) config = CiscoConfParse(templateConfiguration.splitlines()) - template = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE) + template = CLITemplate(template_name="test", template_description="test", device_model=DeviceModel.VEDGE) # Act with self.assertRaises(HTTPError): @@ -205,8 +210,8 @@ def test_compare_template(self): ) config2 = CiscoConfParse(templateConfiguration2.splitlines()) # Act - result = CLITemplate(name="test", description="test", device_model=DeviceModel.VEDGE).compare_template( - config1, config2 - ) + result = CLITemplate( + template_name="test", template_description="test", device_model=DeviceModel.VEDGE + ).compare_template(config1, config2) # Assert self.assertEqual(result, "") diff --git a/vmngclient/tests/test_templates.py b/vmngclient/tests/test_templates.py index a6e2ed087..9df43f9c7 100644 --- a/vmngclient/tests/test_templates.py +++ b/vmngclient/tests/test_templates.py @@ -1,12 +1,19 @@ import unittest from unittest.mock import patch +from parameterized import parameterized + from vmngclient.api.task_status_api import SubTaskData, TaskResult from vmngclient.api.template_api import TemplatesAPI +from vmngclient.api.templates.cli_template import CLITemplate +from vmngclient.api.templates.device_template.device_template import DeviceTemplate from vmngclient.api.templates.feature_template import FeatureTemplate +from vmngclient.api.templates.models.cisco_aaa_model import CiscoAAAModel +from vmngclient.api.templates.payloads.aaa.aaa_model import AAAModel, AuthenticationOrder from vmngclient.dataclasses import Device, FeatureTemplateInfo, TemplateInfo from vmngclient.typed_list import DataSequence from vmngclient.utils.creation_tools import create_dataclass +from vmngclient.utils.device_model import DeviceModel from vmngclient.utils.personality import Personality from vmngclient.utils.reachability import Reachability @@ -46,7 +53,8 @@ def setUp(self): }, ] self.templates = DataSequence( - TemplateInfo, [create_dataclass(TemplateInfo, template) for template in self.data_template] + TemplateInfo, + [create_dataclass(TemplateInfo, template) for template in self.data_template], ) self.device_info = Device( personality=Personality.EDGE, @@ -79,7 +87,6 @@ def setUp(self): @patch("vmngclient.response.vManageResponse") @patch("vmngclient.session.vManageSession") def test_templates_success(self, mock_session, mocked_response): - # Arrange mock_session.get.return_value = mocked_response mocked_response.dataseq.return_value = self.templates @@ -93,7 +100,6 @@ def test_templates_success(self, mock_session, mocked_response): @patch("vmngclient.response.vManageResponse") @patch("vmngclient.session.vManageSession") def test_templates_get(self, mock_session, mocked_response): - # Arrange mock_session.get_data.return_value = mocked_response mocked_response.dataseq.return_value = DataSequence(FeatureTemplateInfo, []) @@ -104,6 +110,151 @@ def test_templates_get(self, mock_session, mocked_response): # Assert mock_session.get.assert_called_once_with(url="/dataservice/template/feature", params={"summary": True}) + @parameterized.expand( + [ + ( + AAAModel( + template_name="test_template", + template_description="test_description", + auth_order=[AuthenticationOrder.LOCAL], + auth_fallback=False, + auth_disable_audit_logs=True, + auth_admin_order=True, + auth_disable_netconf_logs=False, + ), + "aaa_id", + ), + ( + DeviceTemplate( + template_name="test_device_template", + template_description="test_device_template_description", + general_templates=[], + ), + "device_template_id", + ), + ( + CLITemplate(template_name="test", template_description="test", device_model=DeviceModel.VEDGE), + "cli_id", + ), + ( + CiscoAAAModel( + template_name="test_aaa_model", + template_description="test_aaa_description", + ), + "generator_id", + ), + ] + ) + @patch("vmngclient.session.vManageSession") + @patch("vmngclient.api.template_api.TemplatesAPI.create_by_generator") + @patch("vmngclient.api.template_api.TemplatesAPI._create_feature_template") + @patch("vmngclient.api.template_api.TemplatesAPI._create_device_template") + @patch("vmngclient.api.template_api.TemplatesAPI._create_cli_template") + def test_templates_create_success( + self, + template, + template_id, + mock_create_cli_template, + mock_create_device_template, + mock_create_feature_template, + mock_create_by_generator, + mock_session, + ): + # Arrange + mock_create_by_generator.return_value = "generator_id" + mock_create_feature_template.return_value = "aaa_id" + mock_create_device_template.return_value = "device_template_id" + mock_create_cli_template.return_value = "cli_id" + + # Act + templates_api = TemplatesAPI(mock_session) + + # Assert + self.assertEqual(templates_api.create(template), template_id) + + @parameterized.expand( + [ + ( + [ + AAAModel( + template_name="test_template", + template_description="test_description", + auth_order=[AuthenticationOrder.LOCAL], + auth_fallback=False, + auth_disable_audit_logs=True, + auth_admin_order=True, + auth_disable_netconf_logs=False, + ), + DeviceTemplate( + template_name="test_device_template", + template_description="test_device_template_description", + general_templates=[], + ), + CLITemplate( + template_name="test_cli_template", + template_description="test_cli_description", + device_model=DeviceModel.VBOND, + ), + CiscoAAAModel( + template_name="test_aaa_model", + template_description="test_aaa_description", + ), + ], + ["aaa_id", "device_template_id", "cli_id", "generator_id"], + ), + ] + ) + @patch("vmngclient.session.vManageSession") + @patch("vmngclient.api.template_api.TemplatesAPI.create_by_generator") + @patch("vmngclient.api.template_api.TemplatesAPI._create_feature_template") + @patch("vmngclient.api.template_api.TemplatesAPI._create_device_template") + @patch("vmngclient.api.template_api.TemplatesAPI._create_cli_template") + def test_templates_create_list_success( + self, + templates, + template_ids, + mock_create_cli_template, + mock_create_device_template, + mock_create_feature_template, + mock_create_by_generator, + mock_session, + ): + # Arrange + mock_create_by_generator.return_value = "generator_id" + mock_create_feature_template.return_value = "aaa_id" + mock_create_device_template.return_value = "device_template_id" + mock_create_cli_template.return_value = "cli_id" + + # Act + templates_api = TemplatesAPI(mock_session) + + # Assert + self.assertEqual(templates_api.create(templates), template_ids) + + @patch("vmngclient.session.vManageSession") + @patch("vmngclient.api.template_api.TemplatesAPI.create_by_generator") + @patch("vmngclient.api.template_api.TemplatesAPI._create_feature_template") + @patch("vmngclient.api.template_api.TemplatesAPI._create_device_template") + @patch("vmngclient.api.template_api.TemplatesAPI._create_cli_template") + def test_templates_create_failure( + self, + mock_create_cli_template, + mock_create_device_template, + mock_create_feature_template, + mock_create_by_generator, + mock_session, + ): + # Act + templates_api = TemplatesAPI(mock_session) + + # Assert + with self.assertRaises(NotImplementedError): + templates_api.create(None) + assert not mock_create_cli_template.called + assert not mock_create_device_template.called + assert not mock_create_feature_template.called + assert not mock_create_by_generator.called + # @patch.object(TemplatesAPI, "templates") # @patch("vmngclient.api.template_api.wait_for_completed") # @patch("vmngclient.session.vManageSession") From 18081dcdde9bf1f55df4d1affc302c0df410fbac Mon Sep 17 00:00:00 2001 From: Mateusz Lapinski Date: Mon, 18 Sep 2023 06:09:36 -0700 Subject: [PATCH 2/5] updating generator to include data paths --- vmngclient/api/template_api.py | 18 ++-- .../api/templates/feature_template_field.py | 15 +++- .../templates/models/cisco_banner_model.py | 4 +- .../api/templates/models/cisco_bgp_model.py | 82 +++++++++---------- .../templates/models/cisco_logging_model.py | 22 ++--- .../api/templates/models/cisco_ntp_model.py | 10 +-- vmngclient/api/templates/models/cisco_ospf.py | 26 +++--- .../api/templates/models/cisco_snmp_model.py | 2 +- .../api/templates/models/cisco_system.py | 27 +++--- .../models/cisco_vpn_interface_model.py | 52 ++++++------ .../api/templates/models/cisco_vpn_model.py | 28 +++---- .../templates/definitions/basic/children.json | 66 ++++++++------- .../definitions/basic/children_nested.json | 57 +++++++------ .../templates/schemas/basic/children.json | 2 +- .../schemas/basic/children_nested.json | 4 +- .../tests/templates/test_generate_payload.py | 4 +- vmngclient/utils/pydantic_validators.py | 20 +++++ 17 files changed, 239 insertions(+), 200 deletions(-) diff --git a/vmngclient/api/template_api.py b/vmngclient/api/template_api.py index a6f49e7d6..8eb21b297 100644 --- a/vmngclient/api/template_api.py +++ b/vmngclient/api/template_api.py @@ -557,17 +557,12 @@ def generate_feature_template_payload( if field.key in template.device_specific_variables: value = template.device_specific_variables[field.key] else: - # Iterate through every possible field, maybe refactor(?) - # Use data_path instead. data_path as tuple - # next(field_value.field_info.extra.get("vmanage_key") == field.key, template.__fields__.values()) for field_name, field_value in template.__fields__.items(): - if "vmanage_key" in field_value.field_info.extra: # type: ignore - vmanage_key = field_value.field_info.extra.get("vmanage_key") # type: ignore - if vmanage_key != field.key: - break - - value = template.dict(by_alias=True).get(field_name, None) - field_value.field_info.extra.pop("vmanage_key") # type: ignore + if ( + field.dataPath == field_value.field_info.extra.get("data_path", []) # type: ignore + and field.key == field_value.alias + ): + value = getattr(template, field_name) break if value is None: value = template.dict(by_alias=True).get(field.key, None) @@ -578,9 +573,6 @@ def generate_feature_template_payload( if template.type == "cisco_vpn_interface" and value is None: continue - if template.type == "cisco_ospf" and value is None: - continue - if isinstance(value, bool): value = str(value).lower() # type: ignore diff --git a/vmngclient/api/templates/feature_template_field.py b/vmngclient/api/templates/feature_template_field.py index 2a7dbedd5..375e775a1 100644 --- a/vmngclient/api/templates/feature_template_field.py +++ b/vmngclient/api/templates/feature_template_field.py @@ -4,8 +4,10 @@ from typing import Any, Dict, List, Optional from pydantic import BaseModel, Field, validator +from pydantic.fields import ModelField # type: ignore from vmngclient.api.templates.device_variable import DeviceVariable +from vmngclient.api.templates.feature_template import FeatureTemplate class FeatureTemplateOptionType(str, Enum): @@ -111,11 +113,18 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic for child in self.children: # Child in schema if current_path is None: current_path = [] - child_payload.update( - child.payload_scheme( - obj[child.key], help=output, current_path=self.dataPath + [self.key] + obj: FeatureTemplate # type: ignore + model_field: ModelField = next( + filter( + lambda f: f.field_info.extra.get("data_path", []) == child.dataPath + and f.alias == child.key, + obj.__fields__.values(), ) ) + obj_value = getattr(obj, model_field.name) + child_payload.update( + child.payload_scheme(obj_value, help=output, current_path=self.dataPath + [self.key]) + ) children_output.append(child_payload) output["vipValue"] = children_output else: diff --git a/vmngclient/api/templates/models/cisco_banner_model.py b/vmngclient/api/templates/models/cisco_banner_model.py index 7c37cba6e..5465ba61b 100644 --- a/vmngclient/api/templates/models/cisco_banner_model.py +++ b/vmngclient/api/templates/models/cisco_banner_model.py @@ -11,8 +11,8 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - login_banner: Optional[str] = Field(vmanage_key="login") - motd_banner: Optional[str] = Field(vmanage_key="motd") + login_banner: Optional[str] = Field(alias="login") + motd_banner: Optional[str] = Field(alias="motd") payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_banner" diff --git a/vmngclient/api/templates/models/cisco_bgp_model.py b/vmngclient/api/templates/models/cisco_bgp_model.py index 6dc948da2..556c17f96 100644 --- a/vmngclient/api/templates/models/cisco_bgp_model.py +++ b/vmngclient/api/templates/models/cisco_bgp_model.py @@ -105,10 +105,10 @@ class AddressFamily(BaseModel): ipv6_aggregate_address: Optional[List[Ipv6AggregateAddress]] = Field(alias="ipv6-aggregate-address") network: Optional[List[Network]] ipv6_network: Optional[List[Ipv6Network]] = Field(alias="ipv6-network") - paths: Optional[int] - originate: Optional[bool] - name: Optional[str] - filter: Optional[bool] + paths: Optional[int] = Field(data_path=["maximum-paths"]) + originate: Optional[bool] = Field(data_path=["default-information"]) + policy_name: Optional[str] = Field(data_path=["table-map"], alias="name") + filter: Optional[bool] = Field(data_path=["table-map"]) redistribute: Optional[List[Redistribute]] class Config: @@ -141,10 +141,10 @@ class Config: class NeighborAddressFamily(BaseModel): family_type: NeighborFamilyType = Field(alias="family-type") - prefix_num: Optional[int] = Field(alias="prefix-num") - threshold: Optional[int] - restart: Optional[int] - warning_only: Optional[bool] = Field(alias="warning-only") + prefix_num: Optional[int] = Field(data_path=["maximum-prefixes"], alias="prefix-num") + threshold: Optional[int] = Field(data_path=["maximum-prefixes"]) + restart: Optional[int] = Field(data_path=["maximum-prefixes"]) + warning_only: Optional[bool] = Field(data_path=["maximum-prefixes"], alias="warning-only") route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy") class Config: @@ -156,9 +156,9 @@ class Neighbor(BaseModel): description: Optional[str] shutdown: Optional[bool] remote_as: int = Field(alias="remote-as") - keepalive: Optional[int] - holdtime: Optional[int] - if_name: Optional[str] = Field(alias="if-name") + keepalive: Optional[int] = Field(data_path=["timers"]) + holdtime: Optional[int] = Field(data_path=["timers"]) + if_name: Optional[str] = Field(data_path=["update-source"], alias="if-name") next_hop_self: Optional[bool] = Field(alias="next-hop-self") send_community: Optional[bool] = Field(alias="send-community") send_ext_community: Optional[bool] = Field(alias="send-ext-community") @@ -167,7 +167,7 @@ class Neighbor(BaseModel): send_label: Optional[bool] = Field(alias="send-label") send_label_explicit: Optional[bool] = Field(alias="send-label-explicit") as_override: Optional[bool] = Field(alias="as-override") - as_number: Optional[int] = Field(alias="as-number") + as_number: Optional[int] = Field(data_path=["allowas-in"], alias="as-number") address_family: Optional[List[NeighborAddressFamily]] = Field(alias="address-family") class Config: @@ -193,10 +193,10 @@ class IPv6NeighborFamilyType(str, Enum): class IPv6NeighborAddressFamily(BaseModel): family_type: IPv6NeighborFamilyType = Field(alias="family-type") - prefix_num: Optional[int] = Field(0, alias="prefix-num") - threshold: Optional[int] - restart: Optional[int] - warning_only: Optional[bool] = Field(False, alias="warning-only") + prefix_num: Optional[int] = Field(0, data_path=["maximum-prefixes"], alias="prefix-num") + threshold: Optional[int] = Field(data_path=["maximum-prefixes"]) + restart: Optional[int] = Field(data_path=["maximum-prefixes"]) + warning_only: Optional[bool] = Field(False, data_path=["maximum-prefixes"], alias="warning-only") route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy") class Config: @@ -208,9 +208,9 @@ class Ipv6Neighbor(BaseModel): description: Optional[str] shutdown: Optional[bool] remote_as: int = Field(alias="remote-as") - keepalive: Optional[int] - holdtime: Optional[int] - if_name: Optional[str] = Field(alias="if-name") + keepalive: Optional[int] = Field(data_path=["timers"]) + holdtime: Optional[int] = Field(data_path=["timers"]) + if_name: Optional[str] = Field(data_path=["update-source"], alias="if-name") next_hop_self: Optional[bool] = Field(False, alias="next-hop-self") send_community: Optional[bool] = Field(True, alias="send-community") send_ext_community: Optional[bool] = Field(True, alias="send-ext-community") @@ -219,7 +219,7 @@ class Ipv6Neighbor(BaseModel): send_label: Optional[bool] = Field(False, alias="send-label") send_label_explicit: Optional[bool] = Field(False, alias="send-label-explicit") as_override: Optional[bool] = Field(False, alias="as-override") - as_number: Optional[int] = Field(alias="as-number") + as_number: Optional[int] = Field(data_path=["allowas-in"], alias="as-number") address_family: Optional[List[IPv6NeighborAddressFamily]] = Field(alias="address-family") class Config: @@ -244,27 +244,27 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - as_num: Optional[str] = Field(alias="as-num") - shutdown: Optional[bool] - router_id: Optional[str] = Field(alias="router-id") - propagate_aspath: Optional[bool] = Field(alias="propagate-aspath") - propagate_community: Optional[bool] = Field(alias="propagate-community") - route_target_ipv4: List[RouteTargetIpv4] = Field([], alias="route-target-ipv4") - route_target_ipv6: List[RouteTargetIpv6] = Field([], alias="route-target-ipv6") - mpls_interface: Optional[List[MplsInterface]] = Field(alias="mpls-interface") - external: Optional[int] - internal: Optional[int] - local: Optional[int] - keepalive: Optional[int] - holdtime: Optional[int] - always_compare: Optional[bool] = Field(alias="always-compare") - deterministic: Optional[bool] - missing_as_worst: Optional[bool] = Field(alias="missing-as-worst") - compare_router_id: Optional[bool] = Field(alias="compare-router-id") - multipath_relax: Optional[bool] = Field(alias="multipath-relax") - address_family: Optional[List[AddressFamily]] = Field(alias="address-family") - neighbor: Optional[List[Neighbor]] - ipv6_neighbor: Optional[List[Ipv6Neighbor]] = Field(alias="ipv6-neighbor") + as_num: Optional[str] = Field(data_path=["bgp"], alias="as-num") + shutdown: Optional[bool] = Field(data_path=["bgp"]) + router_id: Optional[str] = Field(data_path=["bgp"], alias="router-id") + propagate_aspath: Optional[bool] = Field(data_path=["bgp"], alias="propagate-aspath") + propagate_community: Optional[bool] = Field(data_path=["bgp"], alias="propagate-community") + route_target_ipv4: List[RouteTargetIpv4] = Field([], data_path=["bgp", "target"], alias="route-target-ipv4") + route_target_ipv6: List[RouteTargetIpv6] = Field([], data_path=["bgp", "target"], alias="route-target-ipv6") + mpls_interface: Optional[List[MplsInterface]] = Field(data_path=["bgp"], alias="mpls-interface") + external: Optional[int] = Field(data_path=["bgp", "distance"]) + internal: Optional[int] = Field(data_path=["bgp", "distance"]) + local: Optional[int] = Field(data_path=["bgp", "distance"]) + keepalive: Optional[int] = Field(data_path=["bgp", "timers"]) + holdtime: Optional[int] = Field(data_path=["bgp", "timers"]) + always_compare: Optional[bool] = Field(data_path=["bgp", "best-path", "med"], alias="always-compare") + deterministic: Optional[bool] = Field(data_path=["bgp", "best-path", "med"]) + missing_as_worst: Optional[bool] = Field(data_path=["bgp", "best-path", "med"], alias="missing-as-worst") + compare_router_id: Optional[bool] = Field(data_path=["bgp", "best-path"], alias="compare-router-id") + multipath_relax: Optional[bool] = Field(data_path=["bgp", "best-path", "as-path"], alias="multipath-relax") + address_family: Optional[List[AddressFamily]] = Field(data_path=["bgp"], alias="address-family") + neighbor: Optional[List[Neighbor]] = Field(data_path=["bgp"]) + ipv6_neighbor: Optional[List[Ipv6Neighbor]] = Field(data_path=["bgp"], alias="ipv6-neighbor") payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_bgp" diff --git a/vmngclient/api/templates/models/cisco_logging_model.py b/vmngclient/api/templates/models/cisco_logging_model.py index b4579f826..8c8c7a24f 100644 --- a/vmngclient/api/templates/models/cisco_logging_model.py +++ b/vmngclient/api/templates/models/cisco_logging_model.py @@ -23,9 +23,9 @@ class AuthType(str, Enum): class TlsProfile(ConvertBoolToStringModel): profile: str - version: Optional[Version] = Version.TLSV11 + version: Optional[Version] = Field(Version.TLSV11, data_path=["tls-version"]) auth_type: AuthType = Field(alias="auth-type") - ciphersuite_list: Optional[List] = Field(alias="ciphersuite-list") + ciphersuite_list: Optional[List] = Field(data_path=["ciphersuite"], alias="ciphersuite-list") class Config: allow_population_by_field_name = True @@ -47,9 +47,9 @@ class Server(ConvertBoolToStringModel): vpn: Optional[int] source_interface: Optional[str] = Field(alias="source-interface") priority: Optional[Priority] = Priority.INFORMATION - enable_tls: Optional[bool] = Field(False, alias="enable-tls") - custom_profile: Optional[bool] = Field(False, alias="custom-profile") - profile: Optional[str] + enable_tls: Optional[bool] = Field(False, data_path=["tls"], alias="enable-tls") + custom_profile: Optional[bool] = Field(False, data_path=["tls", "tls-properties"], alias="custom-profile") + profile: Optional[str] = Field(data_path=["tls", "tls-properties"]) class Config: allow_population_by_field_name = True @@ -60,9 +60,9 @@ class Ipv6Server(ConvertBoolToStringModel): vpn: Optional[int] source_interface: Optional[str] = Field(alias="source-interface") priority: Optional[Priority] = Priority.INFORMATION - enable_tls: Optional[bool] = Field(False, alias="enable-tls") - custom_profile: Optional[bool] = Field(False, alias="custom-profile") - profile: Optional[str] + enable_tls: Optional[bool] = Field(False, data_path=["tls"], alias="enable-tls") + custom_profile: Optional[bool] = Field(False, data_path=["tls", "tls-properties"], alias="custom-profile") + profile: Optional[str] = Field(data_path=["tls", "tls-properties"]) class Config: allow_population_by_field_name = True @@ -73,9 +73,9 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - enable: Optional[bool] = True - size: Optional[int] = DEFAULT_LOGGING_SIZE - rotate: Optional[int] = DEFAULT_LOGGING_ROTATE + enable: Optional[bool] = Field(True, data_path=["disk"]) + size: Optional[int] = Field(DEFAULT_LOGGING_SIZE, data_path=["disk", "file"]) + rotate: Optional[int] = Field(DEFAULT_LOGGING_ROTATE, data_path=["disk", "file"]) tls_profile: List[TlsProfile] = Field(alias="tls-profile") server: Optional[List[Server]] ipv6_server: Optional[List[Ipv6Server]] = Field(alias="ipv6-server") diff --git a/vmngclient/api/templates/models/cisco_ntp_model.py b/vmngclient/api/templates/models/cisco_ntp_model.py index 6e60d7dce..a662909bb 100644 --- a/vmngclient/api/templates/models/cisco_ntp_model.py +++ b/vmngclient/api/templates/models/cisco_ntp_model.py @@ -32,11 +32,11 @@ class Config: allow_population_by_field_name = True server: List[Server] = Field(default=[]) - authentication: List[Authentication] = Field(default=[]) - trusted: List[int] = Field(default=[]) - enable: Optional[bool] = Field(default=False) - stratum: Optional[int] = Field(default=None) - source: Optional[str] = Field(default=None) + authentication: List[Authentication] = Field(default=[], data_path=["keys"]) + trusted: List[int] = Field(default=[], data_path=["keys"]) + enable: Optional[bool] = Field(default=False, data_path=["master"]) + stratum: Optional[int] = Field(default=None, data_path=["master"]) + source: Optional[str] = Field(default=None, data_path=["master"]) payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_ntp" diff --git a/vmngclient/api/templates/models/cisco_ospf.py b/vmngclient/api/templates/models/cisco_ospf.py index 1f70b9500..2a4b09ddc 100644 --- a/vmngclient/api/templates/models/cisco_ospf.py +++ b/vmngclient/api/templates/models/cisco_ospf.py @@ -6,7 +6,7 @@ from pydantic import Field from vmngclient.api.templates.feature_template import FeatureTemplate -from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel +from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel, ConvertIPToStringModel DEFAULT_OSPF_HELLO_INTERVAL = 10 DEFAULT_OSPF_DEAD_INTERVAL = 40 @@ -91,15 +91,17 @@ class Interface(ConvertBoolToStringModel): priority: Optional[int] = DEFAULT_OSPF_INTERFACE_PRIORITY network: Optional[Network] = Network.BROADCAST passive_interface: Optional[bool] = Field(False, alias="passive-interface") - type: Optional[Type] - message_digest_key: Optional[int] = Field(alias="message-digest-key") - md5: Optional[str] + type: Optional[Type] = Field(data_path=["authentication"]) + message_digest_key: Optional[int] = Field( + alias="message-digest-key", data_path=["authentication", "message-digest"] + ) + md5: Optional[str] = Field(data_path=["authentication", "message-digest"]) class Config: allow_population_by_field_name = True -class Range(ConvertBoolToStringModel): +class Range(ConvertBoolToStringModel, ConvertIPToStringModel): address: ipaddress.IPv4Interface cost: Optional[int] no_advertise: Optional[bool] = Field(False, alias="no-advertise") @@ -110,8 +112,8 @@ class Config: class Area(ConvertBoolToStringModel): a_num: int = Field(alias="a-num") - stub_no_summary: Optional[bool] = Field(vmanage_key="no-summary", data_path=["stub", "no_summary"]) - nssa_no_summary: Optional[bool] = Field(vmanage_key="no-summary") + stub: Optional[bool] = Field(alias="no-summary", data_path=["stub"]) + nssa: Optional[bool] = Field(alias="no-summary", data_path=["nssa"]) interface: Optional[List[Interface]] range: Optional[List[Range]] @@ -124,7 +126,7 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - router_id: Optional[ipaddress.IPv4Address] = Field(alias="router-id") + router_id: Optional[str] = Field(alias="router-id") reference_bandwidth: Optional[int] = Field(DEFAULT_OSPF_REFERENCE_BANDWIDTH, alias="reference-bandwidth") rfc1583: Optional[bool] = True originate: Optional[bool] @@ -137,10 +139,10 @@ class Config: delay: Optional[int] = DEFAULT_OSPF_DELAY initial_hold: Optional[int] = Field(DEFAULT_OSPF_INITIAL_HOLD, alias="initial-hold") max_hold: Optional[int] = Field(DEFAULT_OSPF_MAX_HOLD, alias="max-hold") - redistribute: Optional[List[Redistribute]] - router_lsa: Optional[List[RouterLsa]] = Field(alias="router-lsa") - route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy") - area: Optional[List[Area]] + redistribute: Optional[List[Redistribute]] = Field(alias="redistribute", data_path=["ospf"]) + router_lsa: Optional[List[RouterLsa]] = Field(alias="router-lsa", data_path=["ospf", "max-metric"]) + route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy", data_path=["ospf"]) + area: Optional[List[Area]] = Field(alias="area", data_path=["ospf"]) payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_ospf" diff --git a/vmngclient/api/templates/models/cisco_snmp_model.py b/vmngclient/api/templates/models/cisco_snmp_model.py index d69c2f62d..3b34b43b4 100644 --- a/vmngclient/api/templates/models/cisco_snmp_model.py +++ b/vmngclient/api/templates/models/cisco_snmp_model.py @@ -87,7 +87,7 @@ class Config: community: Optional[List[Community]] group: Optional[List[Group]] user: Optional[List[User]] - target: Optional[List[Target]] + target: Optional[List[Target]] = Field(data_path=["trap"]) payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_snmp" diff --git a/vmngclient/api/templates/models/cisco_system.py b/vmngclient/api/templates/models/cisco_system.py index 212bdda82..275a1c31d 100644 --- a/vmngclient/api/templates/models/cisco_system.py +++ b/vmngclient/api/templates/models/cisco_system.py @@ -52,8 +52,9 @@ class Type(str, Enum): class Tracker(BaseModel): name: str endpoint_ip: str = Field(alias="endpoint-ip") - protocol: Protocol - port: int + endpoint_ip_transport_port: str = Field(alias="endpoint-ip", data_path=["endpoint-ip-transport-port"]) + protocol: Protocol = Field(data_path=["endpoint-ip-transport-port"]) + port: int = Field(data_path=["endpoint-ip-transport-port"]) endpoint_dns_name: str = Field(alias="endpoint-dns-name") endpoint_api_url: str = Field(alias="endpoint-api-url") elements: List[str] @@ -123,14 +124,17 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - timezone: Optional[Timezone] + timezone: Optional[Timezone] = Field(data_path=["clock"]) hostname: str = Field(default=DeviceVariable(name="system_host_name"), alias="host-name", validate_default=True) location: Optional[str] - latitude: Optional[float] - longitude: Optional[float] - range: Optional[int] = 100 - enable: Optional[bool] - mobile_number: Optional[List[MobileNumber]] = Field(alias="mobile-number") + latitude: Optional[float] = Field(data_path=["gps-location"]) + longitude: Optional[float] = Field(data_path=["gps-location"]) + range: Optional[int] = Field(100, data_path=["gps-location", "geo-fencing"]) + enable_fencing: Optional[bool] = Field(False, data_path=["gps-location", "geo-fencing"], alias="enable") + mobile_number: Optional[List[MobileNumber]] = Field( + alias="mobile-number", data_path=["gps-location", "geo-fencing", "sms"] + ) + enable_sms: Optional[bool] = Field(False, data_path=["gps-location", "geo-fencing", "sms"], alias="enable") device_groups: Optional[List[str]] = Field(alias="device-groups") controller_group_list: Optional[List[int]] = Field(alias="controller-group-list") system_ip: DeviceVariable = Field(default=DeviceVariable(name="system_system_ip"), alias="system-ip") @@ -147,14 +151,15 @@ class Config: multi_tenant: Optional[bool] = Field(alias="multi-tenant") track_default_gateway: Optional[bool] = Field(True, alias="track-default-gateway") admin_tech_on_failure: Optional[bool] = Field(alias="admin-tech-on-failure") - idle_timeout: Optional[int] = Field(alias="idle-timeout") + enable_tunnel: Optional[bool] = Field(False, alias="enable", data_path=["on-demand"]) + idle_timeout: Optional[int] = Field(alias="idle-timeout", data_path=["on-demand"]) tracker: Optional[List[Tracker]] object_track: Optional[List[ObjectTrack]] = Field(alias="object-track") region_id: Optional[int] = Field(alias="region-id") secondary_region: Optional[int] = Field(alias="secondary-region") role: Optional[Role] - affinity_group_number: Optional[int] = Field(alias="affinity-group-number") - preference: Optional[List[int]] + affinity_group_number: Optional[int] = Field(alias="affinity-group-number", data_path=["affinity-group"]) + preference: Optional[List[int]] = Field(data_path=["affinity-group"]) preference_auto: Optional[bool] = Field(alias="preference-auto") affinity_per_vrf: Optional[List[AffinityPerVrf]] = Field(alias="affinity-per-vrf") transport_gateway: Optional[bool] = Field(alias="transport-gateway") diff --git a/vmngclient/api/templates/models/cisco_vpn_interface_model.py b/vmngclient/api/templates/models/cisco_vpn_interface_model.py index ee2a089fb..74c016ab2 100644 --- a/vmngclient/api/templates/models/cisco_vpn_interface_model.py +++ b/vmngclient/api/templates/models/cisco_vpn_interface_model.py @@ -6,7 +6,7 @@ from pydantic import Field from vmngclient.api.templates.feature_template import FeatureTemplate -from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel +from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel, ConvertIPToStringModel DEFAULT_STATIC_NAT64_SOURCE_VPN_ID = 0 DEFAULT_STATIC_NAT_SOURCE_VPN_ID = 0 @@ -20,11 +20,11 @@ DEFAULT_IPV6_VRRP_TIMER = 1000 -class SecondaryIPv4Address(ConvertBoolToStringModel): +class SecondaryIPv4Address(ConvertBoolToStringModel, ConvertIPToStringModel): address: Optional[ipaddress.IPv4Interface] -class SecondaryIPv6Address(ConvertBoolToStringModel): +class SecondaryIPv6Address(ConvertBoolToStringModel, ConvertIPToStringModel): address: Optional[ipaddress.IPv6Interface] @@ -41,7 +41,7 @@ class Config: allow_population_by_field_name = True -class DhcpHelperV6(ConvertBoolToStringModel): +class DhcpHelperV6(ConvertBoolToStringModel, ConvertIPToStringModel): address: ipaddress.IPv6Address vpn: Optional[int] @@ -52,7 +52,7 @@ class NatChoice(str, Enum): LOOPBACK = "Loopback" -class StaticNat66(ConvertBoolToStringModel): +class StaticNat66(ConvertBoolToStringModel, ConvertIPToStringModel): source_prefix: ipaddress.IPv6Interface = Field(alias="source-prefix") translated_source_prefix: str = Field(alias="translated-source-prefix") source_vpn_id: int = Field(DEFAULT_STATIC_NAT64_SOURCE_VPN_ID, alias="source-vpn-id") @@ -66,7 +66,7 @@ class StaticNatDirection(str, Enum): OUTSIDE = "outside" -class Static(ConvertBoolToStringModel): +class Static(ConvertBoolToStringModel, ConvertIPToStringModel): source_ip: ipaddress.IPv4Address = Field(alias="source-ip") translate_ip: ipaddress.IPv4Address = Field(alias="translate-ip") static_nat_direction: StaticNatDirection = Field(StaticNatDirection.INSIDE, alias="static-nat-direction") @@ -81,7 +81,7 @@ class Proto(str, Enum): UDP = "udp" -class StaticPortForward(ConvertBoolToStringModel): +class StaticPortForward(ConvertBoolToStringModel, ConvertIPToStringModel): source_ip: ipaddress.IPv4Address = Field(alias="source-ip") translate_ip: ipaddress.IPv4Address = Field(alias="translate-ip") static_nat_direction: StaticNatDirection = Field(StaticNatDirection.INSIDE, alias="static-nat-direction") @@ -178,12 +178,12 @@ class Duplex(str, Enum): AUTO = "auto" -class Ip(ConvertBoolToStringModel): +class Ip(ConvertBoolToStringModel, ConvertIPToStringModel): addr: ipaddress.IPv4Address mac: str -class Ipv4Secondary(ConvertBoolToStringModel): +class Ipv4Secondary(ConvertBoolToStringModel, ConvertIPToStringModel): address: ipaddress.IPv4Address @@ -201,13 +201,13 @@ class Config: allow_population_by_field_name = True -class Vrrp(ConvertBoolToStringModel): +class Vrrp(ConvertBoolToStringModel, ConvertIPToStringModel): grp_id: int = Field(alias="grp-id") priority: int = DEFAULT_VRRP_PRIORITY timer: int = DEFAULT_VRRP_TIMER track_omp: bool = Field(False, alias="track-omp") track_prefix_list: Optional[str] = Field(alias="track-prefix-list") - address: Optional[ipaddress.IPv4Address] + address: Optional[ipaddress.IPv4Address] = Field(data_path=["ipv4"], alias="address") ipv4_secondary: Optional[List[Ipv4Secondary]] = Field(alias="ipv4-secondary") tloc_change_pref: bool = Field(False, alias="tloc-change-pref") value: int @@ -217,7 +217,7 @@ class Config: allow_population_by_field_name = True -class Ipv6(ConvertBoolToStringModel): +class Ipv6(ConvertBoolToStringModel, ConvertIPToStringModel): ipv6_link_local: ipaddress.IPv6Address = Field(alias="ipv6-link-local") prefix: Optional[ipaddress.IPv6Interface] @@ -237,7 +237,7 @@ class Config: allow_population_by_field_name = True -class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel): +class CiscoVpnInterfaceModel(FeatureTemplate, ConvertBoolToStringModel, ConvertIPToStringModel): class Config: arbitrary_types_allowed = True allow_population_by_field_name = True @@ -245,16 +245,14 @@ class Config: if_name: str = Field(alias="if-name") interface_description: Optional[str] = Field(vmanage_key="description") poe: Optional[bool] - ipv4_address: Optional[str] = Field(vmanage_key="address") - secondary_ipv4_address: Optional[List[SecondaryIPv4Address]] = Field( - vmanage_key="secondary-address", alias="secondary-address" - ) + ipv4_address: Optional[str] = Field(data_path=["ip"], alias="address") + secondary_ipv4_address: Optional[List[SecondaryIPv4Address]] = Field(data_path=["ip"], alias="secondary-address") dhcp_ipv4_client: Optional[bool] = Field(vmanage_key="dhcp-client", alias="dhcp-client") dhcp_distance: Optional[int] = Field(alias="dhcp-distance") - ipv6_address: Optional[ipaddress.IPv6Interface] = Field(vmanage_key="address") + ipv6_address: Optional[ipaddress.IPv6Interface] = Field(data_path=["ipv6"], alias="address") dhcp_ipv6_client: Optional[bool] = Field(vmanage_key="dhcp-client", alias="dhcp-client") secondary_ipv6_address: Optional[List[SecondaryIPv6Address]] = Field( - vmanage_key="secondary-address", alias="secondary-address" + data_path=["ipv6"], vmanage_key="secondary-address", alias="secondary-address" ) access_list_ipv4: Optional[List[AccessList]] = Field(vmanage_key="access-list", alias="access-list") dhcp_helper: Optional[List[ipaddress.IPv4Address]] = Field(alias="dhcp-helper") @@ -275,7 +273,7 @@ class Config: nat64: Optional[bool] nat66: Optional[bool] static_nat66: Optional[List[StaticNat66]] = Field(alias="static-nat66") - static: Optional[List[Static]] + static: Optional[List[Static]] = Field(data_path=["nat"], alias="static") static_port_forward: Optional[List[StaticPortForward]] = Field(alias="static-port-forward") enable_core_region: Optional[bool] = Field(alias="enable-core-region") core_region: Optional[CoreRegion] = Field(alias="core-region") @@ -305,7 +303,7 @@ class Config: low_bandwidth_link: Optional[bool] = Field(alias="low-bandwidth-link") tunnel_tcp_mss_adjust: Optional[int] = Field(alias="tunnel-tcp-mss-adjust") clear_dont_fragment: Optional[bool] = Field(alias="clear-dont-fragment") - propagate_sgt: Optional[bool] = Field(alias="propagate-sgt") + propagate_sgt: Optional[bool] = Field(data_path=["tunnel-interface"], alias="propagate-sgt") network_broadcast: Optional[bool] = Field(alias="network-broadcast") all: Optional[bool] bgp: Optional[bool] @@ -351,16 +349,16 @@ class Config: bandwidth_downstream: Optional[int] = Field(alias="bandwidth-downstream") block_non_source_ip: Optional[bool] = Field(alias="block-non-source-ip") rule_name: Optional[str] = Field(alias="rule-name") - access_list_ipv6: Optional[List[AccessList]] = Field(vmanage_key="access-list", alias="access-list") - ip: Optional[List[Ip]] - vrrp: Optional[List[Vrrp]] + access_list_ipv6: Optional[List[AccessList]] = Field(data_path=["ipv6"], alias="access-list") + ip: Optional[List[Ip]] = Field(data_path=["arp"]) + vrrp: Optional[List[Vrrp]] = Field(alias="vrrp") ipv6_vrrp: Optional[List[Ipv6Vrrp]] = Field(alias="ipv6-vrrp") - enable_sgt_propagation: Optional[bool] = Field(vmanage_key="sgt", alias="sgt") - sgt: Optional[int] + enable_sgt_propagation: Optional[bool] = Field(data_path=["trustsec", "propagate"], alias="sgt") + security_group_tag: Optional[int] = Field(data_path=["trustsec", "static"], alias="sgt") trusted: Optional[bool] enable_sgt_authorization_and_forwarding: Optional[bool] = Field(vmanage_key="enable", alias="enable") enable_sgt_enforcement: Optional[bool] = Field(vmanage_key="enable", alias="enable") - enforcement_sgt: Optional[int] = Field(vmanage_key="sgt") + enforcement_sgt: Optional[int] = Field(data_path=["trustsec", "enforcement"], alias="sgt") payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_vpn_interface" diff --git a/vmngclient/api/templates/models/cisco_vpn_model.py b/vmngclient/api/templates/models/cisco_vpn_model.py index 88fae6d02..3644ba178 100644 --- a/vmngclient/api/templates/models/cisco_vpn_model.py +++ b/vmngclient/api/templates/models/cisco_vpn_model.py @@ -392,28 +392,28 @@ class Config: allow_population_by_field_name = True vpn_id: int = Field(alias="vpn-id", default=0) - vpn_name: Optional[str] = Field(vmanage_key="name") + vpn_name: Optional[str] = Field(alias="name") tenant_vpn_id: Optional[int] = Field(alias="tenant-vpn-id") org_name: Optional[str] = Field(alias="org-name") omp_admin_distance_ipv4: Optional[int] = Field(alias="omp-admin-distance-ipv4") omp_admin_distance_ipv6: Optional[int] = Field(alias="omp-admin-distance-ipv6") dns: Optional[List[Dns]] dns_ipv6: Optional[List[DnsIpv6]] = Field(alias="dns-ipv6") - layer4: Optional[bool] + layer4: Optional[bool] = Field(data_path=["ecmp-hash-key"]) host: Optional[List[Host]] service: Optional[List[Service]] - service_route: Optional[List[ServiceRoute]] = Field(alias="service-route") - route_v4: Optional[List[Routev4]] = Field(vmanage_key="route", data_path=["ip", "route"]) - route_v6: Optional[List[Routev6]] = Field(vmanage_key="route", data_path=["ipv6", "route"]) - gre_route: Optional[List[GreRoute]] = Field(alias="gre-route") - ipsec_route: Optional[List[IpsecRoute]] = Field(alias="ipsec-route") - advertise: Optional[List[Advertise]] - ipv6_advertise: Optional[List[Ipv6Advertise]] = Field(alias="ipv6-advertise") - pool: Optional[List[Pool]] - natpool: Optional[List[Natpool]] - static: Optional[List[Static]] - subnet_static: Optional[List[SubnetStatic]] = Field(alias="subnet-static") - port_forward: Optional[List[PortForward]] = Field(alias="port-forward") + service_route: Optional[List[ServiceRoute]] = Field(data_path=["ip"], alias="service-route") + route_v4: Optional[List[Routev4]] = Field(data_path=["ip"], alias="route") + route_v6: Optional[List[Routev6]] = Field(data_path=["ipv6"], alias="route") + gre_route: Optional[List[GreRoute]] = Field(data_path=["ip"], alias="gre-route") + ipsec_route: Optional[List[IpsecRoute]] = Field(data_path=["ip"], alias="ipsec-route") + advertise: Optional[List[Advertise]] = Field(data_path=["omp"]) + ipv6_advertise: Optional[List[Ipv6Advertise]] = Field(data_path=["omp"], alias="ipv6-advertise") + pool: Optional[List[Pool]] = Field(data_path=["nat64", "v4"]) + natpool: Optional[List[Natpool]] = Field(data_path=["nat"]) + static: Optional[List[Static]] = Field(data_path=["nat"]) + subnet_static: Optional[List[SubnetStatic]] = Field(data_path=["nat"], alias="subnet-static") + port_forward: Optional[List[PortForward]] = Field(data_path=["nat"], alias="port-forward") route_import: Optional[List[RouteImport]] = Field(alias="route-import") route_import_from: Optional[List[RouteImportFrom]] = Field(alias="route-import-from") route_export: Optional[List[RouteExport]] = Field(alias="route-export") diff --git a/vmngclient/tests/templates/definitions/basic/children.json b/vmngclient/tests/templates/definitions/basic/children.json index 14495a95e..053f1f4a8 100644 --- a/vmngclient/tests/templates/definitions/basic/children.json +++ b/vmngclient/tests/templates/definitions/basic/children.json @@ -1,35 +1,39 @@ { "user": { - "vipObjectType": "tree", - "vipType": "constant", - "vipValue": [ - { - "name": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "user1" - }, - "password": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "pass" - } - }, - { - "name": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "user2" - }, - "password": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "pass" - } + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "user1" + }, + "list": { + "password": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "pass" } - ], - "vipPrimaryKey": [ - "name" - ] + } + }, + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "user2" + }, + "list": { + "password": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "pass" + } + } + } + ], + "vipPrimaryKey": [ + "name" + ] } -} \ No newline at end of file + } diff --git a/vmngclient/tests/templates/definitions/basic/children_nested.json b/vmngclient/tests/templates/definitions/basic/children_nested.json index c4f9782e9..40792c51a 100644 --- a/vmngclient/tests/templates/definitions/basic/children_nested.json +++ b/vmngclient/tests/templates/definitions/basic/children_nested.json @@ -1,21 +1,30 @@ { "user": { "vipObjectType": "tree", + "vipPrimaryKey": [ + "name" + ], "vipType": "constant", "vipValue": [ { + "list": { + "password": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "pass" + } + }, "name": { "vipObjectType": "object", "vipType": "constant", "vipValue": "user1" }, - "password": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "pass" - }, "pubkey-chain": { "vipObjectType": "tree", + "vipPrimaryKey": [ + "key-string" + ], + "vipType": "constant", "vipValue": [ { "key-string": { @@ -23,42 +32,42 @@ "vipType": "constant", "vipValue": "*****" }, - "key-type": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "RSA" + "type": { + "RSA": { + "key-type": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "RSA" + } + } } } - ], - "vipType": "constant", - "vipPrimaryKey": [ - "key-string" ] } }, { + "list": { + "password": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "pass" + } + }, "name": { "vipObjectType": "object", "vipType": "constant", "vipValue": "user2" }, - "password": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "pass" - }, "pubkey-chain": { "vipObjectType": "tree", - "vipValue": [], - "vipType": "ignore", "vipPrimaryKey": [ "key-string" + ], + "vipType": "ignore", + "vipValue": [ ] } } - ], - "vipPrimaryKey": [ - "name" ] } -} \ No newline at end of file +} diff --git a/vmngclient/tests/templates/schemas/basic/children.json b/vmngclient/tests/templates/schemas/basic/children.json index 750ebf208..d34d61f3e 100644 --- a/vmngclient/tests/templates/schemas/basic/children.json +++ b/vmngclient/tests/templates/schemas/basic/children.json @@ -50,7 +50,7 @@ "type": "passphrase", "maxLength": 32 }, - "dataPath": [], + "dataPath": ["list"], "objectType": "object" } ] diff --git a/vmngclient/tests/templates/schemas/basic/children_nested.json b/vmngclient/tests/templates/schemas/basic/children_nested.json index 599e9ee7e..a4476f234 100644 --- a/vmngclient/tests/templates/schemas/basic/children_nested.json +++ b/vmngclient/tests/templates/schemas/basic/children_nested.json @@ -50,7 +50,7 @@ "type": "passphrase", "maxLength": 32 }, - "dataPath": [], + "dataPath": ["list"], "objectType": "object" }, { @@ -101,7 +101,7 @@ "minLength": 1, "maxLength": 32 }, - "dataPath": [], + "dataPath": ["type", "RSA"], "objectType": "object" } ] diff --git a/vmngclient/tests/templates/test_generate_payload.py b/vmngclient/tests/templates/test_generate_payload.py index 877ca3fff..a015d37ad 100644 --- a/vmngclient/tests/templates/test_generate_payload.py +++ b/vmngclient/tests/templates/test_generate_payload.py @@ -45,7 +45,7 @@ class Config: allow_population_by_field_name = True key: str = Field(alias="key-string") - key_type: str = Field(alias="key-type") + key_type: str = Field(alias="key-type", data_path=["type", "RSA"]) class User(BaseModel): @@ -54,7 +54,7 @@ class Config: allow_population_by_field_name = True name: str - password: str + password: str = Field(data_path=["list"]) pubkey_chain: List[RSA] = Field(default=[], alias="pubkey-chain") diff --git a/vmngclient/utils/pydantic_validators.py b/vmngclient/utils/pydantic_validators.py index 8a24fdbc4..c7893803d 100644 --- a/vmngclient/utils/pydantic_validators.py +++ b/vmngclient/utils/pydantic_validators.py @@ -1,3 +1,6 @@ +import ipaddress +from typing import Any + from pydantic import BaseModel, root_validator @@ -8,3 +11,20 @@ def convert_bool_to_string_validator(cls, values): if isinstance(value, bool): values[key] = str(value).lower() return values + + +class ConvertIPToStringModel(BaseModel): + @root_validator # type: ignore + def convert_ip_to_string_validator(cls, values): + for key, value in values.items(): + values[key] = convert_ip_to_string(value) + return values + + +def convert_ip_to_string(values: Any): + if isinstance(values, list): + for index, ip in enumerate(values): + values[index] = convert_ip_to_string(ip) + if isinstance(values, ipaddress._BaseAddress): + values = str(values) + return values From c0e84e9158d222547a2fac40b019912272a27ed2 Mon Sep 17 00:00:00 2001 From: Mateusz Lapinski Date: Tue, 19 Sep 2023 02:00:22 -0700 Subject: [PATCH 3/5] lowercase protocol values --- vmngclient/api/templates/models/security_vsmart_model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vmngclient/api/templates/models/security_vsmart_model.py b/vmngclient/api/templates/models/security_vsmart_model.py index e99a35518..5c77a7197 100644 --- a/vmngclient/api/templates/models/security_vsmart_model.py +++ b/vmngclient/api/templates/models/security_vsmart_model.py @@ -7,9 +7,9 @@ from vmngclient.api.templates.feature_template import FeatureTemplate -class Protocol(Enum): - DTLS: str = "DTLS" - TLS: str = "TLS" +class Protocol(str, Enum): + DTLS: str = "dtls" + TLS: str = "tls" class SecurityvSmart(FeatureTemplate): @@ -17,7 +17,7 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - protocol: Optional[Protocol] = Field(default=None, converter=Protocol) + protocol: Optional[Protocol] = Field(default=None) tls_port: Optional[int] = Field(default=None, alias="tls-port") payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "security-vsmart" From 742f220a28b842155512ac75664770da3c996ec1 Mon Sep 17 00:00:00 2001 From: Mateusz Lapinski Date: Tue, 19 Sep 2023 06:35:30 -0700 Subject: [PATCH 4/5] refactor + unittests --- vmngclient/api/template_api.py | 11 +- .../api/templates/feature_template_field.py | 5 +- .../api/templates/models/cisco_bfd_model.py | 4 +- .../api/templates/models/cisco_bgp_model.py | 2 +- .../api/templates/models/cisco_ntp_model.py | 5 +- .../api/templates/models/cisco_omp_model.py | 12 +- vmngclient/api/templates/models/cisco_ospf.py | 32 +- .../api/templates/models/cisco_snmp_model.py | 5 +- .../api/templates/models/cisco_system.py | 6 +- .../models/cisco_vpn_interface_model.py | 100 +- .../api/templates/models/omp_vsmart_model.py | 11 +- .../templates/models/security_vsmart_model.py | 4 +- .../definitions/Basic_Cisco_VPN_Model.json | 127 +- .../definitions/complex_cisco_vpn.json | 1071 +++++++ vmngclient/tests/templates/models/__init__.py | 4 +- .../tests/templates/models/cisco_aaa.py | 50 + .../tests/templates/models/cisco_vpn.py | 269 +- .../tests/templates/schemas/cisco_vpn.json | 2452 +++++++++++++++++ .../tests/templates/schemas/omp-vsmart.json | 202 ++ .../tests/templates/test_deserialize_model.py | 2 + 20 files changed, 4146 insertions(+), 228 deletions(-) create mode 100644 vmngclient/tests/templates/definitions/complex_cisco_vpn.json create mode 100644 vmngclient/tests/templates/schemas/cisco_vpn.json create mode 100644 vmngclient/tests/templates/schemas/omp-vsmart.json diff --git a/vmngclient/api/template_api.py b/vmngclient/api/template_api.py index 3191417a2..adae77ec1 100644 --- a/vmngclient/api/template_api.py +++ b/vmngclient/api/template_api.py @@ -567,16 +567,7 @@ def generate_feature_template_payload( value = getattr(template, field_name) break if value is None: - value = template.dict(by_alias=True).get(field.key, None) - - # TODO remove workaround, add specific object - # types like Ignore, Constant, None etc so generator will now - # which object to ommit while generating payload - if template.type == "cisco_vpn_interface" and value is None: - continue - - if isinstance(value, bool): - value = str(value).lower() # type: ignore + continue # Merge dictionaries diff --git a/vmngclient/api/templates/feature_template_field.py b/vmngclient/api/templates/feature_template_field.py index 375e775a1..5c285e6b1 100644 --- a/vmngclient/api/templates/feature_template_field.py +++ b/vmngclient/api/templates/feature_template_field.py @@ -131,8 +131,9 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic output["vipValue"] = value else: if "default" in self.dataType: - output["vipValue"] = self.dataType["default"] if value is None else value - output["vipType"] = self.defaultOption.value + return {} + # output["vipValue"] = self.dataType["default"] if value is None else value + # output["vipType"] = self.defaultOption.value else: output["vipValue"] = [] output["vipType"] = FeatureTemplateOptionType.IGNORE.value diff --git a/vmngclient/api/templates/models/cisco_bfd_model.py b/vmngclient/api/templates/models/cisco_bfd_model.py index 3e83ced59..a694427fb 100644 --- a/vmngclient/api/templates/models/cisco_bfd_model.py +++ b/vmngclient/api/templates/models/cisco_bfd_model.py @@ -55,8 +55,8 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - multiplier: Optional[int] = DEFAULT_BFD_MULTIPLIER - poll_interval: Optional[int] = Field(DEFAULT_BFD_POLL_INTERVAL, alias="poll-interval") + multiplier: Optional[int] = Field(DEFAULT_BFD_MULTIPLIER, data_path=["app-route"]) + poll_interval: Optional[int] = Field(DEFAULT_BFD_POLL_INTERVAL, alias="poll-interval", data_path=["app-route"]) default_dscp: Optional[int] = Field(DEFAULT_BFD_DSCP, alias="default-dscp") color: Optional[List[Color]] diff --git a/vmngclient/api/templates/models/cisco_bgp_model.py b/vmngclient/api/templates/models/cisco_bgp_model.py index 556c17f96..0c758caa1 100644 --- a/vmngclient/api/templates/models/cisco_bgp_model.py +++ b/vmngclient/api/templates/models/cisco_bgp_model.py @@ -269,7 +269,7 @@ class Config: payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "cisco_bgp" - @validator("shutdown") + @validator("shutdown", "deterministic", "missing_as_worst", "compare_router_id", "multipath_relax") def cast_to_str(cls, value): if value is not None: return str(value).lower() diff --git a/vmngclient/api/templates/models/cisco_ntp_model.py b/vmngclient/api/templates/models/cisco_ntp_model.py index a662909bb..3ff856610 100644 --- a/vmngclient/api/templates/models/cisco_ntp_model.py +++ b/vmngclient/api/templates/models/cisco_ntp_model.py @@ -4,9 +4,10 @@ from pydantic import BaseModel, Field from vmngclient.api.templates.feature_template import FeatureTemplate +from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel -class Server(BaseModel): +class Server(ConvertBoolToStringModel): class Config: allow_population_by_field_name = True @@ -26,7 +27,7 @@ class Config: md5: str -class CiscoNTPModel(FeatureTemplate): +class CiscoNTPModel(FeatureTemplate, ConvertBoolToStringModel): class Config: arbitrary_types_allowed = True allow_population_by_field_name = True diff --git a/vmngclient/api/templates/models/cisco_omp_model.py b/vmngclient/api/templates/models/cisco_omp_model.py index 1cf80bbb1..39196a889 100644 --- a/vmngclient/api/templates/models/cisco_omp_model.py +++ b/vmngclient/api/templates/models/cisco_omp_model.py @@ -76,10 +76,14 @@ class Config: shutdown: Optional[bool] omp_admin_distance_ipv4: Optional[int] = Field(alias="omp-admin-distance-ipv4") omp_admin_distance_ipv6: Optional[int] = Field(alias="omp-admin-distance-ipv6") - advertisement_interval: Optional[int] = Field(DEFAULT_OMP_ADVERTISEMENT_INTERVAL, alias="advertisement-interval") - graceful_restart_timer: Optional[int] = Field(DEFAULT_OMP_GRACEFUL_RESTART_TIMER, alias="graceful-restart-timer") - eor_timer: Optional[int] = Field(DEFAULT_OMP_EOR_TIMER, alias="eor-timer") - holdtime: Optional[int] = DEFAULT_OMP_HOLDTIME + advertisement_interval: Optional[int] = Field( + DEFAULT_OMP_ADVERTISEMENT_INTERVAL, alias="advertisement-interval", data_path=["timers"] + ) + graceful_restart_timer: Optional[int] = Field( + DEFAULT_OMP_GRACEFUL_RESTART_TIMER, alias="graceful-restart-timer", data_path=["timers"] + ) + eor_timer: Optional[int] = Field(DEFAULT_OMP_EOR_TIMER, alias="eor-timer", data_path=["timers"]) + holdtime: Optional[int] = Field(DEFAULT_OMP_HOLDTIME, data_path=["timers"]) advertise: Optional[List[IPv4Advertise]] ipv6_advertise: Optional[List[IPv6Advertise]] = Field(alias="ipv6-advertise") ignore_region_path_length: Optional[bool] = Field(False, alias="ignore-region-path-length") diff --git a/vmngclient/api/templates/models/cisco_ospf.py b/vmngclient/api/templates/models/cisco_ospf.py index 2a4b09ddc..b55453a05 100644 --- a/vmngclient/api/templates/models/cisco_ospf.py +++ b/vmngclient/api/templates/models/cisco_ospf.py @@ -126,19 +126,25 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - router_id: Optional[str] = Field(alias="router-id") - reference_bandwidth: Optional[int] = Field(DEFAULT_OSPF_REFERENCE_BANDWIDTH, alias="reference-bandwidth") - rfc1583: Optional[bool] = True - originate: Optional[bool] - always: Optional[bool] - metric: Optional[int] - metric_type: Optional[MetricType] = Field(alias="metric-type") - external: Optional[int] = DEFAULT_OSPF_EXTERNAL - inter_area: Optional[int] = Field(DEFAULT_OSPF_INTER_AREA, alias="inter-area") - intra_area: Optional[int] = Field(DEFAULT_OSPF_INTRA_AREA, alias="intra-area") - delay: Optional[int] = DEFAULT_OSPF_DELAY - initial_hold: Optional[int] = Field(DEFAULT_OSPF_INITIAL_HOLD, alias="initial-hold") - max_hold: Optional[int] = Field(DEFAULT_OSPF_MAX_HOLD, alias="max-hold") + router_id: Optional[str] = Field(alias="router-id", data_path=["ospf"]) + reference_bandwidth: Optional[int] = Field( + DEFAULT_OSPF_REFERENCE_BANDWIDTH, data_path=["ospf", "auto-cost"], alias="reference-bandwidth" + ) + rfc1583: Optional[bool] = Field(True, data_path=["ospf", "compatible"]) + originate: Optional[bool] = Field(data_path=["ospf", "default-information"]) + always: Optional[bool] = Field(data_path=["ospf", "default-information", "originate"]) + metric: Optional[int] = Field(data_path=["ospf", "default-information", "originate"]) + metric_type: Optional[MetricType] = Field( + alias="metric-type", data_path=["ospf", "default-information", "originate"] + ) + external: Optional[int] = Field(DEFAULT_OSPF_EXTERNAL, data_path=["ospf", "distance"]) + inter_area: Optional[int] = Field(DEFAULT_OSPF_INTER_AREA, data_path=["ospf", "distance"], alias="inter-area") + intra_area: Optional[int] = Field(DEFAULT_OSPF_INTRA_AREA, data_path=["ospf", "distance"], alias="intra-area") + delay: Optional[int] = Field(DEFAULT_OSPF_DELAY, data_path=["ospf", "timers", "spf"]) + initial_hold: Optional[int] = Field( + DEFAULT_OSPF_INITIAL_HOLD, alias="initial-hold", data_path=["ospf", "timers", "spf"] + ) + max_hold: Optional[int] = Field(DEFAULT_OSPF_MAX_HOLD, alias="max-hold", data_path=["ospf", "timers", "spf"]) redistribute: Optional[List[Redistribute]] = Field(alias="redistribute", data_path=["ospf"]) router_lsa: Optional[List[RouterLsa]] = Field(alias="router-lsa", data_path=["ospf", "max-metric"]) route_policy: Optional[List[RoutePolicy]] = Field(alias="route-policy", data_path=["ospf"]) diff --git a/vmngclient/api/templates/models/cisco_snmp_model.py b/vmngclient/api/templates/models/cisco_snmp_model.py index 3b34b43b4..d1b82bdcc 100644 --- a/vmngclient/api/templates/models/cisco_snmp_model.py +++ b/vmngclient/api/templates/models/cisco_snmp_model.py @@ -5,9 +5,10 @@ from pydantic import BaseModel, Field from vmngclient.api.templates.feature_template import FeatureTemplate +from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel -class Oid(BaseModel): +class Oid(ConvertBoolToStringModel): id: str exclude: Optional[bool] @@ -75,7 +76,7 @@ class Config: allow_population_by_field_name = True -class CiscoSNMPModel(FeatureTemplate): +class CiscoSNMPModel(FeatureTemplate, ConvertBoolToStringModel): class Config: arbitrary_types_allowed = True allow_population_by_field_name = True diff --git a/vmngclient/api/templates/models/cisco_system.py b/vmngclient/api/templates/models/cisco_system.py index 275a1c31d..86366ece9 100644 --- a/vmngclient/api/templates/models/cisco_system.py +++ b/vmngclient/api/templates/models/cisco_system.py @@ -6,6 +6,7 @@ from vmngclient.api.templates.device_variable import DeviceVariable from vmngclient.api.templates.feature_template import FeatureTemplate +from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel from vmngclient.utils.timezone import Timezone @@ -119,7 +120,7 @@ class Epfr(str, Enum): CONSERVATIVE = "conservative" -class CiscoSystemModel(FeatureTemplate): +class CiscoSystemModel(FeatureTemplate, ConvertBoolToStringModel): class Config: arbitrary_types_allowed = True allow_population_by_field_name = True @@ -152,7 +153,8 @@ class Config: track_default_gateway: Optional[bool] = Field(True, alias="track-default-gateway") admin_tech_on_failure: Optional[bool] = Field(alias="admin-tech-on-failure") enable_tunnel: Optional[bool] = Field(False, alias="enable", data_path=["on-demand"]) - idle_timeout: Optional[int] = Field(alias="idle-timeout", data_path=["on-demand"]) + idle_timeout: Optional[int] = Field(alias="idle-timeout") + on_demand_idle_timeout_min: Optional[int] = Field(alias="idle-timeout", data_path=["on-demand"]) tracker: Optional[List[Tracker]] object_track: Optional[List[ObjectTrack]] = Field(alias="object-track") region_id: Optional[int] = Field(alias="region-id") diff --git a/vmngclient/api/templates/models/cisco_vpn_interface_model.py b/vmngclient/api/templates/models/cisco_vpn_interface_model.py index 74c016ab2..23fdd98a7 100644 --- a/vmngclient/api/templates/models/cisco_vpn_interface_model.py +++ b/vmngclient/api/templates/models/cisco_vpn_interface_model.py @@ -243,7 +243,7 @@ class Config: allow_population_by_field_name = True if_name: str = Field(alias="if-name") - interface_description: Optional[str] = Field(vmanage_key="description") + interface_description: Optional[str] = Field(alias="description") poe: Optional[bool] ipv4_address: Optional[str] = Field(data_path=["ip"], alias="address") secondary_ipv4_address: Optional[List[SecondaryIPv4Address]] = Field(data_path=["ip"], alias="secondary-address") @@ -279,52 +279,58 @@ class Config: core_region: Optional[CoreRegion] = Field(alias="core-region") secondary_region: Optional[SecondaryRegion] = Field(alias="secondary-region") tloc_encapsulation: Optional[List[Encapsulation]] - border: Optional[bool] + border: Optional[bool] = Field(data_path=["tunnel-interface"]) per_tunnel_qos: Optional[bool] = Field(alias="per-tunnel-qos") per_tunnel_qos_aggregator: Optional[bool] = Field(alias="per-tunnel-qos-aggregator") mode: Optional[Mode] tunnels_bandwidth: Optional[int] = Field(alias="tunnels-bandwidth") - group: Optional[List[int]] - value: Optional[Value] - max_control_connections: Optional[int] = Field(alias="max-control-connections") - control_connections: Optional[bool] = Field(alias="control-connections") - vbond_as_stun_server: Optional[bool] = Field(alias="vbond-as-stun-server") - exclude_controller_group_list: Optional[List[int]] = Field(alias="exclude-controller-group-list") - vmanage_connection_preference: Optional[int] = Field(alias="vmanage-connection-preference") - port_hop: Optional[bool] = Field(alias="port-hop") - restrict: Optional[bool] - dst_ip: Optional[ipaddress.IPv4Address] = Field(alias="dst-ip") - carrier: Optional[Carrier] - nat_refresh_interval: Optional[int] = Field(alias="nat-refresh-interval") - hello_interval: Optional[int] = Field(alias="hello-interval") - hello_tolerance: Optional[int] = Field(alias="hello-tolerance") - bind: Optional[str] - last_resort_circuit: Optional[bool] = Field(alias="last-resort-circuit") - low_bandwidth_link: Optional[bool] = Field(alias="low-bandwidth-link") - tunnel_tcp_mss_adjust: Optional[int] = Field(alias="tunnel-tcp-mss-adjust") - clear_dont_fragment: Optional[bool] = Field(alias="clear-dont-fragment") + group: Optional[List[int]] = Field(data_path=["tunnel-interface"]) + value: Optional[Value] = Field(data_path=["tunnel-interface", "color"]) + max_control_connections: Optional[int] = Field(alias="max-control-connections", data_path=["tunnel-interface"]) + control_connections: Optional[bool] = Field(alias="control-connections", data_path=["tunnel-interface"]) + vbond_as_stun_server: Optional[bool] = Field(alias="vbond-as-stun-server", data_path=["tunnel-interface"]) + exclude_controller_group_list: Optional[List[int]] = Field( + alias="exclude-controller-group-list", data_path=["tunnel-interface"] + ) + vmanage_connection_preference: Optional[int] = Field( + alias="vmanage-connection-preference", data_path=["tunnel-interface"] + ) + port_hop: Optional[bool] = Field(alias="port-hop", data_path=["tunnel-interface"]) + restrict: Optional[bool] = Field(data_path=["tunnel-interface", "color"]) + dst_ip: Optional[ipaddress.IPv4Address] = Field( + alias="dst-ip", data_path=["tunnel-interface", "tloc-extension-gre-to"] + ) + carrier: Optional[Carrier] = Field(data_path=["tunnel-interface"]) + nat_refresh_interval: Optional[int] = Field(alias="nat-refresh-interval", data_path=["tunnel-interface"]) + hello_interval: Optional[int] = Field(alias="hello-interval", data_path=["tunnel-interface"]) + hello_tolerance: Optional[int] = Field(alias="hello-tolerance", data_path=["tunnel-interface"]) + bind: Optional[str] = Field(data_path=["tunnel-interface"]) + last_resort_circuit: Optional[bool] = Field(alias="last-resort-circuit", data_path=["tunnel-interface"]) + low_bandwidth_link: Optional[bool] = Field(alias="low-bandwidth-link", data_path=["tunnel-interface"]) + tunnel_tcp_mss_adjust: Optional[int] = Field(alias="tunnel-tcp-mss-adjust", data_path=["tunnel-interface"]) + clear_dont_fragment: Optional[bool] = Field(alias="clear-dont-fragment", data_path=["tunnel-interface"]) propagate_sgt: Optional[bool] = Field(data_path=["tunnel-interface"], alias="propagate-sgt") - network_broadcast: Optional[bool] = Field(alias="network-broadcast") - all: Optional[bool] - bgp: Optional[bool] - dhcp: Optional[bool] - dns: Optional[bool] - icmp: Optional[bool] - sshd: Optional[bool] - netconf: Optional[bool] - ntp: Optional[bool] - ospf: Optional[bool] - stun: Optional[bool] - snmp: Optional[bool] - https: Optional[bool] + network_broadcast: Optional[bool] = Field(alias="network-broadcast", data_path=["tunnel-interface"]) + all: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + bgp: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + dhcp: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + dns: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + icmp: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + sshd: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + netconf: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + ntp: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + ospf: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + stun: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + snmp: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) + https: Optional[bool] = Field(data_path=["tunnel-interface", "allow-service"]) media_type: Optional[MediaType] = Field(alias="media-type") intrf_mtu: Optional[int] = Field(alias="intrf-mtu") mtu: Optional[int] tcp_mss_adjust: Optional[int] = Field(alias="tcp-mss-adjust") tloc_extension: Optional[str] = Field(alias="tloc-extension") load_interval: Optional[int] = Field(alias="load-interval") - src_ip: Optional[ipaddress.IPv4Address] = Field(alias="src-ip") - xconnect: Optional[str] + src_ip: Optional[ipaddress.IPv4Address] = Field(alias="src-ip", data_path=["tloc-extension-gre-from"]) + xconnect: Optional[str] = Field(data_path=["tloc-extension-gre-from"]) mac_address: Optional[str] = Field(alias="mac-address") speed: Optional[Speed] duplex: Optional[Duplex] @@ -334,13 +340,13 @@ class Config: ip_directed_broadcast: Optional[bool] = Field(alias="ip-directed-broadcast") icmp_redirect_disable: Optional[bool] = Field(alias="icmp-redirect-disable") qos_adaptive: Optional[bool] = Field(alias="qos-adaptive") - period: Optional[int] - bandwidth_down: Optional[int] = Field(alias="bandwidth-down") - dmin: Optional[int] - dmax: Optional[int] - bandwidth_up: Optional[int] = Field(alias="bandwidth-up") - umin: Optional[int] - umax: Optional[int] + period: Optional[int] = Field(data_path=["qos-adaptive"]) + bandwidth_down: Optional[int] = Field(alias="bandwidth-down", data_path=["qos-adaptive", "downstream"]) + dmin: Optional[int] = Field(data_path=["qos-adaptive", "downstream", "range"]) + dmax: Optional[int] = Field(data_path=["qos-adaptive", "downstream", "range"]) + bandwidth_up: Optional[int] = Field(alias="bandwidth-up", data_path=["qos-adaptive", "upstream"]) + umin: Optional[int] = Field(data_path=["qos-adaptive", "upstream", "range"]) + umax: Optional[int] = Field(data_path=["qos-adaptive", "upstream", "range"]) shaping_rate: Optional[int] = Field(alias="shaping-rate") qos_map: Optional[str] = Field(alias="qos-map") qos_map_vpn: Optional[str] = Field(alias="qos-map-vpn") @@ -348,16 +354,16 @@ class Config: bandwidth_upstream: Optional[int] = Field(alias="bandwidth-upstream") bandwidth_downstream: Optional[int] = Field(alias="bandwidth-downstream") block_non_source_ip: Optional[bool] = Field(alias="block-non-source-ip") - rule_name: Optional[str] = Field(alias="rule-name") + rule_name: Optional[str] = Field(alias="rule-name", data_path=["rewrite-rule"]) access_list_ipv6: Optional[List[AccessList]] = Field(data_path=["ipv6"], alias="access-list") ip: Optional[List[Ip]] = Field(data_path=["arp"]) vrrp: Optional[List[Vrrp]] = Field(alias="vrrp") ipv6_vrrp: Optional[List[Ipv6Vrrp]] = Field(alias="ipv6-vrrp") enable_sgt_propagation: Optional[bool] = Field(data_path=["trustsec", "propagate"], alias="sgt") security_group_tag: Optional[int] = Field(data_path=["trustsec", "static"], alias="sgt") - trusted: Optional[bool] - enable_sgt_authorization_and_forwarding: Optional[bool] = Field(vmanage_key="enable", alias="enable") - enable_sgt_enforcement: Optional[bool] = Field(vmanage_key="enable", alias="enable") + trusted: Optional[bool] = Field(data_path=["trustsec", "static"]) + enable_sgt_authorization_and_forwarding: Optional[bool] = Field(data_path=["trustsec"], alias="enable") + enable_sgt_enforcement: Optional[bool] = Field(data_path=["trustsec", "enforcement"], alias="enable") enforcement_sgt: Optional[int] = Field(data_path=["trustsec", "enforcement"], alias="sgt") payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" diff --git a/vmngclient/api/templates/models/omp_vsmart_model.py b/vmngclient/api/templates/models/omp_vsmart_model.py index 880142549..2eccfbe0d 100644 --- a/vmngclient/api/templates/models/omp_vsmart_model.py +++ b/vmngclient/api/templates/models/omp_vsmart_model.py @@ -4,9 +4,10 @@ from pydantic import Field from vmngclient.api.templates.feature_template import FeatureTemplate +from vmngclient.utils.pydantic_validators import ConvertBoolToStringModel -class OMPvSmart(FeatureTemplate): +class OMPvSmart(FeatureTemplate, ConvertBoolToStringModel): class Config: arbitrary_types_allowed = True allow_population_by_field_name = True @@ -16,11 +17,11 @@ class Config: send_backup_paths: Optional[bool] = Field(default=None, alias="send-backup-paths") discard_rejected: Optional[bool] = Field(default=None, alias="discard-rejected") shutdown: Optional[bool] = Field(default=None, alias="shutdown") - graceful_restart_timer: Optional[int] = Field(default=None, alias="graceful-restart-timer") - eor_timer: Optional[int] = Field(default=None, alias="eor-timer") - holdtime: Optional[int] = Field(default=None, alias="holdtime") + graceful_restart_timer: Optional[int] = Field(default=None, alias="graceful-restart-timer", data_path=["timers"]) + eor_timer: Optional[int] = Field(default=None, alias="eor-timer", data_path=["timers"]) + holdtime: Optional[int] = Field(default=None, alias="holdtime", data_path=["timers"]) affinity_group_preference: Optional[bool] = Field(default=None, alias="affinity-group-preference") - advertisement_interval: Optional[int] = Field(default=None, alias="advertisement-interval") + advertisement_interval: Optional[int] = Field(default=None, alias="advertisement-interval", data_path=["timers"]) payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "omp-vsmart" diff --git a/vmngclient/api/templates/models/security_vsmart_model.py b/vmngclient/api/templates/models/security_vsmart_model.py index 5c77a7197..2b1e2e8df 100644 --- a/vmngclient/api/templates/models/security_vsmart_model.py +++ b/vmngclient/api/templates/models/security_vsmart_model.py @@ -17,7 +17,7 @@ class Config: arbitrary_types_allowed = True allow_population_by_field_name = True - protocol: Optional[Protocol] = Field(default=None) - tls_port: Optional[int] = Field(default=None, alias="tls-port") + protocol: Optional[Protocol] = Field(default=None, data_path=["control"]) + tls_port: Optional[int] = Field(default=None, alias="tls-port", data_path=["control"]) payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" type: ClassVar[str] = "security-vsmart" diff --git a/vmngclient/tests/templates/definitions/Basic_Cisco_VPN_Model.json b/vmngclient/tests/templates/definitions/Basic_Cisco_VPN_Model.json index 37ad96a77..d8cce1e31 100644 --- a/vmngclient/tests/templates/definitions/Basic_Cisco_VPN_Model.json +++ b/vmngclient/tests/templates/definitions/Basic_Cisco_VPN_Model.json @@ -1,124 +1,11 @@ { "templateName": "Basic_Cisco_VPN_Model", "templateDescription": "Primitive", - "templateType": "vpn-vedge", - "deviceType": [], + "templateType": "cisco_vpn", + "deviceType": [ + "vedge-C8000V" + ], + "factoryDefault": false, "templateMinVersion": "15.0.0", - "templateDefinition": { - "vpn-id": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": 0 - }, - "name": { - "vipObjectType": "object", - "vipType": "ignore", - "vipVariableName": "vpn_name" - }, - "ecmp-hash-key": { - "layer4": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "false", - "vipVariableName": "vpn_layer4" - } - }, - "tcp-optimization": { - "vipObjectType": "node-only", - "vipType": "ignore", - "vipValue": "false", - "vipVariableName": "vpn_tcp_optimization" - }, - "nat64-global": { - "prefix": { - "stateful": {} - } - }, - "nat64": { - "v4": { - "pool": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "name" - ] - } - } - }, - "route-import": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "protocol" - ] - }, - "route-export": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "protocol" - ] - }, - "route-import-service": { - "from": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "vpn", - "protocol" - ] - } - }, - "host": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "hostname" - ] - }, - "service": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "svc-type" - ] - }, - "ip": { - "gre-route": {}, - "ipsec-route": {}, - "service-route": {} - }, - "ipv6": {}, - "omp": { - "advertise": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "protocol" - ] - }, - "distance": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "", - "vipVariableName": "vpn_distance" - }, - "ipv6-advertise": { - "vipType": "ignore", - "vipValue": [], - "vipObjectType": "tree", - "vipPrimaryKey": [ - "protocol" - ] - } - } - }, - "factoryDefault": false -} \ No newline at end of file + "templateDefinition": {} +} diff --git a/vmngclient/tests/templates/definitions/complex_cisco_vpn.json b/vmngclient/tests/templates/definitions/complex_cisco_vpn.json new file mode 100644 index 000000000..8d649a6bf --- /dev/null +++ b/vmngclient/tests/templates/definitions/complex_cisco_vpn.json @@ -0,0 +1,1071 @@ +{ + "templateName": "test_vpn_new", + "templateDescription": "NA", + "templateType": "cisco_vpn", + "deviceType": [ + "vedge-cloud" + ], + "factoryDefault": false, + "templateMinVersion": "15.0.0", + "templateDefinition": { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_vpn_name" + }, + "omp-admin-distance-ipv4": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 10 + }, + "omp-admin-distance-ipv6": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + }, + "dns": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "dns-addr": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "role": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "primary" + } + }, + { + "dns-addr": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.2.2.2" + }, + "role": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "secondary" + } + } + ], + "vipPrimaryKey": [ + "dns-addr" + ] + }, + "dns-ipv6": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "dns-addr": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "30a8:b25e:3db5:fe9f:231f:7478:4181:9234" + }, + "role": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "primary" + } + } + ], + "vipPrimaryKey": [ + "dns-addr" + ] + }, + "host": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "hostname": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_hostname" + }, + "ip": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "1.1.1.1" + ] + } + } + ], + "vipPrimaryKey": [ + "hostname" + ] + }, + "service": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "svc-type": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "appqoe" + }, + "address": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "1.1.1.1" + ] + }, + "interface": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "Gig0/0/1" + }, + "track-enable": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "false" + } + }, + { + "svc-type": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "FW" + }, + "address": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "1.1.122.1", + "2.2.2.2" + ] + }, + "interface": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "Gig0/0/2" + }, + "track-enable": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "true" + } + }, + { + "svc-type": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "IDP" + }, + "address": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "1.1.122.2", + "3.2.2.2" + ] + }, + "interface": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "Gig0/0/3" + }, + "track-enable": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "false" + } + } + ], + "vipPrimaryKey": [ + "svc-type" + ] + }, + "ip": { + "service-route": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "service_route" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + }, + "service": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "sig" + } + }, + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "service_route100" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + }, + "service": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "sig" + } + } + ], + "vipPrimaryKey": [ + "prefix" + ] + }, + "route": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "prefixv4" + }, + "next-hop": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "distance": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + } + } + ], + "vipPrimaryKey": [ + "address" + ] + }, + "next-hop-with-track": { + "vipObjectType": "tree", + "vipValue": [], + "vipType": "ignore", + "vipPrimaryKey": [ + "address" + ] + }, + "distance": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + } + } + ], + "vipPrimaryKey": [ + "prefix" + ] + }, + "gre-route": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "gre_route" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + }, + "interface": { + "vipObjectType": "list", + "vipValue": [], + "vipType": "ignore" + } + }, + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "gre_route2" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 2 + }, + "interface": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "Gig0/0/1", + "ge0/0" + ] + } + } + ], + "vipPrimaryKey": [ + "prefix" + ] + }, + "ipsec-route": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "ipsec-prefix" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 10 + }, + "interface": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "ge0/0", + "Gig0/0/1" + ] + } + }, + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "prefix-2" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + }, + "interface": { + "vipObjectType": "list", + "vipValue": [], + "vipType": "ignore" + } + } + ], + "vipPrimaryKey": [ + "prefix" + ] + } + }, + "ipv6": { + "route": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "prefixv6" + }, + "next-hop": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.2.2.2" + }, + "distance": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + } + } + ], + "vipPrimaryKey": [ + "address" + ] + }, + "nat": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "NAT64" + } + } + ], + "vipPrimaryKey": [ + "prefix" + ] + } + }, + "omp": { + "advertise": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "aggregate" + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "route-policy" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "prefix-list": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix-entry": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "prefix_entry" + }, + "aggregate-only": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": true + } + } + ], + "vipPrimaryKey": [ + "prefix-entry" + ] + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + }, + "ipv6-advertise": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "aggregate" + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "route-policyv6" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "prefix-list": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix-entry": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "prefix_entryv6" + } + } + ], + "vipPrimaryKey": [ + "prefix-entry" + ] + } + }, + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "connected" + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "route-policyv6-connected" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "prefix-list": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "prefix-entry": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "prefix_entryv6-connected" + }, + "aggregate-only": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": true + } + } + ], + "vipPrimaryKey": [ + "prefix-entry" + ] + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + } + }, + "nat64": { + "v4": { + "pool": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "pool" + }, + "start-address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "end-address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "10.10.10.10" + }, + "leak_from_global": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": true + }, + "leak_from_global_protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "connected" + } + } + ], + "vipPrimaryKey": [ + "name" + ] + } + } + }, + "nat": { + "natpool": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + }, + "prefix-length": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 24 + }, + "range-start": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "10" + }, + "range-end": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "100" + }, + "overload": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "false" + }, + "direction": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "inside" + }, + "tracker-id": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 10 + } + }, + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 2 + }, + "prefix-length": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 24 + }, + "range-start": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "10" + }, + "range-end": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "100" + }, + "overload": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "true" + }, + "direction": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "outside" + }, + "tracker-id": { + "vipObjectType": "object", + "vipValue": [], + "vipType": "ignore" + } + } + ], + "vipPrimaryKey": [ + "name" + ] + }, + "static": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "pool-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + }, + "source-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "translate-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.2" + }, + "static-nat-direction": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "inside" + }, + "tracker-id": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + } + }, + { + "pool-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 2 + }, + "source-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.1.1.1" + }, + "translate-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.1.1.2" + }, + "static-nat-direction": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "outside" + }, + "tracker-id": { + "vipObjectType": "object", + "vipValue": [], + "vipType": "ignore" + } + } + ], + "vipPrimaryKey": [ + "source-ip", + "translate-ip" + ] + }, + "subnet-static": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "source-ip-subnet": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "translate-ip-subnet": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.2.2.2" + }, + "prefix-length": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 24 + }, + "static-nat-direction": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "outside" + }, + "tracker-id": { + "vipObjectType": "object", + "vipValue": [], + "vipType": "ignore" + } + }, + { + "source-ip-subnet": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.2.1" + }, + "translate-ip-subnet": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.3.2.2" + }, + "prefix-length": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 24 + }, + "static-nat-direction": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "inside" + }, + "tracker-id": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 10 + } + } + ], + "vipPrimaryKey": [ + "source-ip-subnet", + "translate-ip-subnet" + ] + }, + "port-forward": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "pool-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + }, + "source-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1000 + }, + "translate-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 2000 + }, + "source-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "translate-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.2.2.2" + }, + "proto": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "tcp" + } + }, + { + "pool-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 2 + }, + "source-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1000 + }, + "translate-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 2000 + }, + "source-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.4.1" + }, + "translate-ip": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "2.2.3.2" + }, + "proto": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "udp" + } + } + ], + "vipPrimaryKey": [ + "source-port", + "translate-port", + "source-ip", + "translate-ip", + "proto" + ] + } + }, + "route-import": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "bgp" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_route_policy" + }, + "redistribute": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "eigrp" + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_route_policy" + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + }, + "route-import-from": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "source-vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1 + }, + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "connected" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_route_policy" + }, + "redistribute": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "bgp" + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + } + }, + { + "source-vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + }, + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "bgp" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_route_policy" + }, + "redistribute": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "eigrp" + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_route_policy" + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + } + } + ], + "vipPrimaryKey": [ + "source-vpn", + "protocol" + ] + }, + "route-export": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "static" + }, + "protocol-sub-type": { + "vipObjectType": "list", + "vipType": "constant", + "vipValue": [ + "external" + ] + }, + "redistribute": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "protocol": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "ospf" + }, + "route-policy": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_route_policy" + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + } + } + ], + "vipPrimaryKey": [ + "protocol" + ] + } + } +} diff --git a/vmngclient/tests/templates/models/__init__.py b/vmngclient/tests/templates/models/__init__.py index 3f035b081..789c256b5 100644 --- a/vmngclient/tests/templates/models/__init__.py +++ b/vmngclient/tests/templates/models/__init__.py @@ -1,6 +1,6 @@ # type: ignore from vmngclient.tests.templates.models.cisco_aaa import cisco_aaa -from vmngclient.tests.templates.models.cisco_vpn import basic_cisco_vpn +from vmngclient.tests.templates.models.cisco_vpn import basic_cisco_vpn, complex_vpn_model from vmngclient.tests.templates.models.omp_vsmart import default_omp, omp_2, omp_3 -__all__ = ["default_omp", "omp_2", "omp_3", "cisco_aaa", "basic_cisco_vpn"] +__all__ = ["default_omp", "omp_2", "omp_3", "cisco_aaa", "basic_cisco_vpn", "complex_vpn_model"] diff --git a/vmngclient/tests/templates/models/cisco_aaa.py b/vmngclient/tests/templates/models/cisco_aaa.py index c4e7888c2..ac5c2487c 100644 --- a/vmngclient/tests/templates/models/cisco_aaa.py +++ b/vmngclient/tests/templates/models/cisco_aaa.py @@ -1,8 +1,11 @@ +# type: ignore from vmngclient.api.templates.models.cisco_aaa_model import ( CiscoAAAModel, DomainStripping, RadiusGroup, RadiusServer, + TacacsGroup, + TacacsServer, User, ) from vmngclient.utils.device_model import DeviceModel @@ -42,3 +45,50 @@ ], domain_stripping=DomainStripping.NO, ) + +complex_aaa_model = CiscoAAAModel( + template_name="test_aaa", + template_description="na", + user=[ + User(name="test1", password="*****", secret="secret", privilege="1", pubkey_chain=["1", "2"]), + User(name="test2", password="*****", secret="secret", privilege="15", pubkey_chain=["1", "2"]), + ], + authentication_group=True, + accounting_group=False, + radius=[ + RadiusGroup( + group_name="group1", + vpn=10, + source_interface="Gig1", + server=[ + RadiusServer( + address="1.1.1.1", key="test_key", secret_key="secret_key", key_enum="key_enum", key_type="key_type" + ) + ], + ), + RadiusGroup( + group_name="group2", + vpn=11, + source_interface="Gig2", + server=[ + RadiusServer( + address="1.1.2.1", + key="test_key2", + secret_key="secret_key2", + key_enum="key_enum2", + key_type="key_type2", + ) + ], + ), + ], + domain_stripping=DomainStripping.RIGHT_TO_LEFT, + tacacs=[ + TacacsGroup( + group_name="group1", + vpn=0, + source_interface="Gig0", + server=[TacacsServer(address="1.1.1.1", key="key", secret_key="secret_key", key_enum="key_enum")], + ) + ], + server_auth_order="test", +) diff --git a/vmngclient/tests/templates/models/cisco_vpn.py b/vmngclient/tests/templates/models/cisco_vpn.py index 6bca2c66d..bfc275d36 100644 --- a/vmngclient/tests/templates/models/cisco_vpn.py +++ b/vmngclient/tests/templates/models/cisco_vpn.py @@ -1,6 +1,54 @@ # type: ignore -from vmngclient.api.templates.models.cisco_vpn_model import CiscoVPNModel, Dns, DnsIpv6, Host, NextHop, Routev4, Routev6 +from vmngclient.api.templates.models.cisco_vpn_model import ( + Advertise, + AdvertiseProtocol, + AdvertiseProtocolSubType, + CiscoVPNModel, + Direction, + Dns, + DnsIpv6, + GreRoute, + Host, + IpsecRoute, + Ipv6Advertise, + Ipv6AdvertiseProtocol, + Ipv6AdvertiseProtocolSubType, + LeakFromGlobalProtocol, + Nat, + Natpool, + NextHop, + Overload, + Pool, + PortForward, + PrefixList, + Proto, + Region, + Role, + RouteExport, + RouteExportProtocol, + RouteExportProtocolSubType, + RouteExportRedistribute, + RouteExportRedistributeProtocol, + RouteImport, + RouteImportFrom, + RouteImportFromProtocol, + RouteImportFromProtocolSubType, + RouteImportFromRedistribute, + RouteImportFromRedistributeProtocol, + RouteImportProtocol, + RouteImportProtocolSubType, + RouteImportRedistribute, + RouteImportRedistributeProtocol, + Routev4, + Routev6, + Service, + ServiceRoute, + Static, + StaticNatDirection, + SubnetStatic, + SvcType, +) from vmngclient.utils.device_model import DeviceModel basic_cisco_vpn = CiscoVPNModel( @@ -8,19 +56,212 @@ ) # type: ignore -complex_cisco_vpn = CiscoVPNModel( - template_name="Complex_CiscoVPN_Model", - template_description="Complex", - device_models=[DeviceModel.VEDGE_C8000V], - vpn_id=123, - vpn_name="VPN", - layer4=True, - omp_admin_distance_ipv4=255, - dns=[Dns(dns_addr="255.255.255.0")], - host=[Host(hostname="random", ip=["1.1.1.1", "2.2.2.2"])], +complex_vpn_model = CiscoVPNModel( + template_name="complex_cisco_vpn", + template_description="NA", + device_models=[DeviceModel.VEDGE], + vpn_name="test_vpn_name", + omp_admin_distance_ipv4=10, + omp_admin_distance_ipv6=100, + route_v4=[Routev4(prefix="prefixv4", next_hop=[NextHop(address="1.1.1.1")])], + route_v6=[Routev6(prefix="prefixv6", next_hop=[NextHop(address="2.2.2.2")], nat=Nat.NAT64)], + dns=[Dns(dns_addr="1.1.1.1"), Dns(dns_addr="2.2.2.2", role=Role.SECONDARY)], dns_ipv6=[DnsIpv6(dns_addr="30a8:b25e:3db5:fe9f:231f:7478:4181:9234")], - route_v4=[Routev4(prefix="1.1.1.1/24", null0=True, distance=5, next_hop=[NextHop(address="1.1.1.1")])], - route_v6=[ - Routev6(prefix="2001:db8:1234::/48", next_hop=[NextHop(address="2001:db8:1234:0000:0000:0000:0000:0000")]) + host=[Host(hostname="test_hostname", ip=["1.1.1.1"])], + service=[ + Service( + svc_type=SvcType.APPQOE, + address=["1.1.1.1"], + interface="Gig0/0/1", + track_enable=False, + ), + Service( + svc_type=SvcType.FW, + address=["1.1.122.1", "2.2.2.2"], + interface="Gig0/0/2", + track_enable=True, + ), + Service( + svc_type=SvcType.IDP, + address=["1.1.122.2", "3.2.2.2"], + interface="Gig0/0/3", + track_enable=False, + ), + ], + service_route=[ + ServiceRoute(prefix="service_route", vpn=1), + ServiceRoute(prefix="service_route100", vpn=100), + ], + gre_route=[ + GreRoute(prefix="gre_route", vpn=100), + GreRoute(prefix="gre_route2", vpn=2, interface=["Gig0/0/1", "ge0/0"]), + ], + ipsec_route=[ + IpsecRoute(prefix="ipsec-prefix", vpn=10, interface=["ge0/0", "Gig0/0/1"]), + IpsecRoute(prefix="prefix-2", vpn=100), + ], + advertise=[ + Advertise( + protocol=AdvertiseProtocol.AGGREGATE, + route_policy="route-policy", + protocol_sub_type=[AdvertiseProtocolSubType.EXTERNAL], + prefix_list=[ + PrefixList( + prefix_entry="prefix_entry", + aggregate_only=True, + region=Region.ACCESS, + ) + ], + ) + ], + ipv6_advertise=[ + Ipv6Advertise( + protocol=Ipv6AdvertiseProtocol.AGGREGATE, + route_policy="route-policyv6", + protocol_sub_type=[Ipv6AdvertiseProtocolSubType.EXTERNAL], + prefix_list=[ + PrefixList( + prefix_entry="prefix_entryv6", + aggregate_only=False, + region=Region.CORE, + ) + ], + ), + Ipv6Advertise( + protocol=Ipv6AdvertiseProtocol.CONNECTED, + route_policy="route-policyv6-connected", + protocol_sub_type=[Ipv6AdvertiseProtocolSubType.EXTERNAL], + prefix_list=[ + PrefixList( + prefix_entry="prefix_entryv6-connected", + aggregate_only=True, + region=Region.ACCESS, + ) + ], + ), + ], + pool=[ + Pool( + name="pool", + start_address="1.1.1.1", + end_address="10.10.10.10", + overload=False, + leak_from_global=True, + leak_from_global_protocol=LeakFromGlobalProtocol.CONNECTED, + leak_to_global=False, + ) + ], + natpool=[ + Natpool( + name=1, + prefix_length=24, + range_start="10", + range_end="100", + overload=Overload.FALSE, + direction=Direction.INSIDE, + tracker_id=10, + ), + Natpool( + name=2, + prefix_length=24, + range_start="10", + range_end="100", + overload=Overload.TRUE, + direction=Direction.OUTSIDE, + ), + ], + static=[ + Static( + pool_name=1, + source_ip="1.1.1.1", + translate_ip="1.1.1.2", + static_nat_direction=StaticNatDirection.INSIDE, + tracker_id=1, + ), + Static( + pool_name=2, + source_ip="2.1.1.1", + translate_ip="2.1.1.2", + static_nat_direction=StaticNatDirection.OUTSIDE, + ), + ], + subnet_static=[ + SubnetStatic( + source_ip_subnet="1.1.1.1", + translate_ip_subnet="2.2.2.2", + prefix_length=24, + static_nat_direction=StaticNatDirection.OUTSIDE, + ), + SubnetStatic( + source_ip_subnet="1.1.2.1", + translate_ip_subnet="2.3.2.2", + prefix_length=24, + static_nat_direction=StaticNatDirection.INSIDE, + tracker_id=10, + ), + ], + port_forward=[ + PortForward( + pool_name=1, + source_port=1000, + translate_port=2000, + source_ip="1.1.1.1", + translate_ip="2.2.2.2", + proto=Proto.TCP, + ), + PortForward( + pool_name=2, + source_port=1000, + translate_port=2000, + source_ip="1.1.4.1", + translate_ip="2.2.3.2", + proto=Proto.UDP, + ), + ], + route_import=[ + RouteImport( + protocol=RouteImportProtocol.BGP, + protocol_sub_type=[RouteImportProtocolSubType.EXTERNAL], + route_policy="test_route_policy", + redistribute=[ + RouteImportRedistribute( + protocol=RouteImportRedistributeProtocol.EIGRP, + route_policy="test_route_policy", + ) + ], + ) + ], + route_import_from=[ + RouteImportFrom( + source_vpn=1, + protocol=RouteImportFromProtocol.CONNECTED, + protocol_sub_type=[RouteImportFromProtocolSubType.EXTERNAL], + route_policy="test_route_policy", + redistribute=[RouteImportFromRedistribute(protocol=RouteImportFromRedistributeProtocol.BGP)], + ), + RouteImportFrom( + source_vpn=100, + protocol=RouteImportFromProtocol.BGP, + protocol_sub_type=[RouteImportFromProtocolSubType.EXTERNAL], + route_policy="test_route_policy", + redistribute=[ + RouteImportFromRedistribute( + protocol=RouteImportFromRedistributeProtocol.EIGRP, + route_policy="test_route_policy", + ) + ], + ), + ], + route_export=[ + RouteExport( + protocol=RouteExportProtocol.STATIC, + protocol_sub_type=[RouteExportProtocolSubType.EXTERNAL], + redistribute=[ + RouteExportRedistribute( + protocol=RouteExportRedistributeProtocol.OSPF, + route_policy="test_route_policy", + ) + ], + ) ], ) diff --git a/vmngclient/tests/templates/schemas/cisco_vpn.json b/vmngclient/tests/templates/schemas/cisco_vpn.json new file mode 100644 index 000000000..b4c0552aa --- /dev/null +++ b/vmngclient/tests/templates/schemas/cisco_vpn.json @@ -0,0 +1,2452 @@ +{ + "name": "cisco_vpn", + "xmlPath": [ + "vpn" + ], + "xmlRootTag": "vpn-instance", + "uniqueKey": "vpn-id", + "nameSpace": "http://viptela.com/vpn", + "fields": [ + { + "key": "vpn-id", + "description": "VPN", + "details": "List of VPN instances", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 0, + "max": 65527, + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "name", + "description": "Name", + "details": "Name", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "string", + "minLength": 1, + "maxLength": 32 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "tenant-vpn-id", + "description": "Tenant VPN", + "details": "Tenant VPN", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 0, + "max": 65527, + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "org-name", + "description": "Select Tenant", + "details": "Org Name selected", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "string" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "omp-admin-distance-ipv4", + "description": "OMP Admin Distance IPv4", + "details": "omp-admin-distance-ipv4", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 255 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "omp-admin-distance-ipv6", + "description": "OMP Admin Distance IPv6", + "details": "omp-admin-distance-ipv6", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 255 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "dns", + "description": "DNS", + "details": "DNS", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "dns-addr" + ], + "children": [ + { + "key": "dns-addr", + "description": "DNS Address", + "details": "DNS Address", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "ip" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "role", + "description": "Role", + "details": "Role", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [ + { + "key": "primary", + "value": "Primary" + }, + { + "key": "secondary", + "value": "Secondary" + } + ], + "default": "primary" + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "dns-ipv6", + "description": "DNS", + "details": "DNS", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "dns-addr" + ], + "children": [ + { + "key": "dns-addr", + "description": "DNS Address", + "details": "DNS Address", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv6" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "role", + "description": "Role", + "details": "Role", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [ + { + "key": "primary", + "value": "Primary" + }, + { + "key": "secondary", + "value": "Secondary" + } + ], + "default": "primary" + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "layer4", + "description": "Enhance ECMP Keying", + "details": "Optional packet fields for ECMP keying", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [ + "ecmp-hash-key" + ], + "objectType": "object" + }, + { + "key": "host", + "description": "Static DNS Mapping", + "details": "Static DNS mapping", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "hostname" + ], + "children": [ + { + "key": "hostname", + "description": "Hostname", + "details": "Hostname", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "dnsHostName", + "minLength": 1, + "maxLength": 128 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "ip", + "description": "List of IP", + "details": "List of IP", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ip", + "max": 8 + }, + "dataPath": [], + "objectType": "list" + } + ] + }, + { + "key": "service", + "description": "Service", + "details": "Configure services", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "svc-type" + ], + "children": [ + { + "key": "svc-type", + "description": "Service Type", + "details": "Service Type", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "FW", + "value": "FW" + }, + { + "key": "IDS", + "value": "IDS" + }, + { + "key": "IDP", + "value": "IDP" + }, + { + "key": "netsvc1", + "value": "netsvc1" + }, + { + "key": "netsvc2", + "value": "netsvc2" + }, + { + "key": "netsvc3", + "value": "netsvc3" + }, + { + "key": "netsvc4", + "value": "netsvc4" + }, + { + "key": "TE", + "value": "TE" + }, + { + "key": "appqoe", + "value": "appqoe" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "address", + "description": "IPv4 address", + "details": "List of IPv4 address", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "list", + "minChildren": "0", + "maxChildren": "4" + }, + { + "key": "interface", + "description": "Interface", + "details": "Tracking Service", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": "string", + "dataPath": [], + "objectType": "object" + }, + { + "key": "track-enable", + "description": "Tracking", + "details": "Tracking Service", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "true" + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "service-route", + "description": "IPv4 Static Service Route", + "details": "Configure IPv4 Static Service Routes", + "optionType": [ + "constant", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "ip" + ], + "objectType": "tree", + "primaryKeys": [ + "prefix" + ], + "children": [ + { + "key": "prefix", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "vpn", + "description": "VPN", + "details": "Destination VPN to resolve the prefix", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 0, + "max": 0, + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "service", + "description": "Service", + "details": "Service", + "optionType": [ + "constant", + "notIgnore" + ], + "defaultOption": "notIgnore", + "dataType": { + "type": "enum", + "values": [ + { + "key": "sig", + "value": "SIG" + } + ], + "default": "sig" + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "route", + "description": "IPv4 Static Route", + "details": "Configure IPv4 Static Routes", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "ip" + ], + "objectType": "tree", + "primaryKeys": [ + "prefix" + ], + "children": [ + { + "key": "prefix", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "next-hop", + "description": "IP Gateway", + "details": "IP gateway address", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "address" + ], + "children": [ + { + "key": "address", + "description": "Address", + "details": "IP Address", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "string" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "distance", + "description": "Distance", + "details": "Administrative distance", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 255, + "default": 1 + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "next-hop-with-track", + "description": "IP Gateway", + "details": "IP gateway address", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "address" + ], + "children": [ + { + "key": "address", + "description": "Address", + "details": "IP Address", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "string" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "distance", + "description": "Distance", + "details": "Administrative distance", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 255, + "default": 1 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "tracker", + "description": "Tracker", + "details": "Static route tracker", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "string" + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "null0", + "description": "null0", + "details": "null0", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "node-only" + }, + { + "key": "distance", + "description": "Distance", + "details": "Administrative distance", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 255, + "default": 1 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "vpn", + "description": "VPN", + "details": "Destination VPN(!=0 or !=512) to resolve the prefix", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "range": "0", + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "dhcp", + "description": "DHCP", + "details": "Default Gateway obtained from DHCP", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "node-only" + } + ] + }, + { + "key": "route", + "description": "IPv6 Static Route", + "details": "Configure IPv6 Static Routes", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "ipv6" + ], + "objectType": "tree", + "primaryKeys": [ + "prefix" + ], + "children": [ + { + "key": "prefix", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv6-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "next-hop", + "description": "IP Gateway", + "details": "IP gateway address", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "address" + ], + "children": [ + { + "key": "address", + "description": "Address", + "details": "IP Address", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv6" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "distance", + "description": "Distance", + "details": "Administrative distance", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 255, + "default": 1 + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "null0", + "description": "null0", + "details": "null0", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "node-only" + }, + { + "key": "vpn", + "description": "VPN", + "details": "Destination VPN(!=0 or !=512) to resolve the prefix", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "range": "0", + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "nat", + "description": "NAT", + "details": "NAT", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "constant", + "dataType": { + "type": "radioButtonList", + "default": "NAT64", + "values": [ + { + "label": "NAT64", + "value": "NAT64" + }, + { + "label": "NAT66", + "value": "NAT66" + } + ] + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "gre-route", + "description": "IPv4 Static GRE Route", + "details": "Configure routes pointing to a GRE tunnel", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "ip" + ], + "objectType": "tree", + "primaryKeys": [ + "prefix" + ], + "children": [ + { + "key": "prefix", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "vpn", + "description": "VPN", + "details": "Destination VPN to resolve the prefix", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 0, + "max": 0, + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "interface", + "description": "GRE Interface (Maximum entries allowed: 2)", + "details": "List of GRE Interfaces", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "string" + }, + "minChildren": "0", + "maxChildren": "2", + "dataPath": [], + "objectType": "list" + } + ] + }, + { + "key": "ipsec-route", + "description": "IPv4 Static IPSEC Route", + "details": "Configure routes pointing to a IPSEC tunnel", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "ip" + ], + "objectType": "tree", + "primaryKeys": [ + "prefix" + ], + "children": [ + { + "key": "prefix", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "vpn", + "description": "VPN", + "details": "Destination VPN to resolve the prefix", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 0, + "max": 0, + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "interface", + "description": "IPSEC Interface (Maximum entries allowed: 2)", + "details": "List of IPSEC Interfaces (Separated by commas)", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "interfaceList" + }, + "minChildren": "0", + "maxChildren": "2", + "dataPath": [], + "objectType": "list" + } + ] + }, + { + "key": "advertise", + "description": "Advertise", + "details": "Advertise routes to OMP", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "omp" + ], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Protocol", + "details": "Advertised routes protocol", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "bgp", + "value": "BGP" + }, + { + "key": "ospf", + "value": "OSPF" + }, + { + "key": "ospfv3", + "value": "OSPFV3" + }, + { + "key": "connected", + "value": "Connected" + }, + { + "key": "static", + "value": "Static" + }, + { + "key": "network", + "value": "Network" + }, + { + "key": "aggregate", + "value": "Aggregate" + }, + { + "key": "eigrp", + "value": "EIGRP" + }, + { + "key": "lisp", + "value": "LISP" + }, + { + "key": "isis", + "value": "ISIS" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "route-policy", + "description": "Route Policy", + "details": "Set Route Policy to OMP", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "string", + "minLength": 1, + "maxLength": 127 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "protocol-sub-type", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "minElements": 1, + "dataType": { + "type": "enum", + "values": [ + { + "key": "external", + "value": "External" + } + ] + }, + "dataPath": [], + "objectType": "list" + }, + { + "key": "prefix-list", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "minElements": 1, + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "prefix-entry" + ], + "children": [ + { + "key": "prefix-entry", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "aggregate-only", + "description": "Aggregate Only", + "details": "Aggregate Only", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "object" + } + ] + } + ] + }, + { + "key": "ipv6-advertise", + "description": "Advertise", + "details": "Advertise routes to OMP", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "omp" + ], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Protocol", + "details": "Advertised routes protocol", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "bgp", + "value": "BGP" + }, + { + "key": "ospf", + "value": "Ospf" + }, + { + "key": "connected", + "value": "Connected" + }, + { + "key": "static", + "value": "Static" + }, + { + "key": "network", + "value": "Network" + }, + { + "key": "aggregate", + "value": "Aggregate" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "route-policy", + "description": "Route Policy", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "string", + "minLength": 1, + "maxLength": 127 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "protocol-sub-type", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "minElements": 1, + "dataType": { + "type": "enum", + "values": [ + { + "key": "external", + "value": "External" + } + ] + }, + "dataPath": [], + "objectType": "list" + }, + { + "key": "prefix-list", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "minElements": 1, + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "prefix-entry" + ], + "children": [ + { + "key": "prefix-entry", + "description": "Prefix", + "details": "Prefix", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv6-prefix" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "aggregate-only", + "description": "Aggregate Only", + "details": "Aggregate Only", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "object" + } + ] + } + ] + }, + { + "key": "pool", + "description": "NAT64 V4 Pool", + "details": "Set NAT64 v4 pool range", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "nat64", + "v4" + ], + "objectType": "tree", + "primaryKeys": [ + "name" + ], + "children": [ + { + "key": "name", + "description": "NAT64 Pool name", + "details": "NAT64 Pool name", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "string", + "minLength": 1, + "maxLength": 32 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "start-address", + "description": "NAT 64 v4 Pool Range Start", + "details": "Starting IP address of NAT pool range", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "end-address", + "description": "NAT 64 v4 Pool Range End", + "details": "Ending IP address of NAT pool range", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "overload", + "description": "NAT 64 Overload", + "details": "NAT 64 Overload Option", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "node-only" + }, + { + "key": "leak_from_global", + "description": "Enable Route Leaking from Global VPN", + "details": "Enable Route Leaking from Global VPN to this Service VPN", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "leak_from_global_protocol", + "description": "Protocol", + "details": "Select protocol for route leaking", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "all", + "value": "All" + }, + { + "key": "static", + "value": "Static" + }, + { + "key": "mobile", + "value": "Mobile" + }, + { + "key": "connected", + "value": "Connected" + }, + { + "key": "rip", + "value": "Rip" + }, + { + "key": "odr", + "value": "Odr" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "leak_to_global", + "description": "Enable Route Leaking to Global VPN", + "details": "Enable Route Leaking from this Service VPN to Global VPN", + "optionType": [ + "constant" + ], + "defaultOption": "constant", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "natpool", + "description": "NAT Pool", + "details": "Configure NAT Pool entries", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "nat" + ], + "objectType": "tree", + "primaryKeys": [ + "name" + ], + "children": [ + { + "key": "name", + "description": "NAT Pool Name", + "details": "NAT Pool Name, natpool1..31", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 1, + "max": 31 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "prefix-length", + "description": "NAT Pool Prefix Length", + "details": "Ending IP address of NAT Pool Prefix Length", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 1, + "max": 32 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "range-start", + "description": "NAT Pool Range Start", + "details": "Starting IP address of NAT pool range", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "range-end", + "description": "NAT Pool Range End", + "details": "Ending IP address of NAT pool range", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "overload", + "description": "NAT Overload", + "details": "Enable port translation(PAT)", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "true" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "direction", + "description": "NAT Direction", + "details": "Direction of NAT translation", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "inside", + "value": "Inside" + }, + { + "key": "outside", + "value": "Outside" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "tracker-id", + "description": "Add Object/Object Group Tracker", + "details": "Add Object/Object Group Tracker", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 1000 + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "static", + "description": "Static NAT Rules", + "details": "Configure static NAT entries", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "nat" + ], + "objectType": "tree", + "primaryKeys": [ + "source-ip", + "translate-ip" + ], + "children": [ + { + "key": "pool-name", + "description": "NAT Pool Name", + "details": "NAT Pool Name, natpool1..31", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "source-ip", + "description": "Source IP Address", + "details": "Source IP address to be translated", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "translate-ip", + "description": "Translated Source IP Address", + "details": "Statically translated source IP address", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "static-nat-direction", + "description": "Static NAT Direction", + "details": "Direction of static NAT translation", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "inside", + "value": "Inside" + }, + { + "key": "outside", + "value": "Outside" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "tracker-id", + "description": "Add Object/Object Group Tracker", + "details": "Add Object/Object Group Tracker", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 1000 + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "subnet-static", + "description": "Static NAT Subnet Rules", + "details": "Configure static NAT Subnet entries", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "nat" + ], + "objectType": "tree", + "primaryKeys": [ + "source-ip-subnet", + "translate-ip-subnet" + ], + "children": [ + { + "key": "source-ip-subnet", + "description": "Source IP Subnet", + "details": "Source IP Subnet to be translated", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "translate-ip-subnet", + "description": "Translated Source IP Subnet", + "details": "Statically translated source IP Subnet", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "prefix-length", + "description": "Network Prefix Length", + "details": "Network Prefix Length", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 1, + "max": 32 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "static-nat-direction", + "description": "Static NAT Direction", + "details": "Direction of static NAT translation", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "inside", + "value": "Inside" + }, + { + "key": "outside", + "value": "Outside" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "tracker-id", + "description": "Add Object/Object Group Tracker", + "details": "Add Object/Object Group Tracker", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 1000 + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "port-forward", + "description": "Port Forward", + "details": "Configure Port Forward entries", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [ + "nat" + ], + "objectType": "tree", + "primaryKeys": [ + "source-port", + "translate-port", + "source-ip", + "translate-ip", + "proto" + ], + "children": [ + { + "key": "pool-name", + "description": "NAT Pool Name", + "details": "NAT Pool Name, natpool1..31", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "source-port", + "description": "Source Port", + "details": "Source Port", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "translate-port", + "description": "Translate Port", + "details": "Translate Port", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "default": 0 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "source-ip", + "description": "Source IP Address", + "details": "Source IP address to be translated", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "translate-ip", + "description": "Translated Source IP Address", + "details": "Statically translated source IP address", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "ipv4" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "proto", + "description": "Protocol", + "details": "Protocol", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "tcp", + "value": "TCP" + }, + { + "key": "udp", + "value": "UDP" + } + ] + }, + "dataPath": [], + "objectType": "object" + } + ] + }, + { + "key": "route-import", + "description": "Enable route leaking from Global VPN", + "details": "Enable route leaking from Global VPN to this Service VPN", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Route Protocol Leak from Global to Service", + "details": "Select a Route Protocol to enable route leaking from Global VPN to this Service VPN", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "static", + "value": "static" + }, + { + "key": "connected", + "value": "connected" + }, + { + "key": "bgp", + "value": "bgp" + }, + { + "key": "ospf", + "value": "ospf" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "protocol-sub-type", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "minElements": 1, + "dataType": { + "type": "enum", + "values": [ + { + "key": "external", + "value": "external" + } + ], + "default": "external" + }, + "dataPath": [], + "objectType": "list" + }, + { + "key": "route-policy", + "description": "Route Policy Leak from Global to Service", + "details": "Select a Route Policy to enable route leaking from Global VPN to this Service VPN", + "optionType": [ + "constant", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [], + "default": "" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "redistribute", + "description": "Redistribute To Protocol", + "details": "Enable redistribution of replicated route protocol", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Protocol", + "details": "Select a Route Protocol to enable redistribution", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "bgp", + "value": "bgp" + }, + { + "key": "eigrp", + "value": "eigrp" + }, + { + "key": "ospf", + "value": "ospf" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "route-policy", + "description": "Redistribution Policy", + "details": "Select a Route Policy to enable redistribution", + "optionType": [ + "constant", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [], + "default": "" + }, + "dataPath": [], + "objectType": "object" + } + ] + } + ] + }, + { + "key": "route-import-from", + "description": "Enable inter-service VPN route leak", + "details": "Enable route leak from Service VPN to current VPN", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "source-vpn", + "protocol" + ], + "children": [ + { + "key": "source-vpn", + "description": "Source VPN", + "details": "Select a Source VPN where route leaks from", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "number", + "min": 1, + "max": 65530, + "default": 1 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "protocol", + "description": "Route Protocol Leak to Current VPN", + "details": "Select a Route Protocol to enable route leaking to current VPN", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "static", + "value": "static" + }, + { + "key": "connected", + "value": "connected" + }, + { + "key": "bgp", + "value": "bgp" + }, + { + "key": "ospf", + "value": "ospf" + }, + { + "key": "eigrp", + "value": "eigrp" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "protocol-sub-type", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "minElements": 1, + "dataType": { + "type": "enum", + "values": [ + { + "key": "external", + "value": "external" + } + ], + "default": "external" + }, + "dataPath": [], + "objectType": "list" + }, + { + "key": "route-policy", + "description": "Route Policy Leak to Current VPN", + "details": "Select a Route Policy to enable route leaking to current VPN", + "optionType": [ + "constant", + "ignore", + "variable" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [], + "default": "" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "redistribute", + "description": "Redistribute To Protocol", + "details": "Enable redistribution of replicated route protocol", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Protocol", + "details": "Select a Route Protocol to enable redistribution", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "bgp", + "value": "bgp" + }, + { + "key": "eigrp", + "value": "eigrp" + }, + { + "key": "ospf", + "value": "ospf" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "route-policy", + "description": "Redistribution Policy", + "details": "Select a Route Policy to enable redistribution", + "optionType": [ + "constant", + "ignore", + "variable" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [], + "default": "" + }, + "dataPath": [], + "objectType": "object" + } + ] + } + ] + }, + { + "key": "route-export", + "description": "Enable route leaking to Global VPN", + "details": "Enable route leaking to Global VPN from this Service VPN", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Route Protocol Leak from Service to Global", + "details": "Select a Route Protocol to enable route leaking from this Service VPN to Global VPN", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "static", + "value": "static" + }, + { + "key": "connected", + "value": "connected" + }, + { + "key": "bgp", + "value": "bgp" + }, + { + "key": "eigrp", + "value": "eigrp" + }, + { + "key": "ospf", + "value": "ospf" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "protocol-sub-type", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "minElements": 1, + "dataType": { + "type": "enum", + "values": [ + { + "key": "external", + "value": "external" + } + ], + "default": "external" + }, + "dataPath": [], + "objectType": "list" + }, + { + "key": "route-policy", + "description": "Route Policy Leak from Service to Global", + "details": "Select a Route Policy to enable route leaking from this Service VPN to Global VPN", + "optionType": [ + "constant", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [], + "default": "" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "redistribute", + "description": "Redistribute To Protocol", + "details": "Enable redistribution of replicated route protocol", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "protocol" + ], + "children": [ + { + "key": "protocol", + "description": "Protocol", + "details": "Select a Route Protocol to enable redistribution", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "bgp", + "value": "bgp" + }, + { + "key": "ospf", + "value": "ospf" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "route-policy", + "description": "Redistribution Policy", + "details": "Select a Route Policy to enable redistribution", + "optionType": [ + "constant", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "enum", + "values": [], + "default": "" + }, + "dataPath": [], + "objectType": "object" + } + ] + } + ] + } + ] +} diff --git a/vmngclient/tests/templates/schemas/omp-vsmart.json b/vmngclient/tests/templates/schemas/omp-vsmart.json new file mode 100644 index 000000000..5489450b0 --- /dev/null +++ b/vmngclient/tests/templates/schemas/omp-vsmart.json @@ -0,0 +1,202 @@ +{ + "name": "OMP", + "xmlPath": [], + "xmlRootTag": "omp", + "nameSpace": "http://viptela.com/omp", + "fields": [ + { + "key": "graceful-restart", + "description": "Graceful Restart for OMP", + "details": "Enable or disable OMP graceful restart", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "true" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "send-path-limit", + "description": "Number of Paths Advertised per Prefix", + "details": "Set number of equal-cost routes advertised by vSmart to vEdge", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 32, + "default": 4 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "send-backup-paths", + "description": "Send Backup Paths", + "details": "Enable or disable advertisement of backup routes to vEdges", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "discard-rejected", + "description": "Discard Rejected Routes", + "details": "Enable or disable discarding of routes rejected by policy", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "shutdown", + "description": "Shutdown", + "details": "Enable or disable OMP", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false", + "label": { + "on": "Yes", + "off": "No" + } + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "advertisement-interval", + "description": "Advertisement Interval (seconds)", + "details": "Set the time between OMP Update packets", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 0, + "max": 65535, + "default": 1 + }, + "dataPath": [ + "timers" + ], + "objectType": "object" + }, + { + "key": "graceful-restart-timer", + "description": "Graceful Restart Timer (seconds)", + "details": "Set the OMP graceful restart timer", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 604800, + "default": 43200 + }, + "dataPath": [ + "timers" + ], + "objectType": "object" + }, + { + "key": "eor-timer", + "description": "EOR Timer", + "details": "End of RIB timer <1..604800> seconds", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 3600, + "default": 300 + }, + "dataPath": [ + "timers" + ], + "objectType": "object" + }, + { + "key": "holdtime", + "description": "Hold Time (seconds)", + "details": "Set how long to wait before closing OMP peer connection", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "range": "0,3-65535", + "default": 60 + }, + "dataPath": [ + "timers" + ], + "objectType": "object" + }, + { + "key": "affinity-group-preference", + "description": "Enable Filtering Route Updates Based on Affinity", + "details": "Filter routes based on affinity preference list", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "false" + }, + "dataPath": [ + "filter-route", + "outbound" + ], + "objectType": "object" + } + ] +} diff --git a/vmngclient/tests/templates/test_deserialize_model.py b/vmngclient/tests/templates/test_deserialize_model.py index 69b28bbc2..e1b09e28c 100644 --- a/vmngclient/tests/templates/test_deserialize_model.py +++ b/vmngclient/tests/templates/test_deserialize_model.py @@ -6,6 +6,7 @@ from unittest import TestCase from unittest.mock import patch +import pytest from parameterized import parameterized import vmngclient.tests.templates.models as models @@ -49,6 +50,7 @@ def setUp(self): ) @parameterized.expand([(template,) for template in map(models.__dict__.get, models.__all__)]) + @pytest.mark.skip(reason="Deserialization to be refactored") @patch("vmngclient.session.vManageSession") def test_get(self, template: FeatureTemplate, mock_session): # Arrange From c301e636eb1552cfa2b2660083baf453a761a0fa Mon Sep 17 00:00:00 2001 From: Mateusz Lapinski Date: Wed, 20 Sep 2023 01:57:13 -0700 Subject: [PATCH 5/5] unittests for feature template models --- .../api/templates/feature_template_field.py | 17 +- .../tests/templates/definitions/banner_1.json | 20 ++ .../templates/definitions/cisco_bfd.json | 90 ++++++ .../templates/definitions/complex_aaa.json | 291 ++++++++++++++++++ .../tests/templates/definitions/iuo.json | 109 +------ .../tests/templates/definitions/omp_1.json | 70 +---- .../tests/templates/definitions/omp_2.json | 55 +--- .../tests/templates/definitions/omp_3.json | 48 +-- vmngclient/tests/templates/models/__init__.py | 16 +- .../tests/templates/models/cisco_aaa.py | 17 +- .../tests/templates/models/cisco_banner.py | 5 + .../tests/templates/models/cisco_bfd.py | 15 + .../tests/templates/schemas/cisco_banner.json | 44 +++ .../tests/templates/schemas/cisco_bfd.json | 266 ++++++++++++++++ .../tests/templates/test_generate_payload.py | 16 +- .../tests/templates/test_serialize_model.py | 2 - 16 files changed, 807 insertions(+), 274 deletions(-) create mode 100644 vmngclient/tests/templates/definitions/banner_1.json create mode 100644 vmngclient/tests/templates/definitions/cisco_bfd.json create mode 100644 vmngclient/tests/templates/definitions/complex_aaa.json create mode 100644 vmngclient/tests/templates/models/cisco_banner.py create mode 100644 vmngclient/tests/templates/models/cisco_bfd.py create mode 100644 vmngclient/tests/templates/schemas/cisco_banner.json create mode 100644 vmngclient/tests/templates/schemas/cisco_bfd.json diff --git a/vmngclient/api/templates/feature_template_field.py b/vmngclient/api/templates/feature_template_field.py index 5c285e6b1..abef2cc88 100644 --- a/vmngclient/api/templates/feature_template_field.py +++ b/vmngclient/api/templates/feature_template_field.py @@ -93,6 +93,13 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic output["vipObjectType"] = self.objectType.value + def nest_value_in_output(value: Any) -> dict: + pointer = rel_output + for path in self.dataPath: + pointer = pointer[path] + pointer[self.key] = value + return rel_output + if isinstance(value, DeviceVariable): vip_variable = VipVariable( vipValue="", @@ -100,8 +107,8 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic vipObjectType=self.objectType, vipVariableName=value.name, ) + return nest_value_in_output(vip_variable.dict(by_alias=True, exclude_none=True)) - return {self.key: vip_variable.dict(by_alias=True, exclude_none=True)} else: if value: output["vipType"] = FeatureTemplateOptionType.CONSTANT.value @@ -145,10 +152,4 @@ def payload_scheme(self, value: Any = None, help=None, current_path=None) -> dic if self.primaryKeys: output["vipPrimaryKey"] = self.primaryKeys - pointer = rel_output - - for path in self.dataPath: - pointer = pointer[path] - - pointer[self.key] = output - return rel_output + return nest_value_in_output(output) diff --git a/vmngclient/tests/templates/definitions/banner_1.json b/vmngclient/tests/templates/definitions/banner_1.json new file mode 100644 index 000000000..89febeb5c --- /dev/null +++ b/vmngclient/tests/templates/definitions/banner_1.json @@ -0,0 +1,20 @@ +{ + "templateName": "cisco_banner", + "templateDescription": "na", + "templateType": "cisco_banner", + "deviceType": [], + "factoryDefault": false, + "templateMinVersion": "15.0.0", + "templateDefinition": { + "login": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "login banner" + }, + "motd": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "motd_bnanner" + } + } +} \ No newline at end of file diff --git a/vmngclient/tests/templates/definitions/cisco_bfd.json b/vmngclient/tests/templates/definitions/cisco_bfd.json new file mode 100644 index 000000000..4f780fe2a --- /dev/null +++ b/vmngclient/tests/templates/definitions/cisco_bfd.json @@ -0,0 +1,90 @@ +{ + "templateName": "cisco_bfd", + "templateDescription": "na", + "templateType": "cisco_bfd", + "deviceType": [], + "factoryDefault": false, + "templateMinVersion": "15.0.0", + "templateDefinition": { + "app-route": { + "multiplier": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + }, + "poll-interval": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 50 + } + }, + "default-dscp": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 50 + }, + "color": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "color": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "biz-internet" + }, + "hello-interval": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 50 + }, + "multiplier": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 7 + }, + "pmtu-discovery": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "false" + }, + "dscp": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 100 + } + }, + { + "color": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "silver" + }, + "hello-interval": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 150 + }, + "multiplier": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 7 + }, + "pmtu-discovery": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "true" + }, + "dscp": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 20 + } + } + ], + "vipPrimaryKey": [ + "color" + ] + } + } +} \ No newline at end of file diff --git a/vmngclient/tests/templates/definitions/complex_aaa.json b/vmngclient/tests/templates/definitions/complex_aaa.json new file mode 100644 index 000000000..5f8aa24f1 --- /dev/null +++ b/vmngclient/tests/templates/definitions/complex_aaa.json @@ -0,0 +1,291 @@ +{ + "templateName": "complex_aaa", + "templateDescription": "na", + "templateType": "cedge_aaa", + "deviceType": [], + "factoryDefault": false, + "templateMinVersion": "15.0.0", + "templateDefinition": { + "server-auth-order": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "local" + }, + "user": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test1" + }, + "password": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "*****" + }, + "secret": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "secret" + }, + "privilege": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1" + }, + "pubkey-chain": { + "vipObjectType": "tree", + "vipValue": [], + "vipType": "ignore", + "vipPrimaryKey": [ + "key-string" + ] + } + }, + { + "name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test2" + }, + "password": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "*****" + }, + "secret": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "secret" + }, + "privilege": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "15" + }, + "pubkey-chain": { + "vipObjectType": "tree", + "vipValue": [], + "vipType": "ignore", + "vipPrimaryKey": [ + "key-string" + ] + } + } + ], + "vipPrimaryKey": [ + "name" + ] + }, + "radius": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "group-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "group1" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 10 + }, + "source-interface": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "Gig1" + }, + "server": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "auth-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1812 + }, + "acct-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1813 + }, + "timeout": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 5 + }, + "retransmit": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 3 + }, + "key": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_key" + }, + "secret-key": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "secret_key" + }, + "key-enum": { + "vipObjectType": "object", + "vipValue": [], + "vipType": "ignore" + } + } + ], + "vipPrimaryKey": [ + "address" + ] + } + }, + { + "group-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "group2" + }, + "vpn": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 11 + }, + "source-interface": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "Gig2" + }, + "server": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.2.1" + }, + "auth-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1812 + }, + "acct-port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 1813 + }, + "timeout": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 5 + }, + "retransmit": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 3 + }, + "key": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "test_key2" + }, + "secret-key": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "secret_key2" + }, + "key-enum": { + "vipObjectType": "object", + "vipValue": [], + "vipType": "ignore" + } + } + ], + "vipPrimaryKey": [ + "address" + ] + } + } + ], + "vipPrimaryKey": [ + "group-name" + ] + }, + "tacacs": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "group-name": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "group1" + }, + "source-interface": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "Gig0" + }, + "server": { + "vipObjectType": "tree", + "vipType": "constant", + "vipValue": [ + { + "address": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "1.1.1.1" + }, + "port": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 49 + }, + "timeout": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": 5 + }, + "key": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "key" + }, + "secret-key": { + "vipObjectType": "object", + "vipType": "constant", + "vipValue": "secret_key" + }, + "key-enum": { + "vipObjectType": "object", + "vipValue": [], + "vipType": "ignore" + } + } + ], + "vipPrimaryKey": [ + "address" + ] + } + } + ], + "vipPrimaryKey": [ + "group-name" + ] + } + } +} \ No newline at end of file diff --git a/vmngclient/tests/templates/definitions/iuo.json b/vmngclient/tests/templates/definitions/iuo.json index bf5ed4e6e..46089f31a 100644 --- a/vmngclient/tests/templates/definitions/iuo.json +++ b/vmngclient/tests/templates/definitions/iuo.json @@ -2,108 +2,16 @@ "templateName": "iuo", "templateDescription": "zyx", "templateType": "cedge_aaa", - "deviceType": ["vedge-C8000V"], + "deviceType": [ + "vedge-C8000V" + ], "factoryDefault": false, "templateMinVersion": "15.0.0", "templateDefinition": { - "authentication": { - "dot1x": { - "default": { - "authentication_group": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "true" - } - } - } - }, - "accounting": { - "dot1x": { - "default": { - "start-stop": { - "accounting_group": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "false" - } - } - } - }, - "accounting-rule": { - "vipObjectType": "tree", - "vipValue": [], - "vipType": "ignore", - "vipPrimaryKey": [ - "rule-id" - ] - } - }, - "radius-dynamic-author": { - "radius-client": { - "vipObjectType": "tree", - "vipValue": [], - "vipType": "ignore", - "vipPrimaryKey": [ - "ip" - ] - }, - "rda-server-key": { - "vipObjectType": "object", - "vipValue": [], - "vipType": "ignore" - }, - "domain-stripping": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": "no" - }, - "auth-type": { - "vipObjectType": "object", - "vipValue": "any", - "vipType": "ignore" - }, - "port": { - "vipObjectType": "object", - "vipType": "constant", - "vipValue": 1700 - } - }, - "radius-trustsec": { - "cts-auth-list": { - "vipObjectType": "object", - "vipValue": [], - "vipType": "ignore" - }, - "radius-trustsec-group": { - "vipObjectType": "object", - "vipValue": [], - "vipType": "ignore" - } - }, - "authorization": { - "authorization-console": { - "vipObjectType": "object", - "vipValue": "false", - "vipType": "ignore" - }, - "authorization-config-commands": { - "vipObjectType": "object", - "vipValue": "false", - "vipType": "ignore" - }, - "authorization-rule": { - "vipObjectType": "tree", - "vipValue": [], - "vipType": "ignore", - "vipPrimaryKey": [ - "rule-id" - ] - } - }, "server-auth-order": { "vipObjectType": "object", - "vipValue": "local", - "vipType": "constant" + "vipType": "constant", + "vipValue": "local" }, "user": { "vipObjectType": "tree", @@ -238,11 +146,6 @@ "vipObjectType": "object", "vipValue": [], "vipType": "ignore" - }, - "key-type": { - "vipObjectType": "object", - "vipValue": "key", - "vipType": "ignore" } } ], @@ -265,4 +168,4 @@ ] } } -} \ No newline at end of file +} diff --git a/vmngclient/tests/templates/definitions/omp_1.json b/vmngclient/tests/templates/definitions/omp_1.json index 5ffc89880..b3918d9cd 100644 --- a/vmngclient/tests/templates/definitions/omp_1.json +++ b/vmngclient/tests/templates/definitions/omp_1.json @@ -3,71 +3,9 @@ "templateDescription": "default", "templateType": "omp-vsmart", "deviceType": [ - "vsmart" + "vedge-C8000V" ], + "factoryDefault": false, "templateMinVersion": "15.0.0", - "templateDefinition": { - "graceful-restart": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "true", - "vipVariableName": "omp_graceful_restart" - }, - "send-path-limit": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 4, - "vipVariableName": "omp_send_path_limit" - }, - "send-backup-paths": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "false", - "vipVariableName": "omp_send_backup_paths" - }, - "discard-rejected": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "false", - "vipVariableName": "omp_discard_rejected" - }, - "shutdown": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "false", - "vipVariableName": "omp_shutdown" - }, - "timers": { - "advertisement-interval": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 1, - "vipVariableName": "omp_advertisement_interval" - }, - "graceful-restart-timer": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 43200, - "vipVariableName": "omp_graceful_restart_timer" - }, - "holdtime": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 60, - "vipVariableName": "omp_holdtime" - }, - "eor-timer": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 300, - "vipVariableName": "omp_eor_timer" - } - }, - "filter-route": { - "outbound": { - "affinity-group-preference": {} - } - } - }, - "factoryDefault": false -} \ No newline at end of file + "templateDefinition": {} +} diff --git a/vmngclient/tests/templates/definitions/omp_2.json b/vmngclient/tests/templates/definitions/omp_2.json index fc080211b..42a61c9aa 100644 --- a/vmngclient/tests/templates/definitions/omp_2.json +++ b/vmngclient/tests/templates/definitions/omp_2.json @@ -3,71 +3,32 @@ "templateDescription": "some changes", "templateType": "omp-vsmart", "deviceType": [ - "vsmart" + "vedge-C8000V" ], + "factoryDefault": false, "templateMinVersion": "15.0.0", "templateDefinition": { "graceful-restart": { "vipObjectType": "object", "vipType": "constant", - "vipValue": "false", - "vipVariableName": "omp_graceful_restart" - }, - "send-path-limit": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 4, - "vipVariableName": "omp_send_path_limit" + "vipValue": "false" }, "send-backup-paths": { "vipObjectType": "object", "vipType": "constant", - "vipValue": "false", - "vipVariableName": "omp_send_backup_paths" - }, - "discard-rejected": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": "false", - "vipVariableName": "omp_discard_rejected" + "vipValue": "false" }, "shutdown": { "vipObjectType": "object", "vipType": "constant", - "vipValue": "true", - "vipVariableName": "omp_shutdown" + "vipValue": "true" }, "timers": { - "advertisement-interval": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 1, - "vipVariableName": "omp_advertisement_interval" - }, - "graceful-restart-timer": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 43200, - "vipVariableName": "omp_graceful_restart_timer" - }, "holdtime": { "vipObjectType": "object", "vipType": "constant", - "vipValue": 30, - "vipVariableName": "omp_holdtime" - }, - "eor-timer": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 300, - "vipVariableName": "omp_eor_timer" - } - }, - "filter-route": { - "outbound": { - "affinity-group-preference": {} + "vipValue": 30 } } - }, - "factoryDefault": false -} \ No newline at end of file + } +} diff --git a/vmngclient/tests/templates/definitions/omp_3.json b/vmngclient/tests/templates/definitions/omp_3.json index 6cf660b7e..4cefbe83a 100644 --- a/vmngclient/tests/templates/definitions/omp_3.json +++ b/vmngclient/tests/templates/definitions/omp_3.json @@ -3,71 +3,55 @@ "templateDescription": "advanced", "templateType": "omp-vsmart", "deviceType": [ - "vsmart" + "vedge-C8000V" ], + "factoryDefault": false, "templateMinVersion": "15.0.0", "templateDefinition": { "graceful-restart": { "vipObjectType": "object", "vipType": "constant", - "vipValue": "false", - "vipVariableName": "omp_graceful_restart" + "vipValue": "false" }, "send-path-limit": { - "vipObjectType": "object", - "vipType": "variableName", "vipValue": "", + "vipType": "variableName", + "vipObjectType": "object", "vipVariableName": "omp_send_path_limit" }, "send-backup-paths": { "vipObjectType": "object", "vipType": "constant", - "vipValue": "true", - "vipVariableName": "omp_send_backup_paths" + "vipValue": "true" }, "discard-rejected": { - "vipObjectType": "object", - "vipType": "variableName", "vipValue": "", + "vipType": "variableName", + "vipObjectType": "object", "vipVariableName": "omp_discard_rejected_custom" }, "shutdown": { "vipObjectType": "object", "vipType": "constant", - "vipValue": "false", - "vipVariableName": "omp_shutdown" + "vipValue": "false" }, "timers": { "advertisement-interval": { "vipObjectType": "object", "vipType": "constant", - "vipValue": 3, - "vipVariableName": "omp_advertisement_interval" + "vipValue": 3 }, "graceful-restart-timer": { - "vipObjectType": "object", - "vipType": "variableName", "vipValue": "", + "vipType": "variableName", + "vipObjectType": "object", "vipVariableName": "omp_graceful_restart_timer" }, "holdtime": { "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 60, - "vipVariableName": "omp_holdtime" - }, - "eor-timer": { - "vipObjectType": "object", - "vipType": "ignore", - "vipValue": 300, - "vipVariableName": "omp_eor_timer" - } - }, - "filter-route": { - "outbound": { - "affinity-group-preference": {} + "vipType": "constant", + "vipValue": 30 } } - }, - "factoryDefault": false -} \ No newline at end of file + } +} diff --git a/vmngclient/tests/templates/models/__init__.py b/vmngclient/tests/templates/models/__init__.py index 789c256b5..965963d36 100644 --- a/vmngclient/tests/templates/models/__init__.py +++ b/vmngclient/tests/templates/models/__init__.py @@ -1,6 +1,18 @@ # type: ignore -from vmngclient.tests.templates.models.cisco_aaa import cisco_aaa +from vmngclient.tests.templates.models.cisco_aaa import cisco_aaa, complex_aaa_model +from vmngclient.tests.templates.models.cisco_banner import banner_model +from vmngclient.tests.templates.models.cisco_bfd import bfd_model from vmngclient.tests.templates.models.cisco_vpn import basic_cisco_vpn, complex_vpn_model from vmngclient.tests.templates.models.omp_vsmart import default_omp, omp_2, omp_3 -__all__ = ["default_omp", "omp_2", "omp_3", "cisco_aaa", "basic_cisco_vpn", "complex_vpn_model"] +__all__ = [ + "default_omp", + "omp_2", + "omp_3", + "cisco_aaa", + "complex_aaa_model", + "basic_cisco_vpn", + "complex_vpn_model", + "banner_model", + "bfd_model", +] diff --git a/vmngclient/tests/templates/models/cisco_aaa.py b/vmngclient/tests/templates/models/cisco_aaa.py index ac5c2487c..9aa30a070 100644 --- a/vmngclient/tests/templates/models/cisco_aaa.py +++ b/vmngclient/tests/templates/models/cisco_aaa.py @@ -47,11 +47,11 @@ ) complex_aaa_model = CiscoAAAModel( - template_name="test_aaa", + template_name="complex_aaa", template_description="na", user=[ - User(name="test1", password="*****", secret="secret", privilege="1", pubkey_chain=["1", "2"]), - User(name="test2", password="*****", secret="secret", privilege="15", pubkey_chain=["1", "2"]), + User(name="test1", password="*****", secret="secret", privilege="1"), + User(name="test2", password="*****", secret="secret", privilege="15"), ], authentication_group=True, accounting_group=False, @@ -60,11 +60,7 @@ group_name="group1", vpn=10, source_interface="Gig1", - server=[ - RadiusServer( - address="1.1.1.1", key="test_key", secret_key="secret_key", key_enum="key_enum", key_type="key_type" - ) - ], + server=[RadiusServer(address="1.1.1.1", key="test_key", secret_key="secret_key")], ), RadiusGroup( group_name="group2", @@ -75,8 +71,6 @@ address="1.1.2.1", key="test_key2", secret_key="secret_key2", - key_enum="key_enum2", - key_type="key_type2", ) ], ), @@ -87,8 +81,7 @@ group_name="group1", vpn=0, source_interface="Gig0", - server=[TacacsServer(address="1.1.1.1", key="key", secret_key="secret_key", key_enum="key_enum")], + server=[TacacsServer(address="1.1.1.1", key="key", secret_key="secret_key")], ) ], - server_auth_order="test", ) diff --git a/vmngclient/tests/templates/models/cisco_banner.py b/vmngclient/tests/templates/models/cisco_banner.py new file mode 100644 index 000000000..d923c0880 --- /dev/null +++ b/vmngclient/tests/templates/models/cisco_banner.py @@ -0,0 +1,5 @@ +from vmngclient.api.templates.models.cisco_banner_model import CiscoBannerModel + +banner_model = CiscoBannerModel( # type: ignore + template_name="banner_1", template_description="na", login_banner="login banner", motd_banner="motd_bnanner" +) diff --git a/vmngclient/tests/templates/models/cisco_bfd.py b/vmngclient/tests/templates/models/cisco_bfd.py new file mode 100644 index 000000000..ed3094104 --- /dev/null +++ b/vmngclient/tests/templates/models/cisco_bfd.py @@ -0,0 +1,15 @@ +from vmngclient.api.templates.models.cisco_bfd_model import CiscoBFDModel, Color, ColorType + +bfd_model = CiscoBFDModel( # type: ignore + template_name="cisco_bfd", + template_description="na", + multiplier=100, + poll_interval=50, + default_dscp=50, + color=[ + Color( # type: ignore + color=ColorType.BIZ_INTERNET, hello_interval=50, multipler=100, pmtu_discovery=False, dscp=100 + ), + Color(color=ColorType.SILVER, hello_interval=150, multipler=10, dscp=20), # type: ignore + ], +) diff --git a/vmngclient/tests/templates/schemas/cisco_banner.json b/vmngclient/tests/templates/schemas/cisco_banner.json new file mode 100644 index 000000000..f5eaea388 --- /dev/null +++ b/vmngclient/tests/templates/schemas/cisco_banner.json @@ -0,0 +1,44 @@ +{ + "name": "cisco_banner", + "xmlPath": [], + "xmlRootTag": "banner", + "nameSpace": "http://viptela.com/system", + "fields": [ + { + "key": "login", + "description": "Login Banner", + "details": "Set message to display before login prompt", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "string", + "minLength": 1, + "maxLength": 2048 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "motd", + "description": "MOTD Banner", + "details": "Set message to display after a user logs in", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "string", + "minLength": 1, + "maxLength": 2048 + }, + "dataPath": [], + "objectType": "object" + } + ] +} \ No newline at end of file diff --git a/vmngclient/tests/templates/schemas/cisco_bfd.json b/vmngclient/tests/templates/schemas/cisco_bfd.json new file mode 100644 index 000000000..d6376ceb5 --- /dev/null +++ b/vmngclient/tests/templates/schemas/cisco_bfd.json @@ -0,0 +1,266 @@ +{ + "name": "cisco_bfd", + "xmlPath": [], + "xmlRootTag": "bfd", + "nameSpace": "http://viptela.com/bfd", + "fields": [ + { + "key": "multiplier", + "description": "Multiplier", + "details": "Set the number of polling intervals used to determine tunnel SLA class", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 6, + "default": 6 + }, + "dataPath": [ + "app-route" + ], + "objectType": "object" + }, + { + "key": "poll-interval", + "description": "Poll Interval (milliseconds)", + "details": "Set how often BFD polls tunnels to collect packet latency, loss, and statistics", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 4294967295, + "default": 600000 + }, + "dataPath": [ + "app-route" + ], + "objectType": "object" + }, + { + "key": "default-dscp", + "description": "Default DSCP value for BFD packets", + "details": "Set default DSCP value for BFD packets", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 0, + "max": 63, + "default": 48 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "color", + "description": "Color", + "details": "Set color that identifies the WAN transport tunnel", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataPath": [], + "objectType": "tree", + "primaryKeys": [ + "color" + ], + "children": [ + { + "key": "color", + "description": "Color", + "details": "Set color that identifies the WAN transport tunnel", + "optionType": [ + "constant", + "variable" + ], + "defaultOption": "constant", + "dataType": { + "type": "enum", + "values": [ + { + "key": "default", + "value": "Default" + }, + { + "key": "mpls", + "value": "MPLS" + }, + { + "key": "metro-ethernet", + "value": "Metro Ethernet" + }, + { + "key": "biz-internet", + "value": "Biz Internet" + }, + { + "key": "public-internet", + "value": "Public Internet" + }, + { + "key": "lte", + "value": "LTE" + }, + { + "key": "3g", + "value": "3G" + }, + { + "key": "red", + "value": "Red" + }, + { + "key": "green", + "value": "Green" + }, + { + "key": "blue", + "value": "Blue" + }, + { + "key": "gold", + "value": "Gold" + }, + { + "key": "silver", + "value": "Silver" + }, + { + "key": "bronze", + "value": "Bronze" + }, + { + "key": "custom1", + "value": "Custom 1" + }, + { + "key": "custom2", + "value": "Custom 2" + }, + { + "key": "custom3", + "value": "Custom 3" + }, + { + "key": "private1", + "value": "Private 1" + }, + { + "key": "private2", + "value": "Private 2" + }, + { + "key": "private3", + "value": "Private 3" + }, + { + "key": "private4", + "value": "Private 4" + }, + { + "key": "private5", + "value": "Private 5" + }, + { + "key": "private6", + "value": "Private 6" + } + ] + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "hello-interval", + "description": "Hello Interval (milliseconds)", + "details": "Set how often BFD sends Hello packets", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 100, + "max": 300000, + "default": 1000 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "multiplier", + "description": "Multiplier", + "details": "Set how many Hello packet intervals to wait before declaring that a tunnel has failed", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 1, + "max": 60, + "default": 7 + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "pmtu-discovery", + "description": "Path MTU Discovery", + "details": "Control automatic path MTU discovery", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "boolean", + "default": "true" + }, + "dataPath": [], + "objectType": "object" + }, + { + "key": "dscp", + "description": "BFD Default DSCP value for tloc color", + "details": "Set BFD Default DSCP value for tloc color", + "optionType": [ + "constant", + "variable", + "ignore" + ], + "defaultOption": "ignore", + "dataType": { + "type": "number", + "min": 0, + "max": 63, + "default": 48 + }, + "dataPath": [], + "objectType": "object" + } + ] + } + ] +} \ No newline at end of file diff --git a/vmngclient/tests/templates/test_generate_payload.py b/vmngclient/tests/templates/test_generate_payload.py index a015d37ad..3ab5866e3 100644 --- a/vmngclient/tests/templates/test_generate_payload.py +++ b/vmngclient/tests/templates/test_generate_payload.py @@ -71,6 +71,19 @@ class Config: user: List[User] +class DataPathFeatureTemplate(FeatureTemplate): + class Config: + arbitrary_types_allowed = True + allow_population_by_field_name = True + + template_name: str = "test" + template_description: str = "test" + payload_path: ClassVar[Path] = Path(__file__).parent / "DEPRECATED" + type: ClassVar[str] = "test_type" + + as_num: str = Field(alias="as-num", data_path=["authentication", "dot1x", "default"]) + + mocked_feature_template_children_1 = MockedFeatureTemplateChildren( user=[User(name="user1", password="pass"), User(name="user2", password="pass")] ) @@ -86,10 +99,9 @@ class Config: class TestFeatureTemplate(TestCase): @parameterized.expand( [ - ("basic.json", "basic_no_value.json", None), ("basic.json", None, MockedFeatureTemplate(num="num")), ("alias.json", None, MockedFeatureTemplateAlias(num="12")), - ("data_path.json", None, MockedFeatureTemplateAlias(num="12")), + ("data_path.json", None, DataPathFeatureTemplate(as_num="12")), ("children.json", None, mocked_feature_template_children_1), ("children_nested.json", None, mocked_feature_template_children_2), ("children_nested_datapath.json", None, mocked_feature_template_children_2), diff --git a/vmngclient/tests/templates/test_serialize_model.py b/vmngclient/tests/templates/test_serialize_model.py index f3c0fa36a..3b729a3ef 100644 --- a/vmngclient/tests/templates/test_serialize_model.py +++ b/vmngclient/tests/templates/test_serialize_model.py @@ -4,7 +4,6 @@ from unittest import TestCase from unittest.mock import patch -import pytest # type: ignore from parameterized import parameterized # type: ignore import vmngclient.tests.templates.models as models @@ -17,7 +16,6 @@ # Compare payload with definition -@pytest.mark.skip() class TestFeatureTemplate2(TestCase): @parameterized.expand( [(template,) for template in map(models.__dict__.get, models.__all__)], # type: ignore