diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index 69b01e7..85d6dee 100644 --- a/ntc_rosetta/parsers/openconfig/junos/__init__.py +++ b/ntc_rosetta/parsers/openconfig/junos/__init__.py @@ -1,3 +1,4 @@ +from copy import copy from lxml import etree from ntc_rosetta.parsers.openconfig.junos.openconfig_interfaces.interfaces import ( @@ -18,9 +19,17 @@ class JunosParser(parser.RootParser): class Yangify(parser.ParserData): def init(self) -> None: - self.root_native["dev_conf"] = etree.fromstring( - self.root_native["dev_conf"] - ) + parsed_xml = etree.fromstring(self.root_native["dev_conf"]) + if parsed_xml.tag != "configuration": + parsed_xml = parsed_xml.find("configuration") + if parsed_xml is None: + raise AttributeError( + "Unable to locate 'configuration' tag in XML blob" + ) + # We need to copy the XML element here, otherwise it will retain its parent + # references, meaning we can inadvertantly walk up the tree when we shouldn't. + parsed_xml = copy(parsed_xml) + self.root_native["dev_conf"] = parsed_xml self.native["dev_conf"] = self.root_native["dev_conf"] interfaces = Interfaces diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_fails_1 b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_fails_1 new file mode 100644 index 0000000..ed22ac8 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_fails_1 @@ -0,0 +1,33 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + + xe-0/0/1 + + 0 + + + 10 + this is interface xe-0/0/1.10 + + + + xe-0/0/3 + + 0 + + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_passes_1 similarity index 100% rename from tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf rename to tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_passes_1 diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_passes_2 b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_passes_2 new file mode 100644 index 0000000..ed22ac8 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_passes_2 @@ -0,0 +1,33 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + + xe-0/0/1 + + 0 + + + 10 + this is interface xe-0/0/1.10 + + + + xe-0/0/3 + + 0 + + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_raises_AttributeError b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_raises_AttributeError new file mode 100644 index 0000000..c073928 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_raises_AttributeError @@ -0,0 +1,33 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + + xe-0/0/1 + + 0 + + + 10 + this is interface xe-0/0/1.10 + + + + xe-0/0/3 + + 0 + + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_fails_1.json b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_fails_1.json new file mode 100644 index 0000000..9a35d8f --- /dev/null +++ b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_fails_1.json @@ -0,0 +1,61 @@ +{ + "openconfig-interfaces:interfaces": { + "interface": [ + { + "name": "xe-0/0/0", + "config": { + "name": "xe-0/0/0", + "type": "iana-if-type:ethernetCsmacd", + "enabled": false + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0, + "description": "this is interface xe-0/0/0" + } + } + ] + } + }, + { + "name": "xe-0/0/1", + "config": { + "name": "xe-0/0/1", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + } + }, + { + "name": "xe-0/0/3", + "config": { + "name": "xe-0/0/3", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + } + } + ] + } +} diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result.json b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_passes_1.json similarity index 100% rename from tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result.json rename to tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_passes_1.json diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_passes_2.json b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_passes_2.json new file mode 100644 index 0000000..fd9deb7 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_passes_2.json @@ -0,0 +1,68 @@ +{ + "openconfig-interfaces:interfaces": { + "interface": [ + { + "name": "xe-0/0/0", + "config": { + "name": "xe-0/0/0", + "type": "iana-if-type:ethernetCsmacd", + "enabled": false + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0, + "description": "this is interface xe-0/0/0" + } + } + ] + } + }, + { + "name": "xe-0/0/1", + "config": { + "name": "xe-0/0/1", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + }, + { + "index": 10, + "config": { + "index": 10, + "description": "this is interface xe-0/0/1.10" + } + } + ] + } + }, + { + "name": "xe-0/0/3", + "config": { + "name": "xe-0/0/3", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + } + } + ] + } +} diff --git a/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_raises_AttributeError.json b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_raises_AttributeError.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_raises_AttributeError.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf deleted file mode 100644 index e9fc594..0000000 --- a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf +++ /dev/null @@ -1,53 +0,0 @@ - - - - xe-0/0/0 - - 0 - this is interface xe-0/0/0 - - - - xe-0/0/1 - - 0 - - - access - - 10 - - - - - - - xe-0/0/3 - - 0 - - - trunk - - 10 - 20 - - - - - - - - - default - 1 - - - prod - 20 - - - 10 - - - diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_fails_1 b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_fails_1 new file mode 100644 index 0000000..9b28d45 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_fails_1 @@ -0,0 +1,58 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + xe-0/0/1 + + 0 + + + access + + 10 + + + + + + + xe-0/0/3 + + 0 + + + trunk + + 10 + 20 + + + + + + + + + default + 1 + + + prod + 20 + + + 10 + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_1 b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_1 new file mode 100644 index 0000000..9b28d45 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_1 @@ -0,0 +1,58 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + xe-0/0/1 + + 0 + + + access + + 10 + + + + + + + xe-0/0/3 + + 0 + + + trunk + + 10 + 20 + + + + + + + + + default + 1 + + + prod + 20 + + + 10 + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_2 b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_2 new file mode 100644 index 0000000..9b28d45 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_2 @@ -0,0 +1,58 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + xe-0/0/1 + + 0 + + + access + + 10 + + + + + + + xe-0/0/3 + + 0 + + + trunk + + 10 + 20 + + + + + + + + + default + 1 + + + prod + 20 + + + 10 + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_raises_AttributeError b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_raises_AttributeError new file mode 100644 index 0000000..fd1dd4e --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_raises_AttributeError @@ -0,0 +1,58 @@ + + + + + xe-0/0/0 + + 0 + this is interface xe-0/0/0 + + + + xe-0/0/1 + + 0 + + + access + + 10 + + + + + + + xe-0/0/3 + + 0 + + + trunk + + 10 + 20 + + + + + + + + + default + 1 + + + prod + 20 + + + 10 + + + + + + + \ No newline at end of file diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_fails_1.json b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_fails_1.json new file mode 100644 index 0000000..93f35ab --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_fails_1.json @@ -0,0 +1,102 @@ +{ + "openconfig-interfaces:interfaces": { + "interface": [ + { + "name": "xe-0/0/0", + "config": { + "name": "xe-0/0/0", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0, + "description": "this is interface xe-0/0/0" + } + } + ] + } + }, + { + "name": "xe-0/0/1", + "config": { + "name": "xe-0/0/1", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + }, + "openconfig-if-ethernet:ethernet": { + "openconfig-vlan:switched-vlan": { + "config": { + "interface-mode": "ACCESS", + "access-vlan": 10 + } + } + } + }, + { + "name": "xe-0/0/3", + "config": { + "name": "xe-0/0/3", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + }, + "openconfig-if-ethernet:ethernet": { + "openconfig-vlan:switched-vlan": { + "config": { + "interface-mode": "TRUNK", + "trunk-vlans": [ + 10, + 20 + ] + } + } + } + } + ] + }, + "openconfig-network-instance:network-instances": { + "network-instance": [ + { + "name": "default", + "config": { + "name": "default" + }, + "vlans": { + "vlan": [ + { + "vlan-id": 1, + "config": { + "vlan-id": 1, + "name": "default", + "status": "ACTIVE" + } + } + ] + } + } + ] + } +} diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result.json b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_passes_1.json similarity index 100% rename from tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result.json rename to tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_passes_1.json diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_passes_2.json b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_passes_2.json new file mode 100644 index 0000000..3b10139 --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_passes_2.json @@ -0,0 +1,117 @@ +{ + "openconfig-interfaces:interfaces": { + "interface": [ + { + "name": "xe-0/0/0", + "config": { + "name": "xe-0/0/0", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0, + "description": "this is interface xe-0/0/0" + } + } + ] + } + }, + { + "name": "xe-0/0/1", + "config": { + "name": "xe-0/0/1", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + }, + "openconfig-if-ethernet:ethernet": { + "openconfig-vlan:switched-vlan": { + "config": { + "interface-mode": "ACCESS", + "access-vlan": 10 + } + } + } + }, + { + "name": "xe-0/0/3", + "config": { + "name": "xe-0/0/3", + "type": "iana-if-type:ethernetCsmacd", + "enabled": true + }, + "subinterfaces": { + "subinterface": [ + { + "index": 0, + "config": { + "index": 0 + } + } + ] + }, + "openconfig-if-ethernet:ethernet": { + "openconfig-vlan:switched-vlan": { + "config": { + "interface-mode": "TRUNK", + "trunk-vlans": [ + 10, + 20 + ] + } + } + } + } + ] + }, + "openconfig-network-instance:network-instances": { + "network-instance": [ + { + "name": "default", + "config": { + "name": "default" + }, + "vlans": { + "vlan": [ + { + "vlan-id": 1, + "config": { + "vlan-id": 1, + "name": "default", + "status": "ACTIVE" + } + }, + { + "vlan-id": 20, + "config": { + "vlan-id": 20, + "name": "prod", + "status": "ACTIVE" + } + }, + { + "vlan-id": 10, + "config": { + "vlan-id": 10, + "status": "SUSPENDED" + } + } + ] + } + } + ] + } +} diff --git a/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_raises_AttributeError.json b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_raises_AttributeError.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_raises_AttributeError.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/models/openconfig/test_models.py b/tests/models/openconfig/test_models.py index 384c1c9..aaa481d 100644 --- a/tests/models/openconfig/test_models.py +++ b/tests/models/openconfig/test_models.py @@ -57,9 +57,40 @@ class Test: def test_parser( self, model: str, driver: str, test_case_path: pathlib.Path ) -> None: - with open(test_case_path.joinpath("dev_conf"), "r") as f: + device_config_files = [] + result_files = [] + for file_path in test_case_path.iterdir(): + if file_path.is_file(): + if file_path.name.startswith("dev_conf"): + device_config_files.append(file_path) + elif file_path.name.startswith("result"): + result_files.append(file_path) + # Assuming there is only a single testcase for this parser. + if len(device_config_files) == 1: + self._run_parser_test( + model, driver, device_config_files[0], result_files[0] + ) + else: + for device_config, result in zip( + sorted(device_config_files), sorted(result_files) + ): + if "passes" in device_config.name: + self._run_parser_test(model, driver, device_config, result) + elif "fails" in device_config.name: + with pytest.raises(AssertionError): + self._run_parser_test(model, driver, device_config, result) + elif "_raises_" in device_config.name: + exception_name = device_config.name.split("_raises_")[-1] + with pytest.raises(eval(exception_name)): + self._run_parser_test(model, driver, device_config, result) + + @staticmethod + def _run_parser_test( + model: str, driver: str, device_config: pathlib.Path, result: pathlib.Path + ) -> None: + with open(device_config, "r") as f: dev_conf = f.read() - with open(test_case_path.joinpath("result.json"), "r") as f: + with open(result, "r") as f: structured = json.load(f) driver_class = ntc_rosetta.get_driver(driver) @@ -71,7 +102,7 @@ def test_parser( include=filters[model]["include"], exclude=filters[model]["exclude"], ) - # print(json.dumps(parsed_obj.raw_value())) + # print(json.dumps(parsed_obj.raw_value())) assert parsed_obj.raw_value() == structured, json.dumps(parsed_obj.raw_value()) def _test_translate(