From f6c32b2fd14d457b24e40384c116af2702f4966a Mon Sep 17 00:00:00 2001 From: Ben Hohnke Date: Thu, 30 May 2019 11:55:00 +1000 Subject: [PATCH 1/7] Add support for nested XML configuration When a `show conf | display xml` is performed on a juniper device, the configuration is provided as follows: ``` - configuration XML here - ``` However, this module currently expected the "configuration" tag to be the top level tag. This minor modification searches for the configuration tag before continuing, and raises an AttributeError if it cannot be found (as the parsing would fail silently anyway if was not the case) --- ntc_rosetta/parsers/openconfig/junos/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index 69b01e7..ba169ec 100644 --- a/ntc_rosetta/parsers/openconfig/junos/__init__.py +++ b/ntc_rosetta/parsers/openconfig/junos/__init__.py @@ -18,6 +18,10 @@ class JunosParser(parser.RootParser): class Yangify(parser.ParserData): def init(self) -> None: + if self.root_native["dev_conf"].tag != "configuration": + self.root_native["dev_conf"] = self.root_native["dev_conf"].find('configuration') + if not self.root_native["dev_conf"]: + raise AttributeError("Unable to locate 'configuration' tag in XML blob") self.root_native["dev_conf"] = etree.fromstring( self.root_native["dev_conf"] ) From 47e3d647bc18ccf9729d8bae0b16597254cb551d Mon Sep 17 00:00:00 2001 From: Ben Hohnke Date: Thu, 30 May 2019 13:57:50 +1000 Subject: [PATCH 2/7] Update __init__.py --- ntc_rosetta/parsers/openconfig/junos/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index ba169ec..f63bab9 100644 --- a/ntc_rosetta/parsers/openconfig/junos/__init__.py +++ b/ntc_rosetta/parsers/openconfig/junos/__init__.py @@ -18,13 +18,12 @@ class JunosParser(parser.RootParser): class Yangify(parser.ParserData): def init(self) -> None: - if self.root_native["dev_conf"].tag != "configuration": - self.root_native["dev_conf"] = self.root_native["dev_conf"].find('configuration') - if not 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 not parsed_xml: raise AttributeError("Unable to locate 'configuration' tag in XML blob") - self.root_native["dev_conf"] = etree.fromstring( - self.root_native["dev_conf"] - ) + self.root_native["dev_conf"] = parsed_xml self.native["dev_conf"] = self.root_native["dev_conf"] interfaces = Interfaces From ad624d87b374374d1da1d5a3d545432e9c4eace0 Mon Sep 17 00:00:00 2001 From: Ben Hohnke Date: Thu, 30 May 2019 15:37:01 +1000 Subject: [PATCH 3/7] finalise working fix Changes now appear to be working as expected. --- ntc_rosetta/parsers/openconfig/junos/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index f63bab9..674c4e1 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 ( @@ -21,8 +22,11 @@ def init(self) -> None: parsed_xml = etree.fromstring(self.root_native["dev_conf"]) if parsed_xml.tag != "configuration": parsed_xml = parsed_xml.find('configuration') - if not parsed_xml: - raise AttributeError("Unable to locate 'configuration' tag in XML blob") + 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"] From dc44b8920e6e1243e198e54f475fa377d708e6b7 Mon Sep 17 00:00:00 2001 From: Ben Hohnke Date: Fri, 31 May 2019 08:08:53 +1000 Subject: [PATCH 4/7] copy -> deepcopy --- ntc_rosetta/parsers/openconfig/junos/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index 674c4e1..2e9704b 100644 --- a/ntc_rosetta/parsers/openconfig/junos/__init__.py +++ b/ntc_rosetta/parsers/openconfig/junos/__init__.py @@ -1,4 +1,4 @@ -from copy import copy +from copy import deepcopy from lxml import etree from ntc_rosetta.parsers.openconfig.junos.openconfig_interfaces.interfaces import ( @@ -26,7 +26,7 @@ def init(self) -> 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) + parsed_xml = deepcopy(parsed_xml) self.root_native["dev_conf"] = parsed_xml self.native["dev_conf"] = self.root_native["dev_conf"] From 1a8f41de6f9b8d8b0af95d4b7cb8454a0c21f64e Mon Sep 17 00:00:00 2001 From: Ben Hohnke Date: Mon, 3 Jun 2019 10:36:30 +1000 Subject: [PATCH 5/7] * Run black across parsers/openconfig/junos/__init__.py to make black happy * Update test_models to allow multiple test cases for each test type * Add test case for the junos parser * Add failing test case for junos parser * Add raises test case for junos parser, to test new code in parsers/openconfig/junos/__init__.py --- .../parsers/openconfig/junos/__init__.py | 10 +- .../parse/junos/config/dev_conf_fails_1 | 33 +++++ .../config/{dev_conf => dev_conf_passes_1} | 0 .../parse/junos/config/dev_conf_passes_2 | 33 +++++ .../config/dev_conf_raises_AttributeError | 33 +++++ .../parse/junos/config/result_fails_1.json | 61 +++++++++ .../{result.json => result_passes_1.json} | 0 .../parse/junos/config/result_passes_2.json | 68 ++++++++++ .../config/result_raises_AttributeError.json | 1 + .../parse/junos/config/dev_conf | 53 -------- .../parse/junos/config/dev_conf_fails_1 | 58 +++++++++ .../parse/junos/config/dev_conf_passes_1 | 58 +++++++++ .../parse/junos/config/dev_conf_passes_2 | 58 +++++++++ .../config/dev_conf_raises_AttributeError | 58 +++++++++ .../parse/junos/config/result_fails_1.json | 102 +++++++++++++++ .../{result.json => result_passes_1.json} | 0 .../parse/junos/config/result_passes_2.json | 117 ++++++++++++++++++ .../config/result_raises_AttributeError.json | 1 + tests/models/openconfig/test_models.py | 37 +++++- 19 files changed, 721 insertions(+), 60 deletions(-) create mode 100644 tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_fails_1 rename tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/{dev_conf => dev_conf_passes_1} (100%) create mode 100644 tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_passes_2 create mode 100644 tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/dev_conf_raises_AttributeError create mode 100644 tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_fails_1.json rename tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/{result.json => result_passes_1.json} (100%) create mode 100644 tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_passes_2.json create mode 100644 tests/models/openconfig/data/openconfig-interfaces/parse/junos/config/result_raises_AttributeError.json delete mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_fails_1 create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_1 create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_passes_2 create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/dev_conf_raises_AttributeError create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_fails_1.json rename tests/models/openconfig/data/openconfig-vlan/parse/junos/config/{result.json => result_passes_1.json} (100%) create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_passes_2.json create mode 100644 tests/models/openconfig/data/openconfig-vlan/parse/junos/config/result_raises_AttributeError.json diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index 2e9704b..d89043c 100644 --- a/ntc_rosetta/parsers/openconfig/junos/__init__.py +++ b/ntc_rosetta/parsers/openconfig/junos/__init__.py @@ -21,10 +21,12 @@ class Yangify(parser.ParserData): def init(self) -> None: 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 + 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 = deepcopy(parsed_xml) self.root_native["dev_conf"] = parsed_xml 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..6bb77a0 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( From bd4cc5f9ebb4a056d90925f9f4e2087cf66ce062 Mon Sep 17 00:00:00 2001 From: Ben Hohnke Date: Wed, 12 Jun 2019 11:12:49 +1000 Subject: [PATCH 6/7] Restore a commented out line --- tests/models/openconfig/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/models/openconfig/test_models.py b/tests/models/openconfig/test_models.py index 6bb77a0..aaa481d 100644 --- a/tests/models/openconfig/test_models.py +++ b/tests/models/openconfig/test_models.py @@ -102,7 +102,7 @@ def _run_parser_test( 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( From ad93d4e7f21f896756bb63851ada43c7ce61bf3b Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 12 Jun 2019 22:00:41 +1000 Subject: [PATCH 7/7] Merge from develop branch s/deepcopy/copy --- ntc_rosetta/parsers/openconfig/junos/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ntc_rosetta/parsers/openconfig/junos/__init__.py b/ntc_rosetta/parsers/openconfig/junos/__init__.py index d89043c..85d6dee 100644 --- a/ntc_rosetta/parsers/openconfig/junos/__init__.py +++ b/ntc_rosetta/parsers/openconfig/junos/__init__.py @@ -1,4 +1,4 @@ -from copy import deepcopy +from copy import copy from lxml import etree from ntc_rosetta.parsers.openconfig.junos.openconfig_interfaces.interfaces import ( @@ -28,7 +28,7 @@ def init(self) -> None: ) # 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 = deepcopy(parsed_xml) + parsed_xml = copy(parsed_xml) self.root_native["dev_conf"] = parsed_xml self.native["dev_conf"] = self.root_native["dev_conf"]